Repository: pinojs/pino
Pull request #772 (2020-02-13 15:23:05)

Deprecate the `changeLevelName` option and alias it to `levelKey`
There was some discussion in [pino-pretty](https://github.com/pinojs/pino-pretty/pull/92) about this.

I'm not sure what sort of approach is normally used when deprecating something, but I thought I'd open a PR just to get the ball rolling.

davidmarkclements: @jsumners deprecation warnings from other modules won't use the logger, however instead of `console.warn` it should be `process.emitWarning` 

As an off-thread aside, maybe a module (or even a pino option) for wrapping emitWarning into logger.warn would be good.
wraithy: So in version 7, this aliasing code will need to be removed. Do you guys have a way of tracking that in Github? Should I leave a TODO in the code?
jsumners: I created a project -- https://github.com/pinojs/pino/projects/2

Patch:
diff --git a/docs/api.md b/docs/api.md
index 7757f0c..bbd57dd 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -254,14 +254,18 @@ Enables printing of level labels instead of level values in the printed logs.
 Warning: this option may not be supported by downstream transports.
 
 <a id="changeLevelName"></a>
-#### `changeLevelName` (String)
+#### `changeLevelName` (String) - DEPRECATED
+Use `levelKey` instead. This will be removed in v7.
+
+<a id="levelKey"></a>
+#### `levelKey` (String)
 
 Default: `'level'`
 
 Changes the property `level` to any string value you pass in:
 ```js
 const logger = pino({
-  changeLevelName: 'priority'
+  levelKey: 'priority'
 })
 logger.info('hello world')
 // {"priority":30,"time":1531257112193,"msg":"hello world","pid":55956,"hostname":"x","v":1}
diff --git a/lib/levels.js b/lib/levels.js
index c4ebbdc..0f60f7b 100644
--- a/lib/levels.js
+++ b/lib/levels.js
@@ -4,7 +4,7 @@ const {
   lsCacheSym,
   levelValSym,
   useLevelLabelsSym,
-  changeLevelNameSym,
+  levelKeySym,
   useOnlyCustomLevelsSym,
   streamSym
 } = require('./symbols')
@@ -49,7 +49,7 @@ const initialLsCache = Object.keys(nums).reduce((o, k) => {
 }, {})
 
 function genLsCache (instance) {
-  const levelName = instance[changeLevelNameSym]
+  const levelName = instance[levelKeySym]
   instance[lsCacheSym] = Object.keys(instance.levels.labels).reduce((o, k) => {
     o[k] = instance[useLevelLabelsSym]
       ? `{"${levelName}":"${instance.levels.labels[k]}"`
diff --git a/lib/symbols.js b/lib/symbols.js
index 9e27d7c..13d3662 100644
--- a/lib/symbols.js
+++ b/lib/symbols.js
@@ -4,7 +4,7 @@ const setLevelSym = Symbol('pino.setLevel')
 const getLevelSym = Symbol('pino.getLevel')
 const levelValSym = Symbol('pino.levelVal')
 const useLevelLabelsSym = Symbol('pino.useLevelLabels')
-const changeLevelNameSym = Symbol('pino.changeLevelName')
+const levelKeySym = Symbol('pino.levelKey')
 const useOnlyCustomLevelsSym = Symbol('pino.useOnlyCustomLevels')
 const mixinSym = Symbol('pino.mixin')
 
@@ -57,7 +57,7 @@ module.exports = {
   messageKeySym,
   nestedKeySym,
   wildcardFirstSym,
-  changeLevelNameSym,
+  levelKeySym,
   wildcardGsym,
   needsMetadataGsym,
   useOnlyCustomLevelsSym
diff --git a/lib/tools.js b/lib/tools.js
index 02127ff..5776d5f 100644
--- a/lib/tools.js
+++ b/lib/tools.js
@@ -178,6 +178,7 @@ function prettifierMetaWrapper (pretty, dest) {
         return
       }
       warned = true
+      setMetadataProps(dest, this)
       dest.write(pretty(Object.assign({
         level: 40, // warn
         msg: 'pino.final with prettyPrint does not support flushing',
@@ -237,6 +238,8 @@ function prettifierMetaWrapper (pretty, dest) {
 
       const formatted = pretty(typeof redact === 'function' ? redact(obj) : obj)
       if (formatted === undefined) return
+
+      setMetadataProps(dest, this)
       dest.write(formatted)
     }
   }
@@ -287,6 +290,14 @@ function createArgsNormalizer (defaultOptions) {
     if ('onTerminated' in opts) {
       throw Error('The onTerminated option has been removed, use pino.final instead')
     }
+    if ('changeLevelName' in opts) {
+      process.emitWarning(
+        'The changeLevelName option is deprecated and will be removed in v7. Use levelKey instead.',
+        { code: 'changeLevelName_deprecation' }
+      )
+      opts.levelKey = opts.changeLevelName
+      delete opts.changeLevelName
+    }
     const { enabled, prettyPrint, prettifier, messageKey } = opts
     if (enabled === false) opts.level = 'silent'
     stream = stream || process.stdout
@@ -351,6 +362,16 @@ function stringify (obj) {
   }
 }
 
+function setMetadataProps (dest, that) {
+  if (dest[needsMetadataGsym] === true) {
+    dest.lastLevel = that.lastLevel
+    dest.lastMsg = that.lastMsg
+    dest.lastObj = that.lastObj
+    dest.lastTime = that.lastTime
+    dest.lastLogger = that.lastLogger
+  }
+}
+
 module.exports = {
   noop,
   buildSafeSonicBoom,
diff --git a/pino.js b/pino.js
index 70c94bd..3fec255 100644
--- a/pino.js
+++ b/pino.js
@@ -29,7 +29,7 @@ const {
   messageKeySym,
   nestedKeySym,
   useLevelLabelsSym,
-  changeLevelNameSym,
+  levelKeySym,
   mixinSym,
   useOnlyCustomLevelsSym
 } = symbols
@@ -52,7 +52,7 @@ const defaultOptions = {
   name: undefined,
   redact: null,
   customLevels: null,
-  changeLevelName: 'level',
+  levelKey: 'level',
   useOnlyCustomLevels: false
 }
 
@@ -74,7 +74,7 @@ function pino (...args) {
     level,
     customLevels,
     useLevelLabels,
-    changeLevelName,
+    levelKey,
     mixin,
     useOnlyCustomLevels
   } = opts
@@ -105,7 +105,7 @@ function pino (...args) {
   const instance = {
     levels,
     [useLevelLabelsSym]: useLevelLabels,
-    [changeLevelNameSym]: changeLevelName,
+    [levelKeySym]: levelKey,
     [useOnlyCustomLevelsSym]: useOnlyCustomLevels,
     [streamSym]: stream,
     [timeSym]: time,
@@ -122,7 +122,7 @@ function pino (...args) {
   }
   Object.setPrototypeOf(instance, proto)
 
-  if (customLevels || useLevelLabels || changeLevelName !== defaultOptions.changeLevelName) genLsCache(instance)
+  if (customLevels || useLevelLabels || levelKey !== defaultOptions.levelKey) genLsCache(instance)
 
   instance[setLevelSym](level)
 


Test patch:
diff --git a/test/custom-levels.test.js b/test/custom-levels.test.js
index 283504d..53f6d29 100644
--- a/test/custom-levels.test.js
+++ b/test/custom-levels.test.js
@@ -251,14 +251,14 @@ test('does not share custom level state across siblings', async ({ doesNotThrow
   })
 })
 
-test('custom level does not affect changeLevelName', async ({ is }) => {
+test('custom level does not affect levelKey', async ({ is }) => {
   const stream = sink()
   const logger = pino({
     customLevels: {
       foo: 35,
       bar: 45
     },
-    changeLevelName: 'priority'
+    levelKey: 'priority'
   }, stream)
 
   logger.foo('test')
diff --git a/test/levels.test.js b/test/levels.test.js
index b7ac1cc..db5f321 100644
--- a/test/levels.test.js
+++ b/test/levels.test.js
@@ -257,12 +257,21 @@ test('resets levels from labels to numbers', async ({ is }) => {
   instance.info('hello world')
 })
 
+test('aliases changeLevelName to levelKey', async ({ is }) => {
+  const instance = pino({ changeLevelName: 'priority' }, sink((result, enc, cb) => {
+    is(result.priority, 30)
+    cb()
+  }))
+
+  instance.info('hello world')
+})
+
 test('changes label naming when told to', async ({ is }) => {
   const expected = [{
     priority: 30,
     msg: 'hello world'
   }]
-  const instance = pino({ changeLevelName: 'priority' }, sink((result, enc, cb) => {
+  const instance = pino({ levelKey: 'priority' }, sink((result, enc, cb) => {
     const current = expected.shift()
     is(result.priority, current.priority)
     is(result.msg, current.msg)
@@ -323,11 +332,11 @@ test('produces labels for custom levels', async ({ is }) => {
   instance.foo('foobar')
 })
 
-test('setting changeLevelName does not affect labels when told to', async ({ is }) => {
+test('setting levelKey does not affect labels when told to', async ({ is }) => {
   const instance = pino(
     {
       useLevelLabels: true,
-      changeLevelName: 'priority'
+      levelKey: 'priority'
     },
     sink((result, enc, cb) => {
       is(result.priority, 'info')
diff --git a/test/pretty.test.js b/test/pretty.test.js
index caf9f62..4017d0c 100644
--- a/test/pretty.test.js
+++ b/test/pretty.test.js
@@ -272,3 +272,41 @@ test('works as expected with an object with the msg prop', async ({ isNot }) =>
   await once(child, 'close')
   isNot(actual.match(/\(123456 on abcdefghijklmnopqr\): hello/), null)
 })
+
+test('should not lose stream metadata for streams with `needsMetadataGsym` flag', async ({ isNot }) => {
+  const dest = new Writable({
+    objectMode: true,
+    write () {
+      isNot(typeof this.lastLevel === 'undefined', true)
+      isNot(typeof this.lastMsg === 'undefined', true)
+      isNot(typeof this.lastObj === 'undefined', true)
+      isNot(typeof this.lastTime === 'undefined', true)
+      isNot(typeof this.lastLogger === 'undefined', true)
+    }
+  })
+
+  dest[pino.symbols.needsMetadataGsym] = true
+
+  const log = pino({
+    prettyPrint: true
+  }, dest)
+  log.info('foo')
+})
+
+test('should not add stream metadata for streams without `needsMetadataGsym` flag', async ({ is }) => {
+  const dest = new Writable({
+    objectMode: true,
+    write () {
+      is(typeof this.lastLevel === 'undefined', true)
+      is(typeof this.lastMsg === 'undefined', true)
+      is(typeof this.lastObj === 'undefined', true)
+      is(typeof this.lastTime === 'undefined', true)
+      is(typeof this.lastLogger === 'undefined', true)
+    }
+  })
+
+  const log = pino({
+    prettyPrint: true
+  }, dest)
+  log.info('foo')
+})