Suite of AT Protocol TypeScript libraries built on web standards
21
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 183 lines 4.4 kB view raw
1import { 2 configure, 3 getConsoleSink, 4 getLogger, 5 type Logger, 6 type LogLevel, 7 type LogMethod, 8 type Sink, 9} from "@logtape/logtape"; 10import { getFileSink } from "@logtape/file"; 11import process from "node:process"; 12 13const isDeno = typeof Deno !== "undefined"; 14type LoggingConfig = { 15 allSystemsEnabled: boolean; 16 enabledSystems: string[]; 17 enabled: boolean; 18 level: LogLevel; 19 logDestination?: string; 20}; 21 22const getEnv = (key: string): string | undefined => { 23 if (isDeno) { 24 try { 25 return Deno.env.get(key); 26 } catch { 27 return undefined; 28 } 29 } 30 31 return process.env[key]; 32}; 33 34let config: LoggingConfig | undefined; 35let configurePromise: Promise<void> | undefined; 36 37async function ensureConfigured() { 38 const { 39 enabled, 40 level, 41 logDestination, 42 } = getLoggingConfig(); 43 if (!enabled) return; 44 if (configurePromise) { 45 await configurePromise; 46 return; 47 } 48 49 const sinks: Record<string, Sink> = { 50 console: getConsoleSink(), 51 }; 52 53 if (logDestination) { 54 sinks.file = getFileSink(logDestination); 55 } 56 57 configurePromise = configure({ 58 sinks, 59 loggers: [ 60 { 61 category: [], 62 lowestLevel: level, 63 sinks: logDestination ? ["console", "file"] : ["console"], 64 }, 65 ], 66 }).catch((error) => { 67 configurePromise = undefined; 68 throw error; 69 }); 70 71 await configurePromise; 72} 73 74const subsystemLoggers: Record<string, Logger> = {}; 75 76export const subsystemLogger = (name: string): Logger => { 77 if (subsystemLoggers[name]) return subsystemLoggers[name]; 78 79 subsystemLoggers[name] = wrapLogger(name, getLogger([name])); 80 return subsystemLoggers[name]; 81}; 82 83export function _resetLoggerStateForTest(): void { 84 config = undefined; 85 configurePromise = undefined; 86 for (const name in subsystemLoggers) { 87 delete subsystemLoggers[name]; 88 } 89} 90 91function getLoggingConfig(): LoggingConfig { 92 if (config) { 93 return config; 94 } 95 96 const logSystems = getEnv("LOG_SYSTEMS") || ""; 97 const enabledSystems = logSystems.replace(",", " ") 98 .split(" ") 99 .filter(Boolean); 100 const enabledEnv = getEnv("LOG_ENABLED"); 101 102 config = { 103 allSystemsEnabled: !logSystems, 104 enabledSystems, 105 enabled: enabledEnv === "true" || enabledEnv === "t" || enabledEnv === "1", 106 level: (getEnv("LOG_LEVEL") || "info") as LogLevel, 107 logDestination: getEnv("LOG_DESTINATION"), 108 }; 109 return config; 110} 111 112function isSubsystemEnabled(name: string): boolean { 113 const { allSystemsEnabled, enabled, enabledSystems } = getLoggingConfig(); 114 return enabled && (allSystemsEnabled || enabledSystems.includes(name)); 115} 116 117function wrapLogger(name: string, logger: Logger): Logger { 118 return new Proxy(logger, { 119 get(target, property, receiver) { 120 if (property === "parent") { 121 return target.parent === null ? null : wrapLogger(name, target.parent); 122 } 123 124 if (property === "getChild") { 125 return (subcategory: Parameters<Logger["getChild"]>[0]) => 126 wrapLogger(name, target.getChild(subcategory)); 127 } 128 129 if (property === "with") { 130 return (properties: Parameters<Logger["with"]>[0]) => 131 wrapLogger(name, target.with(properties)); 132 } 133 134 if ( 135 property === "trace" || 136 property === "debug" || 137 property === "info" || 138 property === "warn" || 139 property === "warning" || 140 property === "error" || 141 property === "fatal" 142 ) { 143 return wrapLogMethod( 144 name, 145 Reflect.get(target, property, receiver).bind(target) as LogMethod, 146 ); 147 } 148 149 if (property === "emit") { 150 return wrapEmitMethod( 151 name, 152 Reflect.get(target, property, receiver).bind( 153 target, 154 ) as Logger["emit"], 155 ); 156 } 157 158 return Reflect.get(target, property, receiver); 159 }, 160 }); 161} 162 163function wrapLogMethod(name: string, method: LogMethod): LogMethod { 164 return ((...args: unknown[]) => { 165 if (!isSubsystemEnabled(name)) { 166 return; 167 } 168 169 ensureConfigured().catch(console.error); 170 Reflect.apply(method as (...args: unknown[]) => void, undefined, args); 171 }) as LogMethod; 172} 173 174function wrapEmitMethod(name: string, method: Logger["emit"]): Logger["emit"] { 175 return ((record) => { 176 if (!isSubsystemEnabled(name)) { 177 return; 178 } 179 180 ensureConfigured().catch(console.error); 181 method(record); 182 }) as Logger["emit"]; 183}