UNPKG

logger4node

Version:

![build](https://github.com/yog27ray/logger4node/actions/workflows/node.js.yml/badge.svg?branch=master) [![codecov](https://codecov.io/gh/yog27ray/logger4node/branch/master/graph/badge.svg)](https://codecov.io/gh/yog27ray/logger4node)

289 lines (284 loc) 10.6 kB
import util from 'util'; import { AsyncLocalStorage } from 'async_hooks'; import { v4 } from 'uuid'; const asyncLocalStorage = new AsyncLocalStorage(); class Trace { static getRequestInfo() { return asyncLocalStorage.getStore(); } static requestHandler(callback) { return (req, res, next) => { Trace.startNewRequest(next, (callback ? callback(req) : undefined)); }; } static startNewRequest(callback, track = {}) { asyncLocalStorage.run({ id: v4(), ...track, }, () => callback()); } } var LogSeverity; (function (LogSeverity) { LogSeverity["DEBUG"] = "debug"; LogSeverity["ERROR"] = "error"; LogSeverity["FATAL"] = "fatal"; LogSeverity["INFO"] = "info"; LogSeverity["VERBOSE"] = "verbose"; LogSeverity["WARN"] = "warn"; })(LogSeverity || (LogSeverity = {})); const LogLevel = { debug: 2, error: 5, fatal: 6, info: 3, verbose: 1, warn: 4, }; const DisplaySeverityMap = { debug: 'Debug', error: 'Error', fatal: 'Fatal', info: 'Info', verbose: 'Verbose', warn: 'Warn', }; const currentFolder = __dirname; class Logger { constructor(loggerName, config) { this.name = loggerName; this.config = config; } static errorStack(...args) { const errorStacks = args .filter((each) => (each instanceof Error)) .map((each) => each.stack); if (!errorStacks.length) { return ''; } return errorStacks.join('\\n|\\n'); } static jsonTransformArgs(...args) { return util.format(...args.map((each) => { if (['bigint', 'boolean', 'function', 'number', 'string'].includes(typeof each)) { return each; } return stringify(each); })); } static transformArgs(...args) { return args.map((each) => { if (['bigint', 'boolean', 'function', 'number', 'string', 'undefined'].includes(typeof each)) { return each; } if (each instanceof Error) { return each; } return stringify(each); }); } debug(formatter, ...args) { this.log(LogSeverity.DEBUG, undefined, formatter, ...args); } error(formatter, ...args) { this.log(LogSeverity.ERROR, undefined, formatter, ...args); } fatal(formatter, ...args) { this.log(LogSeverity.FATAL, undefined, formatter, ...args); } info(formatter, ...args) { this.log(LogSeverity.INFO, undefined, formatter, ...args); } log(logSeverity, extraData, formatter, ...args) { if (!this.isLogEnabled(logSeverity)) { return; } if (this.config.jsonLogging()) { const data = { className: this.name, extra: extraData || {}, level: logSeverity, message: Logger.jsonTransformArgs(formatter, ...args), request: Trace.getRequestInfo(), source: this.generateLogSource(), stack: Logger.errorStack(formatter, ...args), time: new Date().toISOString(), }; console.log(this.config.disableJsonStringify() ? data : stringify(data)); return; } console.log(`${DisplaySeverityMap[logSeverity]}:`, this.name, util.format(formatter, ...Logger.transformArgs(...args))); } verbose(formatter, ...args) { this.log(LogSeverity.VERBOSE, undefined, formatter, ...args); } warn(formatter, ...args) { this.log(LogSeverity.WARN, undefined, formatter, ...args); } generateGithubLink(file, line) { if (!this.config.github) { return undefined; } const githubFilePath = file.split(this.config.github.basePath)[1]; if (githubFilePath.includes('node_modules')) { return undefined; } return `https://github.com/${this.config.github.org}/${this.config.github.repo}/blob/${this.config.github.commitHash}${githubFilePath}#L${line}`; } generateLogSource() { const { stack } = new Error(); const logSource = stack.split('\n') // .find((line): boolean => !ignoreFolders.some((folder: string): boolean => line.includes(folder)) // && line.trim().startsWith('at ')); .find((line) => !line.includes(currentFolder) && line.trim().startsWith('at ')); if (!logSource) { return {}; } if (logSource.endsWith(')')) { const [caller, filePath] = logSource.split(' ('); if (!filePath) { return {}; } const filePathSplit = filePath.substring(0, filePath.length - 1).split('/'); const [fileName, line, column] = filePathSplit.pop().split(':'); if (!fileName || !line || !column) { return {}; } const path = filePathSplit.join('/'); return { caller: caller.split('at ')[1], column, fileName, github: this.generateGithubLink(`${path}/${fileName}`, line), line, path, }; } const filePathSplit = logSource.split('at ')[1].split('/'); const [fileName, line, column] = filePathSplit.pop().split(':'); if (!fileName || !line || !column) { return {}; } const path = filePathSplit.join('/'); return { column, fileName, github: this.generateGithubLink(`${path}/${fileName}`, line), line, path, }; } isLogEnabled(logSeverity) { if (!isNotMatchWithPatterns(this.config.logSeverityPattern[logSeverity].negative, this.name)) { return false; } if (isMatchWithPatterns(this.config.logSeverityPattern[logSeverity].positive, this.name)) { return true; } if (LogLevel[logSeverity] < this.config.minLogLevelEnabled()) { return false; } if (!isNotMatchWithPatterns(this.config.logPattern.negative, this.name)) { return false; } return isMatchWithPatterns(this.config.logPattern.positive, this.name); } } function setLogPattern(logPattern, pattern) { logPattern.positive.splice(0, logPattern.positive.length); logPattern.negative.splice(0, logPattern.positive.length); const [positive, negative] = generateMatchAndDoesNotMatchArray(pattern); logPattern.positive.push(...positive); logPattern.negative.push(...negative); } function setLogSeverityPattern(logSeverityPattern, level, pattern) { logSeverityPattern[level].positive.splice(0, logSeverityPattern[level].positive.length); logSeverityPattern[level].negative.splice(0, logSeverityPattern[level].positive.length); const [positive, negative] = pattern ? generateMatchAndDoesNotMatchArray(pattern) : [[], []]; logSeverityPattern[level].positive.push(...positive); logSeverityPattern[level].negative.push(...negative); } function generateMatchAndDoesNotMatchArray(input = '') { const positive = []; const negative = []; input.split(',').forEach((key_) => { let key = key_; let operator = '+'; if (key.startsWith('-')) { operator = '-'; key = key.substring(1, key.length); } key = key.replace(/\*/g, '.*'); switch (operator) { case '-': { negative.push(key); return; } default: { positive.push(key); } } }); return [positive, negative]; } function isMatchWithPatterns(patterns, value) { return patterns.some((pattern) => new RegExp(`^${pattern}$`).test(value)); } function isNotMatchWithPatterns(patterns, value) { return patterns.every((pattern) => !new RegExp(`^${pattern}$`).test(value)); } function stringify(data) { return JSON.stringify(data); } class Logger4Node { constructor(applicationName, option = {}) { this.disableJsonStringify = false; this.jsonLogging = false; this.logPattern = { negative: [], positive: [] }; this.logSeverityPattern = { [LogSeverity.DEBUG]: { negative: [], positive: [] }, [LogSeverity.ERROR]: { negative: [], positive: [] }, [LogSeverity.FATAL]: { negative: [], positive: [] }, [LogSeverity.INFO]: { negative: [], positive: [] }, [LogSeverity.VERBOSE]: { negative: [], positive: [] }, [LogSeverity.WARN]: { negative: [], positive: [] }, }; this.minLogLevelEnabled = LogLevel[LogSeverity.DEBUG]; this.stringLogging = false; this._applicationName = applicationName; this.github = option.github ? { ...option.github } : undefined; this.setLogLevel(process.env.DEBUG_LEVEL); this.setLogPattern(process.env.DEBUG); console.log(`App: ${applicationName}`, 'Default logging details :', process.env.DEBUG_LEVEL, process.env.DEBUG); Object.keys(LogLevel) .forEach((logSeverity) => this.setLogSeverityPattern(logSeverity, process.env[`LOG_${logSeverity.toUpperCase()}`])); } instance(name) { return new Logger(`${this._applicationName}:${name}`, { disableJsonStringify: () => this.disableJsonStringify, github: this.github, jsonLogging: () => this.jsonLogging, logPattern: this.logPattern, logSeverityPattern: this.logSeverityPattern, minLogLevelEnabled: () => this.minLogLevelEnabled, }); } setDisableJsonStringify(disableJsonStringify) { this.disableJsonStringify = disableJsonStringify; } setJsonLogging(jsonLogging) { this.jsonLogging = jsonLogging; } setLogLevel(logSeverity = process.env.DEBUG_LEVEL) { this.minLogLevelEnabled = LogLevel[logSeverity] || LogLevel[LogSeverity.DEBUG]; } setLogPattern(pattern = process.env.DEBUG) { setLogPattern(this.logPattern, pattern); } setLogSeverityPattern(level, pattern) { setLogSeverityPattern(this.logSeverityPattern, level, pattern || process.env[`LOG_${level.toUpperCase()}`]); } setStringLogging(stringOnly) { this.stringLogging = stringOnly; } } Logger4Node.Trace = Trace; export { LogSeverity, Logger, Logger4Node }; //# sourceMappingURL=index.js.map