type Level = 'info' | 'debug' | 'error' | 'warn' | 'log' export type TransportFunction = (level: Level, ...args: unknown[]) => void type LoggerOptions = { transport: TransportFunction[] } /** * Concrete logger implementation produced by {@link createLogger}. Each log method writes to * the native `console` counterpart **and** fans out to every registered transport, so * third-party sinks (e.g. Sentry, Datadog) receive the same payload without extra wiring. * * `test` is intentionally a no-op — it exists as a scratch channel that leaves no output in * any environment. * * Transport functions are shared as a static property, meaning all `CustomLogger` instances * created in the same process share the same transport list. Only one call to * {@link createLogger} is expected per app. */ class CustomLogger { static transport: TransportFunction[] static applyTransport(level: Level, ...args: unknown[]) { CustomLogger.transport.map(fn => { fn(level, ...args) }) } info(...args: unknown[]) { console.info(...args) CustomLogger.applyTransport('info', ...args) } error(...args: unknown[]) { console.error(...args) CustomLogger.applyTransport('error', ...args) } warn(...args: unknown[]) { console.warn(...args) CustomLogger.applyTransport('warn', ...args) } /** Intentional no-op. Use as a scratch channel for temporary logs that must not ship. */ test(...args: unknown[]) { } log(...args: unknown[]) { console.log(...args) CustomLogger.applyTransport('log', ...args) } debug(...args: unknown[]) { console.debug(...args) CustomLogger.applyTransport('debug', ...args) } } /** * Wires up the transport list and returns a ready-to-use {@link CustomLogger} instance. * * The returned instance satisfies the logging method contract expected by {@link Logger}, * so it should be used to replace the stub methods on the shared `logger` singleton after * calling `logger.initialize(config)`. * * Calling this more than once replaces the shared transport list for all existing instances. */ export const createLogger = (options: LoggerOptions) => { CustomLogger.transport = options.transport return new CustomLogger() }