Repository: pinojs/pino-http
Pull request #88 (2020-03-31 13:29:34)

Option to override the log object key naming with custom values
Adds a configuration option to rename the keys in the JSON log. Keys which can be renamed are `req`, `res`, `err`, and `responseTime`.

CalumDickson: Addresses issue #87 

Patch:
diff --git a/.gitignore b/.gitignore
index 0182bac..7829623 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,3 +40,6 @@ profile*
 .nyc_output
 
 .DS_Store
+
+# lockfile
+package-lock.json
diff --git a/README.md b/README.md
index 4c618a8..0e83a6a 100644
--- a/README.md
+++ b/README.md
@@ -53,7 +53,7 @@ server.listen(3000)
 ```
 
 ```
-$ node example.js | pino
+$ node example.js | pino-pretty
 [2016-03-31T16:53:21.079Z] INFO (46316 on MBP-di-Matteo): something else
     req: {
       "id": 1,
@@ -103,6 +103,7 @@ $ node example.js | pino
 * `stream`: same as the second parameter
 * `customSuccessMessage`: set to a `function (res) => { /* returns message string */ }` This function will be invoked at each successful response, setting "msg" property to returned string. If not set, default value will be used.
 * `customErrorMessage`: set to a `function (res, err) => { /* returns message  string */ }` This function will be invoked at each failed response, setting "msg" property to returned string. If not set, default value will be used.
+* `customAttributeKeys`: allows the log object attributes added by `pino-http` to be given custom keys. Accepts an object of format `{ [original]: [override] }`. Attributes available for override are `req`, `res`, `err`, and `responseTime`. 
 
 `stream`: the destination stream. Could be passed in as an option too.
 
@@ -133,7 +134,6 @@ var logger = require('pino-http')({
   // Logger level is `info` by default
   useLevel: 'info',
 
-
   // Define a custom logger level
   customLogLevel: function (res, err) {
     if (res.statusCode >= 400 && res.statusCode < 500) {
@@ -155,6 +155,14 @@ var logger = require('pino-http')({
   // Define a custom error message
   customErrorMessage: function (error, res) {
     return 'request errored with status code: ' + res.statusCode
+  },
+
+  // Override attribute keys for the log object
+  customAttributeKeys: {
+    req: 'request',
+    res: 'response',
+    err: 'error',
+    responseTime: 'timeTaken'
   }
 })
 
@@ -265,6 +273,8 @@ var logger = require('pino-http')({
 })
 ```
 
+##### Logging request body
+
 Logging of requests' bodies is disabled by default since it can cause security risks such as having private user information (password, other GDPR-protected data, etc.) logged (and persisted in most setups). However if enabled, sensitive information can be redacted as per [redaction documentation](http://getpino.io/#/docs/redaction).
 
 Furthermore, logging more bytes does slow down throughput. [This video by pino maintainers Matteo Collina & David Mark Clements](https://www.youtube.com/watch?v=zja-_IYNrFc&feature=youtu.be) goes into this in more detail.
@@ -283,6 +293,12 @@ const logger = require('pino-http')({
 });
 ```
 
+##### Custom serializers + custom log attribute keys
+
+If custom attribute keys for `req`, `res`, or `err` log keys have been provided, serializers will be applied with the following order of precedence:
+
+`serializer matching custom key` > `serializer matching default key` > `default pino serializer`
+
 ## Team
 
 ### Matteo Collina
diff --git a/logger.js b/logger.js
index cd349e6..ed87432 100644
--- a/logger.js
+++ b/logger.js
@@ -12,10 +12,21 @@ function pinoLogger (opts, stream) {
   }
 
   opts = opts || {}
+
+  opts.customAttributeKeys = opts.customAttributeKeys || {}
+  var reqKey = opts.customAttributeKeys.req || 'req'
+  var resKey = opts.customAttributeKeys.res || 'res'
+  var errKey = opts.customAttributeKeys.err || 'err'
+  var responseTimeKey = opts.customAttributeKeys.responseTime || 'responseTime'
+  delete opts.customAttributeKeys
+
   opts.serializers = opts.serializers || {}
-  opts.serializers.req = serializers.wrapRequestSerializer(opts.serializers.req || serializers.req)
-  opts.serializers.res = serializers.wrapResponseSerializer(opts.serializers.res || serializers.res)
-  opts.serializers.err = serializers.wrapErrorSerializer(opts.serializers.err || serializers.err)
+  var requestSerializer = opts.serializers[reqKey] || opts.serializers.req || serializers.req
+  var responseSerializer = opts.serializers[resKey] || opts.serializers.res || serializers.res
+  var errorSerializer = opts.serializers[errKey] || opts.serializers.err || serializers.err
+  opts.serializers[reqKey] = serializers.wrapRequestSerializer(requestSerializer)
+  opts.serializers[resKey] = serializers.wrapResponseSerializer(responseSerializer)
+  opts.serializers[errKey] = serializers.wrapErrorSerializer(errorSerializer)
 
   if (opts.useLevel && opts.customLogLevel) {
     throw new Error("You can't pass 'useLevel' and 'customLogLevel' together")
@@ -56,16 +67,16 @@ function pinoLogger (opts, stream) {
       var error = err || this.err || new Error('failed with status code ' + this.statusCode)
 
       log[level]({
-        res: this,
-        err: error,
-        responseTime: responseTime
+        [resKey]: this,
+        [errKey]: error,
+        [responseTimeKey]: responseTime
       }, errorMessage(error, this))
       return
     }
 
     log[level]({
-      res: this,
-      responseTime: responseTime
+      [resKey]: this,
+      [responseTimeKey]: responseTime
     }, successMessage(this))
   }
 
@@ -73,7 +84,7 @@ function pinoLogger (opts, stream) {
     var shouldLogSuccess = true
 
     req.id = genReqId(req)
-    req.log = res.log = logger.child({req: req})
+    req.log = res.log = logger.child({[reqKey]: req})
     res[startTime] = res[startTime] || Date.now()
 
     if (autoLogging) {


Test patch:
diff --git a/test.js b/test.js
index d939805..76d55a9 100644
--- a/test.js
+++ b/test.js
@@ -638,3 +638,52 @@ test('uses the custom errorMessage callback if passed in as an option', function
     t.end()
   })
 })
+
+test('uses custom log object attribute keys when provided, successful request', function (t) {
+  var dest = split(JSON.parse)
+  var logger = pinoHttp({
+    customAttributeKeys: {
+      req: 'httpReq',
+      res: 'httpRes',
+      err: 'httpErr',
+      responseTime: 'timeTaken'
+    }
+  }, dest)
+
+  setup(t, logger, function (err, server) {
+    t.error(err)
+    doGet(server)
+  })
+
+  dest.on('data', function (line) {
+    t.ok(line.httpReq, 'httpReq is defined')
+    t.ok(line.httpRes, 'httpRes is defined')
+    t.equal(typeof line.timeTaken, 'number')
+    t.end()
+  })
+})
+
+test('uses custom log object attribute keys when provided, error request', function (t) {
+  var dest = split(JSON.parse)
+  var logger = pinoHttp({
+    customAttributeKeys: {
+      req: 'httpReq',
+      res: 'httpRes',
+      err: 'httpErr',
+      responseTime: 'timeTaken'
+    }
+  }, dest)
+
+  setup(t, logger, function (err, server) {
+    t.error(err)
+    doGet(server, ERROR_URL)
+  })
+
+  dest.on('data', function (line) {
+    t.ok(line.httpReq, 'httpReq is defined')
+    t.ok(line.httpRes, 'httpRes is defined')
+    t.ok(line.httpErr, 'httpRes is defined')
+    t.equal(typeof line.timeTaken, 'number')
+    t.end()
+  })
+})