logger4node
Version:
 [](https://codecov.io/gh/yog27ray/logger4node)
350 lines (340 loc) • 12.6 kB
JavaScript
;
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