strong-tie/inbound-calls
0
1'use strict'2 3const format = require('quick-format-unescaped')4 5module.exports = pino6 7const _console = pfGlobalThisOrFallback().console || {}8const stdSerializers = {9 mapHttpRequest: mock,10 mapHttpResponse: mock,11 wrapRequestSerializer: passthrough,12 wrapResponseSerializer: passthrough,13 wrapErrorSerializer: passthrough,14 req: mock,15 res: mock,16 err: asErrValue,17 errWithCause: asErrValue18}19function levelToValue (level, logger) {20 return level === 'silent'21 ? Infinity22 : logger.levels.values[level]23}24const baseLogFunctionSymbol = Symbol('pino.logFuncs')25const hierarchySymbol = Symbol('pino.hierarchy')26 27const logFallbackMap = {28 error: 'log',29 fatal: 'error',30 warn: 'error',31 info: 'log',32 debug: 'log',33 trace: 'log'34}35 36function appendChildLogger (parentLogger, childLogger) {37 const newEntry = {38 logger: childLogger,39 parent: parentLogger[hierarchySymbol]40 }41 childLogger[hierarchySymbol] = newEntry42}43 44function setupBaseLogFunctions (logger, levels, proto) {45 const logFunctions = {}46 levels.forEach(level => {47 logFunctions[level] = proto[level] ? proto[level] : (_console[level] || _console[logFallbackMap[level] || 'log'] || noop)48 })49 logger[baseLogFunctionSymbol] = logFunctions50}51 52function shouldSerialize (serialize, serializers) {53 if (Array.isArray(serialize)) {54 const hasToFilter = serialize.filter(function (k) {55 return k !== '!stdSerializers.err'56 })57 return hasToFilter58 } else if (serialize === true) {59 return Object.keys(serializers)60 }61 62 return false63}64 65function pino (opts) {66 opts = opts || {}67 opts.browser = opts.browser || {}68 69 const transmit = opts.browser.transmit70 if (transmit && typeof transmit.send !== 'function') { throw Error('pino: transmit option must have a send function') }71 72 const proto = opts.browser.write || _console73 if (opts.browser.write) opts.browser.asObject = true74 const serializers = opts.serializers || {}75 const serialize = shouldSerialize(opts.browser.serialize, serializers)76 let stdErrSerialize = opts.browser.serialize77 78 if (79 Array.isArray(opts.browser.serialize) &&80 opts.browser.serialize.indexOf('!stdSerializers.err') > -181 ) stdErrSerialize = false82 83 const customLevels = Object.keys(opts.customLevels || {})84 const levels = ['error', 'fatal', 'warn', 'info', 'debug', 'trace'].concat(customLevels)85 86 if (typeof proto === 'function') {87 levels.forEach(function (level) {88 proto[level] = proto89 })90 }91 if (opts.enabled === false || opts.browser.disabled) opts.level = 'silent'92 const level = opts.level || 'info'93 const logger = Object.create(proto)94 if (!logger.log) logger.log = noop95 96 setupBaseLogFunctions(logger, levels, proto)97 // setup root hierarchy entry98 appendChildLogger({}, logger)99 100 Object.defineProperty(logger, 'levelVal', {101 get: getLevelVal102 })103 Object.defineProperty(logger, 'level', {104 get: getLevel,105 set: setLevel106 })107 108 const setOpts = {109 transmit,110 serialize,111 asObject: opts.browser.asObject,112 formatters: opts.browser.formatters,113 levels,114 timestamp: getTimeFunction(opts),115 messageKey: opts.messageKey || 'msg',116 onChild: opts.onChild || noop117 }118 logger.levels = getLevels(opts)119 logger.level = level120 121 logger.setMaxListeners = logger.getMaxListeners =122 logger.emit = logger.addListener = logger.on =123 logger.prependListener = logger.once =124 logger.prependOnceListener = logger.removeListener =125 logger.removeAllListeners = logger.listeners =126 logger.listenerCount = logger.eventNames =127 logger.write = logger.flush = noop128 logger.serializers = serializers129 logger._serialize = serialize130 logger._stdErrSerialize = stdErrSerialize131 logger.child = function (...args) { return child.call(this, setOpts, ...args) }132 133 if (transmit) logger._logEvent = createLogEventShape()134 135 function getLevelVal () {136 return levelToValue(this.level, this)137 }138 139 function getLevel () {140 return this._level141 }142 function setLevel (level) {143 if (level !== 'silent' && !this.levels.values[level]) {144 throw Error('unknown level ' + level)145 }146 this._level = level147 148 set(this, setOpts, logger, 'error') // <-- must stay first149 set(this, setOpts, logger, 'fatal')150 set(this, setOpts, logger, 'warn')151 set(this, setOpts, logger, 'info')152 set(this, setOpts, logger, 'debug')153 set(this, setOpts, logger, 'trace')154 155 customLevels.forEach((level) => {156 set(this, setOpts, logger, level)157 })158 }159 160 function child (setOpts, bindings, childOptions) {161 if (!bindings) {162 throw new Error('missing bindings for child Pino')163 }164 childOptions = childOptions || {}165 if (serialize && bindings.serializers) {166 childOptions.serializers = bindings.serializers167 }168 const childOptionsSerializers = childOptions.serializers169 if (serialize && childOptionsSerializers) {170 var childSerializers = Object.assign({}, serializers, childOptionsSerializers)171 var childSerialize = opts.browser.serialize === true172 ? Object.keys(childSerializers)173 : serialize174 delete bindings.serializers175 applySerializers([bindings], childSerialize, childSerializers, this._stdErrSerialize)176 }177 function Child (parent) {178 this._childLevel = (parent._childLevel | 0) + 1179 180 // make sure bindings are available in the `set` function181 this.bindings = bindings182 183 if (childSerializers) {184 this.serializers = childSerializers185 this._serialize = childSerialize186 }187 if (transmit) {188 this._logEvent = createLogEventShape(189 [].concat(parent._logEvent.bindings, bindings)190 )191 }192 }193 Child.prototype = this194 const newLogger = new Child(this)195 196 // must happen before the level is assigned197 appendChildLogger(this, newLogger)198 newLogger.child = function (...args) { return child.call(this, setOpts, ...args) }199 // required to actually initialize the logger functions for any given child200 newLogger.level = childOptions.level || this.level // allow level to be set by childOptions201 setOpts.onChild(newLogger)202 203 return newLogger204 }205 return logger206}207 208function getLevels (opts) {209 const customLevels = opts.customLevels || {}210 211 const values = Object.assign({}, pino.levels.values, customLevels)212 const labels = Object.assign({}, pino.levels.labels, invertObject(customLevels))213 214 return {215 values,216 labels217 }218}219 220function invertObject (obj) {221 const inverted = {}222 Object.keys(obj).forEach(function (key) {223 inverted[obj[key]] = key224 })225 return inverted226}227 228pino.levels = {229 values: {230 fatal: 60,231 error: 50,232 warn: 40,233 info: 30,234 debug: 20,235 trace: 10236 },237 labels: {238 10: 'trace',239 20: 'debug',240 30: 'info',241 40: 'warn',242 50: 'error',243 60: 'fatal'244 }245}246 247pino.stdSerializers = stdSerializers248pino.stdTimeFunctions = Object.assign({}, { nullTime, epochTime, unixTime, isoTime })249 250function getBindingChain (logger) {251 const bindings = []252 if (logger.bindings) {253 bindings.push(logger.bindings)254 }255 256 // traverse up the tree to get all bindings257 let hierarchy = logger[hierarchySymbol]258 while (hierarchy.parent) {259 hierarchy = hierarchy.parent260 if (hierarchy.logger.bindings) {261 bindings.push(hierarchy.logger.bindings)262 }263 }264 265 return bindings.reverse()266}267 268function set (self, opts, rootLogger, level) {269 // override the current log functions with either `noop` or the base log function270 Object.defineProperty(self, level, {271 value: (levelToValue(self.level, rootLogger) > levelToValue(level, rootLogger)272 ? noop273 : rootLogger[baseLogFunctionSymbol][level]),274 writable: true,275 enumerable: true,276 configurable: true277 })278 279 if (self[level] === noop) {280 if (!opts.transmit) return281 282 const transmitLevel = opts.transmit.level || self.level283 const transmitValue = levelToValue(transmitLevel, rootLogger)284 const methodValue = levelToValue(level, rootLogger)285 if (methodValue < transmitValue) return286 }287 288 // make sure the log format is correct289 self[level] = createWrap(self, opts, rootLogger, level)290 291 // prepend bindings if it is not the root logger292 const bindings = getBindingChain(self)293 if (bindings.length === 0) {294 // early exit in case for rootLogger295 return296 }297 self[level] = prependBindingsInArguments(bindings, self[level])298}299 300function prependBindingsInArguments (bindings, logFunc) {301 return function () {302 return logFunc.apply(this, [...bindings, ...arguments])303 }304}305 306function createWrap (self, opts, rootLogger, level) {307 return (function (write) {308 return function LOG () {309 const ts = opts.timestamp()310 const args = new Array(arguments.length)311 const proto = (Object.getPrototypeOf && Object.getPrototypeOf(this) === _console) ? _console : this312 for (var i = 0; i < args.length; i++) args[i] = arguments[i]313 314 var argsIsSerialized = false315 if (opts.serialize) {316 applySerializers(args, this._serialize, this.serializers, this._stdErrSerialize)317 argsIsSerialized = true318 }319 if (opts.asObject || opts.formatters) {320 write.call(proto, asObject(this, level, args, ts, opts))321 } else write.apply(proto, args)322 323 if (opts.transmit) {324 const transmitLevel = opts.transmit.level || self._level325 const transmitValue = levelToValue(transmitLevel, rootLogger)326 const methodValue = levelToValue(level, rootLogger)327 if (methodValue < transmitValue) return328 transmit(this, {329 ts,330 methodLevel: level,331 methodValue,332 transmitLevel,333 transmitValue: rootLogger.levels.values[opts.transmit.level || self._level],334 send: opts.transmit.send,335 val: levelToValue(self._level, rootLogger)336 }, args, argsIsSerialized)337 }338 }339 })(self[baseLogFunctionSymbol][level])340}341 342function asObject (logger, level, args, ts, opts) {343 const {344 level: levelFormatter,345 log: logObjectFormatter = (obj) => obj346 } = opts.formatters || {}347 const argsCloned = args.slice()348 let msg = argsCloned[0]349 const logObject = {}350 if (ts) {351 logObject.time = ts352 }353 354 if (levelFormatter) {355 const formattedLevel = levelFormatter(level, logger.levels.values[level])356 Object.assign(logObject, formattedLevel)357 } else {358 logObject.level = logger.levels.values[level]359 }360 361 let lvl = (logger._childLevel | 0) + 1362 if (lvl < 1) lvl = 1363 // deliberate, catching objects, arrays364 if (msg !== null && typeof msg === 'object') {365 while (lvl-- && typeof argsCloned[0] === 'object') {366 Object.assign(logObject, argsCloned.shift())367 }368 msg = argsCloned.length ? format(argsCloned.shift(), argsCloned) : undefined369 } else if (typeof msg === 'string') msg = format(argsCloned.shift(), argsCloned)370 if (msg !== undefined) logObject[opts.messageKey] = msg371 372 const formattedLogObject = logObjectFormatter(logObject)373 return formattedLogObject374}375 376function applySerializers (args, serialize, serializers, stdErrSerialize) {377 for (const i in args) {378 if (stdErrSerialize && args[i] instanceof Error) {379 args[i] = pino.stdSerializers.err(args[i])380 } else if (typeof args[i] === 'object' && !Array.isArray(args[i]) && serialize) {381 for (const k in args[i]) {382 if (serialize.indexOf(k) > -1 && k in serializers) {383 args[i][k] = serializers[k](args[i][k])384 }385 }386 }387 }388}389 390function transmit (logger, opts, args, argsIsSerialized = false) {391 const send = opts.send392 const ts = opts.ts393 const methodLevel = opts.methodLevel394 const methodValue = opts.methodValue395 const val = opts.val396 const bindings = logger._logEvent.bindings397 398 if (!argsIsSerialized) {399 applySerializers(400 args,401 logger._serialize || Object.keys(logger.serializers),402 logger.serializers,403 logger._stdErrSerialize === undefined ? true : logger._stdErrSerialize404 )405 }406 407 logger._logEvent.ts = ts408 logger._logEvent.messages = args.filter(function (arg) {409 // bindings can only be objects, so reference equality check via indexOf is fine410 return bindings.indexOf(arg) === -1411 })412 413 logger._logEvent.level.label = methodLevel414 logger._logEvent.level.value = methodValue415 416 send(methodLevel, logger._logEvent, val)417 418 logger._logEvent = createLogEventShape(bindings)419}420 421function createLogEventShape (bindings) {422 return {423 ts: 0,424 messages: [],425 bindings: bindings || [],426 level: { label: '', value: 0 }427 }428}429 430function asErrValue (err) {431 const obj = {432 type: err.constructor.name,433 msg: err.message,434 stack: err.stack435 }436 for (const key in err) {437 if (obj[key] === undefined) {438 obj[key] = err[key]439 }440 }441 return obj442}443 444function getTimeFunction (opts) {445 if (typeof opts.timestamp === 'function') {446 return opts.timestamp447 }448 if (opts.timestamp === false) {449 return nullTime450 }451 return epochTime452}453 454function mock () { return {} }455function passthrough (a) { return a }456function noop () {}457 458function nullTime () { return false }459function epochTime () { return Date.now() }460function unixTime () { return Math.round(Date.now() / 1000.0) }461function isoTime () { return new Date(Date.now()).toISOString() } // using Date.now() for testability462 463/* eslint-disable */464/* istanbul ignore next */465function pfGlobalThisOrFallback () {466 function defd (o) { return typeof o !== 'undefined' && o }467 try {468 if (typeof globalThis !== 'undefined') return globalThis469 Object.defineProperty(Object.prototype, 'globalThis', {470 get: function () {471 delete Object.prototype.globalThis472 return (this.globalThis = this)473 },474 configurable: true475 })476 return globalThis477 } catch (e) {478 return defd(self) || defd(window) || defd(this) || {}479 }480}481/* eslint-enable */482 483module.exports.default = pino484module.exports.pino = pino485 