You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
234 lines
6.6 KiB
234 lines
6.6 KiB
3 years ago
|
'use strict'
|
||
|
|
||
|
/* eslint no-prototype-builtins: 0 */
|
||
|
|
||
|
const { EventEmitter } = require('events')
|
||
|
const SonicBoom = require('sonic-boom')
|
||
|
const flatstr = require('flatstr')
|
||
|
const warning = require('./deprecations')
|
||
|
const {
|
||
|
lsCacheSym,
|
||
|
levelValSym,
|
||
|
setLevelSym,
|
||
|
getLevelSym,
|
||
|
chindingsSym,
|
||
|
parsedChindingsSym,
|
||
|
mixinSym,
|
||
|
asJsonSym,
|
||
|
writeSym,
|
||
|
mixinMergeStrategySym,
|
||
|
timeSym,
|
||
|
timeSliceIndexSym,
|
||
|
streamSym,
|
||
|
serializersSym,
|
||
|
formattersSym,
|
||
|
useOnlyCustomLevelsSym,
|
||
|
needsMetadataGsym,
|
||
|
redactFmtSym,
|
||
|
stringifySym,
|
||
|
formatOptsSym,
|
||
|
stringifiersSym
|
||
|
} = require('./symbols')
|
||
|
const {
|
||
|
getLevel,
|
||
|
setLevel,
|
||
|
isLevelEnabled,
|
||
|
mappings,
|
||
|
initialLsCache,
|
||
|
genLsCache,
|
||
|
assertNoLevelCollisions
|
||
|
} = require('./levels')
|
||
|
const {
|
||
|
asChindings,
|
||
|
asJson,
|
||
|
buildFormatters,
|
||
|
stringify
|
||
|
} = require('./tools')
|
||
|
const {
|
||
|
version
|
||
|
} = require('./meta')
|
||
|
const redaction = require('./redaction')
|
||
|
|
||
|
// note: use of class is satirical
|
||
|
// https://github.com/pinojs/pino/pull/433#pullrequestreview-127703127
|
||
|
const constructor = class Pino {}
|
||
|
const prototype = {
|
||
|
constructor,
|
||
|
child,
|
||
|
bindings,
|
||
|
setBindings,
|
||
|
flush,
|
||
|
isLevelEnabled,
|
||
|
version,
|
||
|
get level () { return this[getLevelSym]() },
|
||
|
set level (lvl) { this[setLevelSym](lvl) },
|
||
|
get levelVal () { return this[levelValSym] },
|
||
|
set levelVal (n) { throw Error('levelVal is read-only') },
|
||
|
[lsCacheSym]: initialLsCache,
|
||
|
[writeSym]: write,
|
||
|
[asJsonSym]: asJson,
|
||
|
[getLevelSym]: getLevel,
|
||
|
[setLevelSym]: setLevel
|
||
|
}
|
||
|
|
||
|
Object.setPrototypeOf(prototype, EventEmitter.prototype)
|
||
|
|
||
|
// exporting and consuming the prototype object using factory pattern fixes scoping issues with getters when serializing
|
||
|
module.exports = function () {
|
||
|
return Object.create(prototype)
|
||
|
}
|
||
|
|
||
|
const resetChildingsFormatter = bindings => bindings
|
||
|
function child (bindings, options) {
|
||
|
if (!bindings) {
|
||
|
throw Error('missing bindings for child Pino')
|
||
|
}
|
||
|
options = options || {} // default options to empty object
|
||
|
const serializers = this[serializersSym]
|
||
|
const formatters = this[formattersSym]
|
||
|
const instance = Object.create(this)
|
||
|
|
||
|
if (bindings.hasOwnProperty('serializers') === true) {
|
||
|
warning.emit('PINODEP004')
|
||
|
options.serializers = bindings.serializers
|
||
|
}
|
||
|
if (bindings.hasOwnProperty('formatters') === true) {
|
||
|
warning.emit('PINODEP005')
|
||
|
options.formatters = bindings.formatters
|
||
|
}
|
||
|
if (bindings.hasOwnProperty('customLevels') === true) {
|
||
|
warning.emit('PINODEP006')
|
||
|
options.customLevels = bindings.customLevels
|
||
|
}
|
||
|
if (bindings.hasOwnProperty('level') === true) {
|
||
|
warning.emit('PINODEP007')
|
||
|
options.level = bindings.level
|
||
|
}
|
||
|
if (options.hasOwnProperty('serializers') === true) {
|
||
|
instance[serializersSym] = Object.create(null)
|
||
|
|
||
|
for (const k in serializers) {
|
||
|
instance[serializersSym][k] = serializers[k]
|
||
|
}
|
||
|
const parentSymbols = Object.getOwnPropertySymbols(serializers)
|
||
|
/* eslint no-var: off */
|
||
|
for (var i = 0; i < parentSymbols.length; i++) {
|
||
|
const ks = parentSymbols[i]
|
||
|
instance[serializersSym][ks] = serializers[ks]
|
||
|
}
|
||
|
|
||
|
for (const bk in options.serializers) {
|
||
|
instance[serializersSym][bk] = options.serializers[bk]
|
||
|
}
|
||
|
const bindingsSymbols = Object.getOwnPropertySymbols(options.serializers)
|
||
|
for (var bi = 0; bi < bindingsSymbols.length; bi++) {
|
||
|
const bks = bindingsSymbols[bi]
|
||
|
instance[serializersSym][bks] = options.serializers[bks]
|
||
|
}
|
||
|
} else instance[serializersSym] = serializers
|
||
|
if (options.hasOwnProperty('formatters')) {
|
||
|
const { level, bindings: chindings, log } = options.formatters
|
||
|
instance[formattersSym] = buildFormatters(
|
||
|
level || formatters.level,
|
||
|
chindings || resetChildingsFormatter,
|
||
|
log || formatters.log
|
||
|
)
|
||
|
} else {
|
||
|
instance[formattersSym] = buildFormatters(
|
||
|
formatters.level,
|
||
|
resetChildingsFormatter,
|
||
|
formatters.log
|
||
|
)
|
||
|
}
|
||
|
if (options.hasOwnProperty('customLevels') === true) {
|
||
|
assertNoLevelCollisions(this.levels, options.customLevels)
|
||
|
instance.levels = mappings(options.customLevels, instance[useOnlyCustomLevelsSym])
|
||
|
genLsCache(instance)
|
||
|
}
|
||
|
|
||
|
// redact must place before asChindings and only replace if exist
|
||
|
if ((typeof options.redact === 'object' && options.redact !== null) || Array.isArray(options.redact)) {
|
||
|
instance.redact = options.redact // replace redact directly
|
||
|
const stringifiers = redaction(instance.redact, stringify)
|
||
|
const formatOpts = { stringify: stringifiers[redactFmtSym] }
|
||
|
instance[stringifySym] = stringify
|
||
|
instance[stringifiersSym] = stringifiers
|
||
|
instance[formatOptsSym] = formatOpts
|
||
|
}
|
||
|
|
||
|
instance[chindingsSym] = asChindings(instance, bindings)
|
||
|
const childLevel = options.level || this.level
|
||
|
instance[setLevelSym](childLevel)
|
||
|
|
||
|
return instance
|
||
|
}
|
||
|
|
||
|
function bindings () {
|
||
|
const chindings = this[chindingsSym]
|
||
|
const chindingsJson = `{${chindings.substr(1)}}` // at least contains ,"pid":7068,"hostname":"myMac"
|
||
|
const bindingsFromJson = JSON.parse(chindingsJson)
|
||
|
delete bindingsFromJson.pid
|
||
|
delete bindingsFromJson.hostname
|
||
|
return bindingsFromJson
|
||
|
}
|
||
|
|
||
|
function setBindings (newBindings) {
|
||
|
const chindings = asChindings(this, newBindings)
|
||
|
this[chindingsSym] = chindings
|
||
|
delete this[parsedChindingsSym]
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Default strategy for creating `mergeObject` from arguments and the result from `mixin()`.
|
||
|
* Fields from `mergeObject` have higher priority in this strategy.
|
||
|
*
|
||
|
* @param {Object} mergeObject The object a user has supplied to the logging function.
|
||
|
* @param {Object} mixinObject The result of the `mixin` method.
|
||
|
* @return {Object}
|
||
|
*/
|
||
|
function defaultMixinMergeStrategy (mergeObject, mixinObject) {
|
||
|
return Object.assign(mixinObject, mergeObject)
|
||
|
}
|
||
|
|
||
|
function write (_obj, msg, num) {
|
||
|
const t = this[timeSym]()
|
||
|
const mixin = this[mixinSym]
|
||
|
const mixinMergeStrategy = this[mixinMergeStrategySym] || defaultMixinMergeStrategy
|
||
|
const objError = _obj instanceof Error
|
||
|
let obj
|
||
|
|
||
|
if (_obj === undefined || _obj === null) {
|
||
|
obj = mixin ? mixin({}) : {}
|
||
|
} else {
|
||
|
obj = mixinMergeStrategy(_obj, mixin ? mixin(_obj) : {})
|
||
|
if (!msg && objError) {
|
||
|
msg = _obj.message
|
||
|
}
|
||
|
|
||
|
if (objError) {
|
||
|
obj.stack = _obj.stack
|
||
|
if (!obj.type) {
|
||
|
obj.type = 'Error'
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const s = this[asJsonSym](obj, msg, num, t)
|
||
|
|
||
|
const stream = this[streamSym]
|
||
|
if (stream[needsMetadataGsym] === true) {
|
||
|
stream.lastLevel = num
|
||
|
stream.lastObj = obj
|
||
|
stream.lastMsg = msg
|
||
|
stream.lastTime = t.slice(this[timeSliceIndexSym])
|
||
|
stream.lastLogger = this // for child loggers
|
||
|
}
|
||
|
if (stream instanceof SonicBoom) stream.write(s)
|
||
|
else stream.write(flatstr(s))
|
||
|
}
|
||
|
|
||
|
function flush () {
|
||
|
const stream = this[streamSym]
|
||
|
if ('flush' in stream) stream.flush()
|
||
|
}
|