Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | 1x 1x 1x 1x 21x 21x 1x 1x 21x 1x 1x 19x 6x 16x 1x 1x 16x 16x 6x 6x 6x 19x 19x 54x 34x 34x 20x 20x 20x 54x 19x 68x 9x 9x 14x 7x 7x 14x 14x 9x 59x 68x 19x 31x 31x 31x 62x 22x 22x 62x 31x 31x 19x 23x 23x 23x 23x 23x 23x 23x 23x 46x 18x 18x 46x 23x 23x 19x 19x 19x 6x 6x 19x 20x 20x 20x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x | import type { Logger, LogLevel, LogMessage, LogTemplateStringsArray } from './definition.ts'; import type { ForgeFinalizer, MergeFinalizer } from './implementation/finalizer.ts'; import { asSingleFinalizer } from './implementation/finalizer.ts'; import { HIGHEST_SEVERITY_LOG_LEVEL, toLevelSeverity } from './implementation/level-utils.ts'; import { OFF_LOGGER } from './off-logger.ts'; /** * Creates a new logger that forwards all log entries to the provided loggers. * * The returned logger (a "tee") behaves like a standard logger but duplicates every log operation to each of the * underlying loggers. * * ### Log level behavior * * The tee's `level` is computed from the underlying loggers to be the less severe among them. It does not expose a * setter and does not attempt to keep child levels in sync. Changing the level of individual child loggers after * composing may lead to different filtering per destination, which is expected. * * @example * * ```ts * import { createConsoleErrorLogger, tee } from 'emitnlog/logger'; * import { createFileLogger } from 'emitnlog/logger/node'; * * const consoleLogger = createConsoleErrorLogger('info'); * const fileLogger = createFileLogger('~/tmp/entries.log'); * const logger = tee(consoleLogger, fileLogger); * logger.i`This will be logged to both console error and file`; * ``` * * @param loggers One or more loggers to combine. * @returns A new logger that fans out logs to the provided loggers. Returns the 'off logger' if loggers is empty or the * specified logger is loggers length is one. */ export const tee = <T extends readonly Logger[]>(...loggers: T): TeeLogger<T> => { loggers = loggers.filter((logger) => logger !== OFF_LOGGER) as unknown as T; if (!loggers.length) { return OFF_LOGGER as TeeLogger<T>; } if (loggers.length === 1) { return loggers[0] as TeeLogger<T>; } const computeLevel = (): LogLevel | 'off' => { const level = loggers.reduce<LogLevel | 'off'>((acc, logger) => { if (logger.level === 'off') { return acc; } if (acc === 'off') { return logger.level; } // Return the lowest level among all loggers return toLevelSeverity(acc) <= toLevelSeverity(logger.level) ? acc : logger.level; }, HIGHEST_SEVERITY_LOG_LEVEL); return level; }; let pendingArgs: unknown[] = []; const consumePendingArgs = (): readonly unknown[] | undefined => { if (!pendingArgs.length) { return undefined; } const args = pendingArgs; pendingArgs = []; return args; }; type TeeInput = LogMessage | Error | { error: unknown }; const toTeeInputProvider = <I>(message: I): I => { if (typeof message === 'function') { let cache: unknown = toTeeInputProvider; return (() => { if (cache === toTeeInputProvider) { cache = (message as () => I)(); } return cache; }) as I; } return message; }; const runLogOperation = <I extends TeeInput>(input: I, operation: (logger: Logger, input: I) => void) => { const currentArgs = consumePendingArgs(); input = toTeeInputProvider(input); loggers.forEach((logger) => { if (currentArgs) { logger.args(...currentArgs); } operation(logger, input); }); }; const runTemplateOperation = <I extends LogTemplateStringsArray>( input: I, values: unknown[], operation: (logger: Logger, input: I, values: unknown[]) => void, ) => { const currentArgs = consumePendingArgs(); input = toTeeInputProvider(input); values = values.map(toTeeInputProvider); loggers.forEach((logger) => { if (currentArgs) { logger.args(...currentArgs); } operation(logger, input, values); }); }; const finalizer = asSingleFinalizer(...loggers); const teeLogger: Logger = { get level() { return computeLevel(); }, args: (...args) => { pendingArgs.push(...args); return teeLogger; }, trace: (message, ...args) => runLogOperation(message, (logger, i) => logger.trace(i, ...args)), debug: (message, ...args) => runLogOperation(message, (logger, i) => logger.debug(i, ...args)), info: (message, ...args) => runLogOperation(message, (logger, i) => logger.info(i, ...args)), notice: (message, ...args) => runLogOperation(message, (logger, i) => logger.notice(i, ...args)), error: (input, ...args) => runLogOperation(input, (logger, i) => logger.error(i, ...args)), warning: (input, ...args) => runLogOperation(input, (logger, i) => logger.warning(i, ...args)), critical: (input, ...args) => runLogOperation(input, (logger, i) => logger.critical(i, ...args)), alert: (input, ...args) => runLogOperation(input, (logger, i) => logger.alert(i, ...args)), emergency: (input, ...args) => runLogOperation(input, (logger, i) => logger.emergency(i, ...args)), log: (level, message, ...args) => runLogOperation(message, (logger, i) => logger.log(level, i, ...args)), t: (strings, ...values) => runTemplateOperation(strings, values, (logger, i, v) => logger.t(i, ...v)), d: (strings, ...values) => runTemplateOperation(strings, values, (logger, i, v) => logger.d(i, ...v)), i: (strings, ...values) => runTemplateOperation(strings, values, (logger, i, v) => logger.i(i, ...v)), n: (strings, ...values) => runTemplateOperation(strings, values, (logger, i, v) => logger.n(i, ...v)), w: (strings, ...values) => runTemplateOperation(strings, values, (logger, i, v) => logger.w(i, ...v)), e: (strings, ...values) => runTemplateOperation(strings, values, (logger, i, v) => logger.e(i, ...v)), c: (strings, ...values) => runTemplateOperation(strings, values, (logger, i, v) => logger.c(i, ...v)), a: (strings, ...values) => runTemplateOperation(strings, values, (logger, i, v) => logger.a(i, ...v)), em: (strings, ...values) => runTemplateOperation(strings, values, (logger, i, v) => logger.em(i, ...v)), ...finalizer, }; return teeLogger as TeeLogger<T>; }; type TeeLogger<Ls extends readonly Logger[]> = MergeFinalizer<Logger, ForgeFinalizer<Ls>>; |