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() | ||
|  | } |