Repository: pinojs/pino-http
Pull request #56 (2018-11-04 11:21:17)

Add getUseLevel option
Adding a getUseLevel function that dynamically allows you to set the log level based on the status of the result and the error object (if any)

The change makes it possible to separate http errors from system errors.
For example, you can specify a user who types in an incorrect password will get an error with an HTTP 401 status, but the log level will be a warning rather than an error.


Patch:
diff --git a/README.md b/README.md
index b992ad1..89e5508 100644
--- a/README.md
+++ b/README.md
@@ -96,6 +96,8 @@ $ node example.js | pino
 * `logger`: `pino-http` can reuse a pino instance if passed with the `logger` property
 * `genReqId`: you can pass a function which gets used to generate a request id. The first argument is the request itself. As fallback `pino-http` is just using an integer. This default might not be the desired behavior if you're running multiple instances of the app
 * `useLevel`: the logger level `pino-http` is using to log out the response. default: `info`
+* `customLogLevel`: set to a `function (res, err) => { /* returns level name string */ }`. This function will be invoked to determine the level at which the log should be issued. This option is mutually exclusive with the `useLevel` option. The first argument is the HTTP response. The second argument is an error object if an error has occurred in the request.
+
 * `stream`: same as the second parameter
 
 `stream`: the destination stream. Could be passed in as an option too.
@@ -124,7 +126,18 @@ var logger = require('pino-http')({
   },
 
   // Logger level is `info` by default
-  useLevel: 'info'
+  useLevel: 'info',
+
+
+  // Define a custom logger level
+  customLogLevel: function (res, err) {
+    if (res.statusCode >= 400 && res.statusCode < 500) {
+      return 'warn'
+    } else if (res.statusCode >= 500 || err) {
+      return 'error'
+    }
+    return 'info'
+  }
 })
 
 function handle (req, res) {
diff --git a/logger.js b/logger.js
index 54ed8b2..c984ec1 100644
--- a/logger.js
+++ b/logger.js
@@ -17,8 +17,14 @@ function pinoLogger (opts, stream) {
   opts.serializers.res = serializers.wrapResponseSerializer(opts.serializers.res || serializers.res)
   opts.serializers.err = opts.serializers.err || serializers.err
 
+  if (opts.useLevel && opts.customLogLevel) {
+    throw new Error("You can't pass 'useLevel' and 'customLogLevel' together")
+  }
+
   var useLevel = opts.useLevel || 'info'
+  var customLogLevel = opts.customLogLevel
   delete opts.useLevel
+  delete opts.customLogLevel
 
   var theStream = opts.stream || stream
   delete opts.stream
@@ -34,9 +40,10 @@ function pinoLogger (opts, stream) {
 
     var log = this.log
     var responseTime = Date.now() - this[startTime]
+    var level = customLogLevel ? customLogLevel(this, err) : useLevel
 
     if (err || this.err || this.statusCode >= 500) {
-      log.error({
+      log[level]({
         res: this,
         err: err || this.err || new Error('failed with status code ' + this.statusCode),
         responseTime: responseTime
@@ -44,7 +51,7 @@ function pinoLogger (opts, stream) {
       return
     }
 
-    log[useLevel]({
+    log[level]({
       res: this,
       responseTime: responseTime
     }, 'request completed')


Test patch:
diff --git a/test.js b/test.js
index a17a5d0..d288304 100644
--- a/test.js
+++ b/test.js
@@ -99,6 +99,37 @@ test('uses the log level passed in as an option', function (t) {
   })
 })
 
+test('uses the custom log level passed in as an option', function (t) {
+  var dest = split(JSON.parse)
+  var logger = pinoHttp({ customLogLevel: function (res, err) {
+    return 'warn'
+  }}, dest)
+
+  setup(t, logger, function (err, server) {
+    t.error(err)
+    doGet(server)
+  })
+
+  dest.on('data', function (line) {
+    t.equal(line.level, 40, 'level')
+    t.notOk(line.customLogLevel, 'customLogLevel not forwarded')
+    t.end()
+  })
+})
+
+test('throw error if custom log level and log level passed in together', function (t) {
+  var dest = split(JSON.parse)
+  var throwFunction = function () {
+    pinoHttp({
+      useLevel: 'info',
+      customLogLevel: function (res, err) {
+        return 'warn'
+      }}, dest)
+  }
+  t.throws(throwFunction, {message: "You can't pass 'useLevel' and 'customLogLevel' together"})
+  t.end()
+})
+
 test('allocate a unique id to every request', function (t) {
   t.plan(5)