/** * Provide function to patch Winston logger * to add dynamic props for each logs */ const { LEVEL, SPLAT } = require("triple-beam"); const path = require("path"), callerId = require("caller-id"), readPkgUp = require("read-pkg-up"); export function patchLogger( loggy, { patchConSole = false }: { patchConSole?: boolean } = {} ) { _patchMainLog(loggy); _patchShortcut(loggy); if (patchConSole) { _patchConsole(loggy); } return loggy; } //overriding dirty console methods function _patchConsole(loggy) { let levels = ["log", "error", "warn", "info", "debug"]; levels.forEach(level => { console[level] = function __patchConsole(...args) { //getting the caller let caller = callerId.getData(__patchConsole); //expensive to generate call stack.. if (Object.keys(loggy.levels).includes(level)) { return loggy.log.call({ caller }, level, ...args); } return loggy.log.call({ caller }, "info", ...args); }; }); } //monkey patching the main log function //https://github.com/winstonjs/winston/blob/master/lib/winston/logger.js#L149 function _patchMainLog(loggy) { let _log = loggy.log; //ref 2 the original func loggy.log = function __log(level, msg, ...splat) { //getting dynamic data let caller = this.caller || callerId.getData(__log); //expensive to generate call stack.. let PKG = readPkgUp.sync({ cwd: path.dirname(caller.filePath) }) .package; let props = { label: PKG.name, caller: getRelativePathOfCaller(caller) }; if (arguments.length === 1) { return _log.call(loggy, Object.assign({}, props, level)); } // Slightly less hotpath, but worth optimizing for. if (arguments.length === 2) { if (msg && typeof msg === "object" && !(msg instanceof Error)) { msg[LEVEL] = msg.level = level; return _log.call(loggy, Object.assign({}, props, msg)); } return _log.call( loggy, Object.assign({}, props, { [LEVEL]: level, level, message: msg }) ); } const meta = splat[splat.length - 1]; //metadata always at end!! if (typeof meta === "object" && meta !== null) { return _log.call( loggy, Object.assign({}, props, meta, { meta, //to always have meta prop [LEVEL]: level, [SPLAT]: splat, //.slice(0, -1), level, message: msg }) ); } else { return _log.call( loggy, Object.assign({}, props, { [LEVEL]: level, [SPLAT]: splat, level, message: msg }) ); } return new Error( "Fatal, this should never reached!! https://github.com/winstonjs/winston/blob/master/lib/winston/logger.js#L149" ); }; } //https://github.com/winstonjs/winston/blob/master/lib/winston/create-logger.js //gotta patch each "levels" (info, debug, blablabla) since theres a hotpath branch for function call with 1 arg function _patchShortcut(loggy) { Object.keys(loggy.levels).map(function(level) { let originFunc = loggy[level]; loggy[level] = function __shortCut(...args) { //getting dynamic data let caller = callerId.getData(__shortCut); //expensive to generate call stack.. let PKG = readPkgUp.sync({ cwd: path.dirname(caller.filePath) }) .package; //hotpath if (args.length === 1) { const [msg] = args; const info = (msg && /**https://github.com/winstonjs/winston/issues/1338?_pjax=%23js-repo-pjax-container#issuecomment-436915825*/ !(msg instanceof Error) && msg.message && msg) || { message: msg }; //Decorate extra stuff here info.label = info.label || PKG.name; info.caller = getRelativePathOfCaller(caller); //Done return originFunc.call(loggy, info); } //otherwise propagating caller context return loggy.log.call({ caller }, level, ...args); }; }); } function getRelativePathOfCaller(caller) { return path .normalize( `${path.relative(process.cwd(), caller.filePath)}:${ caller.lineNumber }` ) .split(/[\\\/]/g) .join(path.posix.sep); }