UNPKG

@lorefnon/tslog

Version:

Extensible TypeScript Logger for Node.js and Browser.

315 lines (314 loc) 16.2 kB
import { formatTemplate } from "./formatTemplate.js"; import { formatNumberAddZeros } from "./formatNumberAddZeros.js"; import { urlToObject } from "./urlToObj.js"; import Runtime from "./runtime/nodejs/index.js"; export * from "./interfaces.js"; export { Runtime }; export class BaseLogger { constructor(settings, logObj, stackDepthLevel = 4) { this.logObj = logObj; this.stackDepthLevel = stackDepthLevel; this.runtime = Runtime; this.settings = { type: settings?.type ?? "pretty", name: settings?.name, parentNames: settings?.parentNames, minLevel: settings?.minLevel ?? 0, argumentsArrayName: settings?.argumentsArrayName, hideLogPositionForProduction: settings?.hideLogPositionForProduction ?? false, prettyLogTemplate: settings?.prettyLogTemplate ?? "{{yyyy}}.{{mm}}.{{dd}} {{hh}}:{{MM}}:{{ss}}:{{ms}}\t{{logLevelName}}\t{{filePathWithLine}}{{nameWithDelimiterPrefix}}\t", prettyErrorTemplate: settings?.prettyErrorTemplate ?? "\n{{errorName}} {{errorMessage}}\nerror stack:\n{{errorStack}}", prettyErrorStackTemplate: settings?.prettyErrorStackTemplate ?? " • {{fileName}}\t{{method}}\n\t{{filePathWithLine}}", prettyErrorParentNamesSeparator: settings?.prettyErrorParentNamesSeparator ?? ":", prettyErrorLoggerNameDelimiter: settings?.prettyErrorLoggerNameDelimiter ?? "\t", stylePrettyLogs: settings?.stylePrettyLogs ?? true, prettyLogTimeZone: settings?.prettyLogTimeZone ?? "UTC", prettyLogStyles: settings?.prettyLogStyles ?? { logLevelName: { "*": ["bold", "black", "bgWhiteBright", "dim"], SILLY: ["bold", "white"], TRACE: ["bold", "whiteBright"], DEBUG: ["bold", "green"], INFO: ["bold", "blue"], WARN: ["bold", "yellow"], ERROR: ["bold", "red"], FATAL: ["bold", "redBright"], }, dateIsoStr: "white", filePathWithLine: "white", name: ["white", "bold"], nameWithDelimiterPrefix: ["white", "bold"], nameWithDelimiterSuffix: ["white", "bold"], errorName: ["bold", "bgRedBright", "whiteBright"], fileName: ["yellow"], fileNameWithLine: "white", }, prettyInspectOptions: settings?.prettyInspectOptions ?? { colors: true, compact: false, depth: Infinity, }, metaProperty: settings?.metaProperty ?? "_meta", maskPlaceholder: settings?.maskPlaceholder ?? "[***]", maskValuesOfKeys: settings?.maskValuesOfKeys ?? ["password"], maskValuesOfKeysCaseInsensitive: settings?.maskValuesOfKeysCaseInsensitive ?? false, maskValuesRegEx: settings?.maskValuesRegEx, prefix: [...(settings?.prefix ?? [])], attachedTransports: [...(settings?.attachedTransports ?? [])], overwrite: { mask: settings?.overwrite?.mask, toLogObj: settings?.overwrite?.toLogObj, addMeta: settings?.overwrite?.addMeta, addPlaceholders: settings?.overwrite?.addPlaceholders, formatMeta: settings?.overwrite?.formatMeta, formatLogObj: settings?.overwrite?.formatLogObj, transportFormatted: settings?.overwrite?.transportFormatted, transportJSON: settings?.overwrite?.transportJSON, }, }; } log(logLevelId, logLevelName, ...args) { if (logLevelId < this.settings.minLevel) { return; } const logArgs = [...this.settings.prefix, ...args]; const maskedArgs = this.settings.overwrite?.mask != null ? this.settings.overwrite?.mask(logArgs) : this.settings.maskValuesOfKeys != null && this.settings.maskValuesOfKeys.length > 0 ? this._mask(logArgs) : logArgs; const thisLogObj = this.logObj != null ? this._recursiveCloneAndExecuteFunctions(this.logObj) : undefined; const logObj = this.settings.overwrite?.toLogObj != null ? this.settings.overwrite?.toLogObj(maskedArgs, thisLogObj) : this._toLogObj(maskedArgs, thisLogObj); const logObjWithMeta = this.settings.overwrite?.addMeta != null ? this.settings.overwrite?.addMeta(logObj, logLevelId, logLevelName) : this._addMetaToLogObj(logObj, logLevelId, logLevelName); let logMetaMarkup; let logArgsAndErrorsMarkup = undefined; if (this.settings.overwrite?.formatMeta != null) { logMetaMarkup = this.settings.overwrite?.formatMeta(logObjWithMeta?.[this.settings.metaProperty]); } if (this.settings.overwrite?.formatLogObj != null) { logArgsAndErrorsMarkup = this.settings.overwrite?.formatLogObj(maskedArgs, this.settings); } if (this.settings.type === "pretty") { logMetaMarkup = logMetaMarkup ?? this._prettyFormatLogObjMeta(logObjWithMeta?.[this.settings.metaProperty]); logArgsAndErrorsMarkup = logArgsAndErrorsMarkup ?? this.runtime.prettyFormatLogObj(maskedArgs, this.settings); } if (logMetaMarkup != null && logArgsAndErrorsMarkup != null) { this.settings.overwrite?.transportFormatted != null ? this.settings.overwrite?.transportFormatted(logMetaMarkup, logArgsAndErrorsMarkup.args, logArgsAndErrorsMarkup.errors, this.settings) : this.runtime.transportFormatted(logMetaMarkup, logArgsAndErrorsMarkup.args, logArgsAndErrorsMarkup.errors, this.settings); } else { this.settings.overwrite?.transportJSON != null ? this.settings.overwrite?.transportJSON(logObjWithMeta) : this.settings.type !== "hidden" ? this.runtime.transportJSON(logObjWithMeta) : undefined; } if (this.settings.attachedTransports != null && this.settings.attachedTransports.length > 0) { this.settings.attachedTransports.forEach((transportLogger) => { transportLogger(logObjWithMeta); }); } return logObjWithMeta; } attachTransport(transportLogger) { this.settings.attachedTransports.push(transportLogger); } getSubLogger(settings, logObj) { const subLoggerSettings = { ...this.settings, ...settings, parentNames: this.settings?.parentNames != null && this.settings?.name != null ? [...this.settings.parentNames, this.settings.name] : this.settings?.name != null ? [this.settings.name] : undefined, prefix: [...this.settings.prefix, ...(settings?.prefix ?? [])], }; const subLogger = new this.constructor(subLoggerSettings, logObj ?? this.logObj, this.stackDepthLevel); return subLogger; } _mask(args) { const maskValuesOfKeys = this.settings.maskValuesOfKeysCaseInsensitive !== true ? this.settings.maskValuesOfKeys : this.settings.maskValuesOfKeys.map((key) => key.toLowerCase()); return args?.map((arg) => { return this._recursiveCloneAndMaskValuesOfKeys(arg, maskValuesOfKeys); }); } _recursiveCloneAndMaskValuesOfKeys(source, keys, seen = []) { if (seen.includes(source)) { return { ...source }; } if (typeof source === "object" && source !== null) { seen.push(source); } if (this.runtime.isError(source) || this.runtime.isBuffer(source)) { return source; } else if (source instanceof Map) { return new Map(source); } else if (source instanceof Set) { return new Set(source); } else if (Array.isArray(source)) { return source.map((item) => this._recursiveCloneAndMaskValuesOfKeys(item, keys, seen)); } else if (source instanceof Date) { return new Date(source.getTime()); } else if (source instanceof URL) { return urlToObject(source); } else if (source !== null && typeof source === "object") { const baseObject = this.runtime.isError(source) ? this._cloneError(source) : Object.create(Object.getPrototypeOf(source)); return Object.getOwnPropertyNames(source).reduce((o, prop) => { o[prop] = keys.includes(this.settings?.maskValuesOfKeysCaseInsensitive !== true ? prop : prop.toLowerCase()) ? this.settings.maskPlaceholder : this._recursiveCloneAndMaskValuesOfKeys(source[prop], keys, seen); return o; }, baseObject); } else { if (typeof source === "string") { let modifiedSource = source; for (const regEx of this.settings?.maskValuesRegEx || []) { modifiedSource = modifiedSource.replace(regEx, this.settings?.maskPlaceholder || ""); } return modifiedSource; } return source; } } _recursiveCloneAndExecuteFunctions(source, seen = []) { if (this.isObjectOrArray(source) && seen.includes(source)) { return this.shallowCopy(source); } if (this.isObjectOrArray(source)) { seen.push(source); } if (Array.isArray(source)) { return source.map((item) => this._recursiveCloneAndExecuteFunctions(item, seen)); } else if (source instanceof Date) { return new Date(source.getTime()); } else if (this.isObject(source)) { return Object.getOwnPropertyNames(source).reduce((o, prop) => { const descriptor = Object.getOwnPropertyDescriptor(source, prop); if (descriptor) { Object.defineProperty(o, prop, descriptor); const value = source[prop]; o[prop] = typeof value === "function" ? value() : this._recursiveCloneAndExecuteFunctions(value, seen); } return o; }, Object.create(Object.getPrototypeOf(source))); } else { return source; } } isObjectOrArray(value) { return typeof value === "object" && value !== null; } isObject(value) { return typeof value === "object" && !Array.isArray(value) && value !== null; } shallowCopy(source) { if (Array.isArray(source)) { return [...source]; } else { return { ...source }; } } _toLogObj(args, clonedLogObj = {}) { args = args?.map((arg) => (this.runtime.isError(arg) ? this._toErrorObject(arg) : arg)); if (this.settings.argumentsArrayName == null) { if (args.length === 1 && !Array.isArray(args[0]) && this.runtime.isBuffer(args[0]) !== true && !(args[0] instanceof Date)) { clonedLogObj = typeof args[0] === "object" && args[0] != null ? { ...args[0], ...clonedLogObj } : { 0: args[0], ...clonedLogObj }; } else { clonedLogObj = { ...clonedLogObj, ...args }; } } else { clonedLogObj = { ...clonedLogObj, [this.settings.argumentsArrayName]: args, }; } return clonedLogObj; } _cloneError(error) { const cloned = new error.constructor(); Object.getOwnPropertyNames(error).forEach((key) => { cloned[key] = error[key]; }); return cloned; } _toErrorObject(error) { return { nativeError: error, name: error.name ?? "Error", message: error.message, stack: this.runtime.getErrorTrace(error), }; } _addMetaToLogObj(logObj, logLevelId, logLevelName) { return { ...logObj, [this.settings.metaProperty]: this.runtime.getMeta(logLevelId, logLevelName, this.stackDepthLevel, this.settings.hideLogPositionForProduction, this.settings.name, this.settings.parentNames), }; } _prettyFormatLogObjMeta(logObjMeta) { if (logObjMeta == null) { return ""; } let template = this.settings.prettyLogTemplate; const placeholderValues = {}; if (template.includes("{{yyyy}}.{{mm}}.{{dd}} {{hh}}:{{MM}}:{{ss}}:{{ms}}")) { template = template.replace("{{yyyy}}.{{mm}}.{{dd}} {{hh}}:{{MM}}:{{ss}}:{{ms}}", "{{dateIsoStr}}"); } else { if (this.settings.prettyLogTimeZone === "UTC") { placeholderValues["yyyy"] = logObjMeta?.date?.getUTCFullYear() ?? "----"; placeholderValues["mm"] = formatNumberAddZeros(logObjMeta?.date?.getUTCMonth(), 2, 1); placeholderValues["dd"] = formatNumberAddZeros(logObjMeta?.date?.getUTCDate(), 2); placeholderValues["hh"] = formatNumberAddZeros(logObjMeta?.date?.getUTCHours(), 2); placeholderValues["MM"] = formatNumberAddZeros(logObjMeta?.date?.getUTCMinutes(), 2); placeholderValues["ss"] = formatNumberAddZeros(logObjMeta?.date?.getUTCSeconds(), 2); placeholderValues["ms"] = formatNumberAddZeros(logObjMeta?.date?.getUTCMilliseconds(), 3); } else { placeholderValues["yyyy"] = logObjMeta?.date?.getFullYear() ?? "----"; placeholderValues["mm"] = formatNumberAddZeros(logObjMeta?.date?.getMonth(), 2, 1); placeholderValues["dd"] = formatNumberAddZeros(logObjMeta?.date?.getDate(), 2); placeholderValues["hh"] = formatNumberAddZeros(logObjMeta?.date?.getHours(), 2); placeholderValues["MM"] = formatNumberAddZeros(logObjMeta?.date?.getMinutes(), 2); placeholderValues["ss"] = formatNumberAddZeros(logObjMeta?.date?.getSeconds(), 2); placeholderValues["ms"] = formatNumberAddZeros(logObjMeta?.date?.getMilliseconds(), 3); } } const dateInSettingsTimeZone = this.settings.prettyLogTimeZone === "UTC" ? logObjMeta?.date : new Date(logObjMeta?.date?.getTime() - logObjMeta?.date?.getTimezoneOffset() * 60000); placeholderValues["rawIsoStr"] = dateInSettingsTimeZone?.toISOString(); placeholderValues["dateIsoStr"] = dateInSettingsTimeZone?.toISOString().replace("T", " ").replace("Z", ""); placeholderValues["logLevelName"] = logObjMeta?.logLevelName; placeholderValues["fileNameWithLine"] = logObjMeta?.path?.fileNameWithLine ?? ""; placeholderValues["filePathWithLine"] = logObjMeta?.path?.filePathWithLine ?? ""; placeholderValues["fullFilePath"] = logObjMeta?.path?.fullFilePath ?? ""; let parentNamesString = this.settings.parentNames?.join(this.settings.prettyErrorParentNamesSeparator); parentNamesString = parentNamesString != null && logObjMeta?.name != null ? parentNamesString + this.settings.prettyErrorParentNamesSeparator : undefined; placeholderValues["name"] = logObjMeta?.name != null || parentNamesString != null ? (parentNamesString ?? "") + logObjMeta?.name ?? "" : ""; placeholderValues["nameWithDelimiterPrefix"] = placeholderValues["name"].length > 0 ? this.settings.prettyErrorLoggerNameDelimiter + placeholderValues["name"] : ""; placeholderValues["nameWithDelimiterSuffix"] = placeholderValues["name"].length > 0 ? placeholderValues["name"] + this.settings.prettyErrorLoggerNameDelimiter : ""; if (this.settings.overwrite?.addPlaceholders != null) { this.settings.overwrite?.addPlaceholders(logObjMeta, placeholderValues); } return formatTemplate(this.settings, template, placeholderValues); } }