logger4node
Version:
 [](https://codecov.io/gh/yog27ray/logger4node)
289 lines (284 loc) • 10.6 kB
JavaScript
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