Repository: pinojs/pino (js)

Issue:
Custom label for level
At first glance it looks like https://github.com/pinojs/pino/blob/master/lib/levels.js#L29-L42 is where the labels are set.

Some of my destinations use the label `severity` instead of `level`. Is there any way at the moment to configure this? Is a PR to add this feature welcome?

Pull request description:
Add ability to change level name when creating the instance
Solution for issue: Custom label for level fix #495

Patch:
diff --git a/docs/api.md b/docs/api.md
index f15e39d..0eee870 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -179,6 +179,20 @@ Default: `false`
 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)
+
+Default: `'level'`
+
+Changes the property `level` to any string value you pass in:
+```js
+const logger = pino({
+  changeLevelName: 'priority'
+})
+logger.info('hello world')
+// {"priority":30,"time":1531257112193,"msg":"hello world","pid":55956,"hostname":"x","v":1}
+```
+
 #### `browser` (Object)
 
 Browser only, may have `asObject` and `write` keys. This option is separately
@@ -664,7 +678,7 @@ of each function execution to avoid losing data.
 
 ### `pino.final(logger, [handler]) => Function | FinalLogger`
 
-The `pino.final` method can be used to acquire a final logger instance 
+The `pino.final` method can be used to acquire a final logger instance
 or create an exit listener function.
 
 The `finalLogger` is a specialist logger that synchronously flushes
diff --git a/lib/levels.js b/lib/levels.js
index f98e01e..e8d7ca5 100644
--- a/lib/levels.js
+++ b/lib/levels.js
@@ -1,6 +1,6 @@
 'use strict'
 const flatstr = require('flatstr')
-const { lsCacheSym, levelValSym, useLevelLabelsSym } = require('./symbols')
+const { lsCacheSym, levelValSym, useLevelLabelsSym, changeLevelNameSym } = require('./symbols')
 const { noop, genLog } = require('./tools')
 
 const levels = {
@@ -32,10 +32,11 @@ const initialLsCache = Object.keys(nums).reduce((o, k) => {
 }, {})
 
 function genLsCache (instance) {
+  const levelName = instance[changeLevelNameSym]
   instance[lsCacheSym] = Object.keys(instance.levels.labels).reduce((o, k) => {
     o[k] = instance[useLevelLabelsSym]
-      ? `{"level":"${instance.levels.labels[k]}"`
-      : flatstr('{"level":' + Number(k))
+      ? `{"${levelName}":"${instance.levels.labels[k]}"`
+      : flatstr(`{"${levelName}":` + Number(k))
     return o
   }, instance[lsCacheSym])
   return instance
diff --git a/lib/symbols.js b/lib/symbols.js
index 4a1c883..f30dd2a 100644
--- a/lib/symbols.js
+++ b/lib/symbols.js
@@ -4,6 +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 lsCacheSym = Symbol('pino.lsCache')
 const chindingsSym = Symbol('pino.chindings')
@@ -46,6 +47,7 @@ module.exports = {
   endSym,
   formatOptsSym,
   messageKeyStringSym,
+  changeLevelNameSym,
   wildcardGsym,
   needsMetadataGsym
 }
diff --git a/pino.js b/pino.js
index ead709a..56f4045 100644
--- a/pino.js
+++ b/pino.js
@@ -26,7 +26,8 @@ const {
   endSym,
   formatOptsSym,
   messageKeyStringSym,
-  useLevelLabelsSym
+  useLevelLabelsSym,
+  changeLevelNameSym
 } = symbols
 const { epochTime, nullTime } = time
 const { pid } = process
@@ -43,7 +44,8 @@ const defaultOptions = {
   timestamp: epochTime,
   name: undefined,
   redact: null,
-  customLevels: null
+  customLevels: null,
+  changeLevelName: 'level'
 }
 
 const normalize = createArgsNormalizer(defaultOptions)
@@ -60,7 +62,8 @@ function pino (...args) {
     name,
     level,
     customLevels,
-    useLevelLabels
+    useLevelLabels,
+    changeLevelName
   } = opts
 
   assertNoLevelCollisions(pino.levels, customLevels)
@@ -87,6 +90,7 @@ function pino (...args) {
   const instance = {
     levels,
     [useLevelLabelsSym]: useLevelLabels,
+    [changeLevelNameSym]: changeLevelName,
     [streamSym]: stream,
     [timeSym]: time,
     [stringifySym]: stringify,
@@ -99,7 +103,7 @@ function pino (...args) {
   }
   Object.setPrototypeOf(instance, proto)
 
-  if (customLevels || useLevelLabels) genLsCache(instance)
+  if (customLevels || useLevelLabels || changeLevelName !== defaultOptions.changeLevelName) genLsCache(instance)
 
   instance[setLevelSym](level)
 


Test patch:
diff --git a/test/custom-levels.test.js b/test/custom-levels.test.js
index 1a41f59..c15f390 100644
--- a/test/custom-levels.test.js
+++ b/test/custom-levels.test.js
@@ -232,3 +232,18 @@ test('does not share custom level state across siblings', async ({doesNotThrow})
     })
   })
 })
+
+test('custom level does not affect changeLevelName', async ({is}) => {
+  const stream = sink()
+  const logger = pino({
+    customLevels: {
+      foo: 35,
+      bar: 45
+    },
+    changeLevelName: 'priority'
+  }, stream)
+
+  logger.foo('test')
+  const { priority } = await once(stream, 'data')
+  is(priority, 35)
+})
diff --git a/test/levels.test.js b/test/levels.test.js
index b30d701..59cd215 100644
--- a/test/levels.test.js
+++ b/test/levels.test.js
@@ -242,6 +242,21 @@ test('produces labels when told to', async ({is}) => {
   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 current = expected.shift()
+    is(result.priority, current.priority)
+    is(result.msg, current.msg)
+    cb()
+  }))
+
+  instance.info('hello world')
+})
+
 test('children produce labels when told to', async ({is}) => {
   const expected = [
     {
@@ -292,3 +307,18 @@ test('produces labels for custom levels', async ({is}) => {
   instance.info('hello world')
   instance.foo('foobar')
 })
+
+test('setting changeLevelName does not affect labels when told to', async ({is}) => {
+  const instance = pino(
+    {
+      useLevelLabels: true,
+      changeLevelName: 'priority'
+    },
+    sink((result, enc, cb) => {
+      is(result.priority, 'info')
+      cb()
+    })
+  )
+
+  instance.info('hello world')
+})