diff --git a/docs/api.md b/docs/api.md index cfd1ca7bc..9f3abf10c 100644 --- a/docs/api.md +++ b/docs/api.md @@ -121,6 +121,8 @@ Default: `undefined` If provided, the `mixin` function is called each time one of the active logging methods is called. The first parameter is the value `mergeObject` or an empty object. The second parameter is the log level number. +The third parameter is the logger or child logger itself, which can be used to +retrieve logger-specific context from within the `mixin` function. The function must synchronously return an object. The properties of the returned object will be added to the logged JSON. @@ -177,7 +179,40 @@ logger.error('Message 2') ``` If the `mixin` feature is being used merely to add static metadata to each log message, -then a [child logger ⇗](/docs/child-loggers.md) should be used instead. +then a [child logger ⇗](/docs/child-loggers.md) should be used instead. Unless your application +needs to concatenate values for a specific key multiple times, in which case `mixin` can be +used to avoid the [duplicate keys caveat](/docs/child-loggers.md#duplicate-keys-caveat): + +```js +const logger = pino({ + mixin (obj, num, logger) { + return { + tags: logger.tags + } + } +}) +logger.tags = {} + +logger.addTag = function (key, value) { + logger.tags[key] = value +} + +function createChild (parent, ...context) { + const newChild = logger.child(...context) + newChild.tags = { ...logger.tags } + newChild.addTag = function (key, value) { + newChild.tags[key] = value + } + return newChild +} + +logger.addTag('foo', 1) +const child = createChild(logger, {}) +child.addTag('bar', 2) +logger.info('this will only have `foo: 1`') +child.info('this will have both `foo: 1` and `bar: 2`') +logger.info('this will still only have `foo: 1`') +``` As of pino 7.x, when the `mixin` is used with the [`nestedKey` option](#opt-nestedkey), the object returned from the `mixin` method will also be nested. Prior versions would mix diff --git a/lib/proto.js b/lib/proto.js index 400945282..4187b3b5b 100644 --- a/lib/proto.js +++ b/lib/proto.js @@ -194,7 +194,7 @@ function write (_obj, msg, num) { } if (mixin) { - obj = mixinMergeStrategy(obj, mixin(obj, num)) + obj = mixinMergeStrategy(obj, mixin(obj, num, this)) } const s = this[asJsonSym](obj, msg, num, t) diff --git a/test/mixin.test.js b/test/mixin.test.js index 86b1622f6..18f073650 100644 --- a/test/mixin.test.js +++ b/test/mixin.test.js @@ -160,3 +160,59 @@ test('mixin can use level number', async ({ ok, same }) => { stack: 'stack' }, 'test') }) + +test('mixin receives logger as third parameter', async ({ ok, same }) => { + const stream = sink() + const instance = pino({ + mixin (context, num, logger) { + ok(logger !== null, 'logger should be defined') + ok(logger !== undefined, 'logger should be defined') + same(logger, instance) + return { ...context, num } + } + }, stream) + instance.level = name + instance[name]({ + message: '123' + }, 'test') +}) + +test('mixin receives child logger', async ({ ok, same }) => { + const stream = sink() + let child = null + const instance = pino({ + mixin (context, num, logger) { + ok(logger !== null, 'logger should be defined') + ok(logger !== undefined, 'logger should be defined') + same(logger.expected, child.expected) + return { ...context, num } + } + }, stream) + instance.level = name + instance.expected = false + child = instance.child({}) + child.expected = true + child[name]({ + message: '123' + }, 'test') +}) + +test('mixin receives logger even if child exists', async ({ ok, same }) => { + const stream = sink() + let child = null + const instance = pino({ + mixin (context, num, logger) { + ok(logger !== null, 'logger should be defined') + ok(logger !== undefined, 'logger should be defined') + same(logger.expected, instance.expected) + return { ...context, num } + } + }, stream) + instance.level = name + instance.expected = false + child = instance.child({}) + child.expected = true + instance[name]({ + message: '123' + }, 'test') +})