import { CommonCfg } from "./CommonCfg"; import { RootCfg } from "./RootCfg"; import { GlobalConfig, CommonConfig, ContextValues, MessageEnvelope } from "./types"; import { NamedLogger } from './NamedLogger'; import { Output } from './Output'; import { isMatch } from 'matcher'; export abstract class Lumbermill { rootCfg: RootCfg; loggerCfgs: Record = {}; outputs: Output[]; loggers: Record = {}; constructor(cfg: GlobalConfig, outputs: Output[]) { this.rootCfg = new RootCfg(cfg); this.outputs = outputs.map((output) => { output.parent = this.rootCfg; return output; }); } abstract parseDebugVarPatterns(debugVar: string | undefined): string[]; getDebugVarPatterns(loggerDebugVar: string | undefined, outputDebugVar: string | undefined): string[] { let debugVarPatterns: string[] = []; if (outputDebugVar !== undefined) { // if output has debugVar set, then ignore any logger debugVar settings and // just use output. // this allows you to setup debug on a specific output, example: // only send logs to datadog output when debugVar is set, but always be outputting // on console output debugVarPatterns = this.parseDebugVarPatterns(outputDebugVar); } else { if (loggerDebugVar !== undefined ) { debugVarPatterns = this.parseDebugVarPatterns(loggerDebugVar); } } return debugVarPatterns.map((patt: string) => { let fixed = patt; if (patt.startsWith('-')) { fixed = `!${patt.slice(1)}`; } return fixed; }); } static isLogLevelEnabled(msgLvl: string, currentLogLvl: string | undefined, logLevels: string[]): boolean { // const outputConfig = this.getConfig(); // const { logLevel, logLevels } = cfg.getConfig(); if (currentLogLvl === undefined) { return true; } // const logLevels: string[] = this.rootCfg.logLevels; const enabledLogLevelIndex: number = logLevels.indexOf(currentLogLvl); const msgLvlIndex: number = logLevels.indexOf(msgLvl); return msgLvlIndex <= enabledLogLevelIndex; } processLog(cfg: CommonCfg, messageCtx: ContextValues = {}, level: string, ...args: any[]): void { // first check if logger is enabled const { enabled: loggerEnabled, logLevel: loggerLogLevel, logLevels, debugVar: loggerDebugVar, context: loggerCtx, } = cfg.getConfig(); const loggerLevelEnabled = Lumbermill.isLogLevelEnabled(level, loggerLogLevel, logLevels); if (loggerEnabled && loggerLevelEnabled) { for (const output of this.outputs) { const { enabled: outputEnabled, logLevel: outputLogLevel, debugVar: outputDebugVar, context: outputCtx, } = output.getConfig(false); // then check if output is enabled const outputLevelEnabled = Lumbermill.isLogLevelEnabled(level, outputLogLevel, logLevels); if (!outputEnabled || !outputLevelEnabled) { continue; } const debugVarPatterns = this.getDebugVarPatterns(loggerDebugVar, outputDebugVar); const loggerName = cfg.name; const matchedDebugVar = isMatch(loggerName, debugVarPatterns); if (debugVarPatterns.length === 0 || matchedDebugVar) { // const logger = this.loggers[loggerName].meta.color const env: MessageEnvelope = { logger: loggerName, level, ctx: { ...(loggerCtx), ...(outputCtx), ...(messageCtx), }, args, } output.writeLog(env); } } } } getOrCreateLogger(name: string, cfg?: CommonConfig): NamedLogger { let logger = this.loggers[name]; if (!logger) { const loggerCfg = new CommonCfg(name, cfg, this.rootCfg); const logLevels = loggerCfg.getConfig().logLevels; const loggingFuncs = { setLogLevel: loggerCfg.setLogLevel, setContext: loggerCfg.setContext, addContext: loggerCfg.addContext, // ...lls, }; for (const logLevel of logLevels) { // loggingFuncs[logLevel] = console loggingFuncs[logLevel] = this.processLog.bind(this, loggerCfg, {}, logLevel); loggingFuncs[logLevel].withContext = (ctx: ContextValues, ...args) => { this.processLog(loggerCfg, ctx, logLevel, ...args); } } logger = new NamedLogger(loggerCfg, loggingFuncs); this.loggers[name] = logger; } return logger; } getLogger(name: string, config?: CommonConfig) { const l = this.getOrCreateLogger(name, config); return l.out; } addOutput(output: Output) { this.outputs.push(output); } setOutputs(outputs: Output[]) { this.outputs = outputs; } }