All files / src/logger with-utils.ts

100% Statements 31/31
88.88% Branches 16/18
100% Functions 2/2
100% Lines 31/31

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 1201x   1x 1x 1x                                                                                           1x 41x 41x 41x 41x 1x 1x   40x 40x 40x 182x 182x 180x 180x 182x 40x   41x 41x                                                                             1x 8x 1x 1x   7x 9x 7x   8x 8x  
import { emptyArray } from '../utils/common/singleton.ts';
import type { Logger, LogLevel } from './definition.ts';
import { createLogger } from './emitter/emitter-logger.ts';
import { OFF_LOGGER } from './off-logger.ts';
import { injectPrefixInformation, isPrefixedLogger } from './prefixed-logger.ts';
 
/**
 * Returns a logger that emits all entries using a fixed level, regardless of the log method used.
 *
 * Note: The returned logger preserves the original logger’s level filtering behavior, but every emitted entry is
 * rewritten to use the provided `level`. To create a logger with a different level, use {@link withLevel}.
 *
 * This is useful when you want messages of any severity to be treated as, say, errors.
 *
 * @example
 *
 * ```ts
 * import { createConsoleLogLogger, withEmitLevel } from 'emitnlog/logger';
 *
 * const baseLogger = createConsoleLogLogger('info');
 *
 * // A logger that emits 'info' or higher severities, all as errors.
 * const errorLogger = withEmitLevel(baseLogger, 'error');
 *
 * errorLogger.d`debug`; // Not emitted (filtered out by baseLogger.level)
 * errorLogger.i`info`; // Emitted as an error
 * errorLogger.c`error`; // Emitted as an error
 * ```
 *
 * @example Dynamic level
 *
 * ```ts
 * import { createConsoleLogLogger, withEmitLevel } from 'emitnlog/logger';
 *
 * const baseLogger = createConsoleLogLogger('trace');
 *
 * // A logger that emits all severities, outputting 'info' entries for 'trace' and 'debug'
 * const infoLogger = withEmitLevel(baseLogger, (level) =>
 *   level === 'trace' || level === 'debug' ? 'info' : level,
 * );
 *
 * infoLogger.d`debug`; // Emitted as an info
 * infoLogger.i`info`; // Emitted as an info
 * infoLogger.c`error`; // Emitted as a critical
 * ```
 *
 * @param logger The original logger to wrap.
 * @param level The level to emit all entries as (or a function that maps that level).
 * @returns A new logger that emits entries as if they were logged with `level`.
 */
export const withEmitLevel = (
  logger: Logger,
  level: LogLevel | 'off' | ((entryLevel: LogLevel) => LogLevel | 'off'),
): Logger => {
  if (logger === OFF_LOGGER) {
    return OFF_LOGGER;
  }
 
  const newLogger = createLogger(
    () => logger.level,
    (entryLevel, message, args) => {
      const emitLevel = typeof level === 'function' ? level(entryLevel) : level;
      if (emitLevel !== 'off') {
        logger.log(emitLevel, message, ...(args ?? emptyArray()));
      }
    },
  );
 
  return isPrefixedLogger(logger) ? injectPrefixInformation(logger, newLogger) : newLogger;
};
 
/**
 * Returns a logger that evaluates entries with a new minimum level while reusing the original logger for emission.
 *
 * Note: The returned logger only changes the level threshold used to decide whether an entry should be emitted. When an
 * entry passes that check it is logged through the original logger with its original level.
 *
 * @example Enforce a stricter level
 *
 * ```ts
 * import { createConsoleLogLogger, withLevel } from 'emitnlog/logger';
 *
 * const baseLogger = createConsoleLogLogger('trace');
 * const errorOnlyLogger = withLevel(baseLogger, 'error');
 *
 * errorOnlyLogger.i`info`; // Not emitted
 * errorOnlyLogger.e`error`; // Emitted as an error
 * ```
 *
 * @example Dynamic level
 *
 * ```ts
 * import { createConsoleLogLogger, withLevel } from 'emitnlog/logger';
 *
 * let currentLevel: LogLevel = 'info';
 * const adjustableLogger = withLevel(createConsoleLogLogger('trace'), () => currentLevel);
 *
 * adjustableLogger.i`info`; // Emitted while currentLevel is 'info'
 *
 * currentLevel = 'error';
 * adjustableLogger.i`info`; // Filtered out
 * adjustableLogger.e`error`; // Emitted
 * ```
 *
 * @param logger The original logger to wrap.
 * @param level The level (or level provider) to use as the minimum severity threshold.
 * @returns A logger that filters entries using `level` before delegating to `logger`.
 */
export const withLevel = (logger: Logger, level: LogLevel | 'off' | (() => LogLevel | 'off')): Logger => {
  if (logger === OFF_LOGGER) {
    return OFF_LOGGER;
  }
 
  const newLogger = createLogger(level, (entryLevel, message, args) => {
    logger.log(entryLevel, message, ...(args ?? emptyArray()));
  });
 
  return isPrefixedLogger(logger) ? injectPrefixInformation(logger, newLogger) : newLogger;
};