Repository: pinojs/pino (js)

Issue:
logger.setBindings() doesn't clear logger[Symbol(pino.parsedChindings)]
#754 added a very useful `logger.setBindings()` method. But this doesn't play well with prettifiers, which appear to make use of a cached version of the bindings:

```js
> let child=logger.child({foo: 123});

// pino.parsedChindings is undefined at start
> child.bindings()
{ foo: 123 }
> child[Pino.symbols.parsedChindingsSym]
undefined

// when the first log call is made, pino.parsedChindings gets populated
> child.info('hello')
[2020-08-11 23:12:27.349 +0000] INFO : hello
    foo: 123
> child[Pino.symbols.parsedChindingsSym]
{ pid: 16561, hostname: 'nutrimatic', foo: 123 }

// logger.setBindings doesn't update pino.parsedChindings
> child.setBindings({bar: 456})
> child.bindings()
{ foo: 123, bar: 456 }
> child[Pino.symbols.parsedChindingsSym]
{ pid: 16561, hostname: 'nutrimatic', foo: 123 }

// so subsequent logs don't include the new bindings
> child.info('hello')
[2020-08-11 23:13:24.553 +0000] INFO : hello
    foo: 123
> child[Pino.symbols.parsedChindingsSym]
{ pid: 16561, hostname: 'nutrimatic', foo: 123 }

// manually clearing pino.parsedChindings causes it to be regenerated on the next log call
> delete child[Pino.symbols.parsedChindingsSym]
> child.info('hello')
[2020-08-11 23:14:28.240 +0000] INFO : hello
    foo: 123
    bar: 456
> child[Pino.symbols.parsedChindingsSym]
{ pid: 16561, hostname: 'nutrimatic', foo: 123, bar: 456 }
```

Would it be "safe" (performance-wise) to have `logger.setBindings()` automatically clear `pino.parserChindings` so that the next log call causes it to update? I think this may be OK since said updating only happens when a prettifier is enabled, so it won't impact production situations where there's no prettifier. Alternatively, could we add an additional method that manually clears `pino.parserChindings`?

I can submit a PR for either of these approaches; I'm just not sure which is best.

Pull request:
Clear cached parsed chindings when they're changed with .setBindings
This fixes #890; see that issue for discussion.

Patch:
diff --git a/lib/proto.js b/lib/proto.js
index e08ea4a..5d1bb08 100644
--- a/lib/proto.js
+++ b/lib/proto.js
@@ -11,6 +11,7 @@ const {
   setLevelSym,
   getLevelSym,
   chindingsSym,
+  parsedChindingsSym,
   mixinSym,
   asJsonSym,
   writeSym,
@@ -136,6 +137,7 @@ function bindings () {
 function setBindings (newBindings) {
   const chindings = asChindings(this, newBindings)
   this[chindingsSym] = chindings
+  delete this[parsedChindingsSym]
 }
 
 function write (_obj, msg, num) {


Test patch:
diff --git a/test/fixtures/pretty/child-with-updated-chindings.js b/test/fixtures/pretty/child-with-updated-chindings.js
new file mode 100644
index 0000000..96ae283
--- /dev/null
+++ b/test/fixtures/pretty/child-with-updated-chindings.js
@@ -0,0 +1,8 @@
+global.process = { __proto__: process, pid: 123456 }
+Date.now = function () { return 1459875739796 }
+require('os').hostname = function () { return 'abcdefghijklmnopqr' }
+var pino = require(require.resolve('./../../../'))
+var log = pino({ prettyPrint: true }).child({ foo: 123 })
+log.info('before')
+log.setBindings({ foo: 456, bar: 789 })
+log.info('after')
diff --git a/test/pretty.test.js b/test/pretty.test.js
index 6f7922d..6fd895c 100644
--- a/test/pretty.test.js
+++ b/test/pretty.test.js
@@ -112,6 +112,20 @@ test('parses and outputs chindings', async ({ is, isNot }) => {
   is(strip(actual).match(/a: 1/g).length, 3)
 })
 
+test('applies updated chindings', async ({ is, isNot }) => {
+  var actual = ''
+  const child = execa(process.argv[0], [join(__dirname, 'fixtures', 'pretty', 'child-with-updated-chindings.js')])
+
+  child.stdout.pipe(writer((s, enc, cb) => {
+    actual += s
+    cb()
+  }))
+  await once(child, 'close')
+  isNot(strip(actual).match(/foo: 123/), null)
+  isNot(strip(actual).match(/foo: 456/), null)
+  isNot(strip(actual).match(/bar: 789/), null)
+})
+
 test('applies formatters', async ({ is, isNot }) => {
   var actual = ''
   const child = execa(process.argv[0], [join(__dirname, 'fixtures', 'pretty', 'formatters.js')])