Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: pass logger/child logger as param to mixin #1709

Merged
merged 1 commit into from
May 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/proto.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
56 changes: 56 additions & 0 deletions test/mixin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
})