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)

350 lines (340 loc) 12.6 kB
'use strict'; var util = require('util'); var async_hooks = require('async_hooks'); const byteToHex = []; for (let i = 0; i < 256; ++i) { byteToHex.push((i + 0x100).toString(16).slice(1)); } function unsafeStringify(arr, offset = 0) { return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); } let getRandomValues; const rnds8 = new Uint8Array(16); function rng() { if (!getRandomValues) { if (typeof crypto === 'undefined' || !crypto.getRandomValues) { throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported'); } getRandomValues = crypto.getRandomValues.bind(crypto); } return getRandomValues(rnds8); } const randomUUID = typeof crypto !== 'undefined' && crypto.randomUUID && crypto.randomUUID.bind(crypto); var native = { randomUUID }; function _v4(options, buf, offset) { options = options || {}; const rnds = options.random ?? options.rng?.() ?? rng(); if (rnds.length < 16) { throw new Error('Random bytes length must be >= 16'); } rnds[6] = (rnds[6] & 0x0f) | 0x40; rnds[8] = (rnds[8] & 0x3f) | 0x80; return unsafeStringify(rnds); } function v4(options, buf, offset) { if (native.randomUUID && true && !options) { return native.randomUUID(); } return _v4(options); } const asyncLocalStorage = new async_hooks.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()); } } exports.LogSeverity = void 0; (function (LogSeverity) { LogSeverity["DEBUG"] = "debug"; LogSeverity["ERROR"] = "error"; LogSeverity["FATAL"] = "fatal"; LogSeverity["INFO"] = "info"; LogSeverity["VERBOSE"] = "verbose"; LogSeverity["WARN"] = "warn"; })(exports.LogSeverity || (exports.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(exports.LogSeverity.DEBUG, undefined, formatter, ...args); } error(formatter, ...args) { this.log(exports.LogSeverity.ERROR, undefined, formatter, ...args); } fatal(formatter, ...args) { this.log(exports.LogSeverity.FATAL, undefined, formatter, ...args); } info(formatter, ...args) { this.log(exports.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(exports.LogSeverity.VERBOSE, undefined, formatter, ...args); } warn(formatter, ...args) { this.log(exports.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 = { [exports.LogSeverity.DEBUG]: { negative: [], positive: [] }, [exports.LogSeverity.ERROR]: { negative: [], positive: [] }, [exports.LogSeverity.FATAL]: { negative: [], positive: [] }, [exports.LogSeverity.INFO]: { negative: [], positive: [] }, [exports.LogSeverity.VERBOSE]: { negative: [], positive: [] }, [exports.LogSeverity.WARN]: { negative: [], positive: [] }, }; this.minLogLevelEnabled = LogLevel[exports.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[exports.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; exports.Logger = Logger; exports.Logger4Node = Logger4Node; //# sourceMappingURL=index.cjs.map