Repository: pinojs/pino (js)

Issue:
Improving pino cli --errorProps
I really appreciate this new feature but I recognized that it does work only with non-nested properties.
If the additional property is an nested object the output is errors: [object Object].
It would be nice if this gets also parsed and prettyfied.

Also it would be quite nice if one could simply pass --errorProps * as argument to print any extra property. (In case I do not exactly know what library XY adds to the base Error object. )

Pull request description:
Issue #371, add print all nested props for errorProps flag
I've added `*` property for `errorProps` flag to resolve #371 

Glad to your feedback 😃 

### Result view

`log.log`:

```
{"level":50,"time":1457537229339,"msg":"Error message.","pid":44127,"hostname":"MacBook-Pro-3.home","type":"Error","stack":"Stack of the error","statusCode":500,"dataBaseSpecificError":{"errorType":"some database error type","erroMessage":"some database error message","evenMoreSpecificStuff":{"someErrorRelatedObject":"error"}},"v":1}
```

<img width="641" alt="screen shot 2018-03-18 at 19 45 06" src="https://user-images.githubusercontent.com/3539802/37565943-0df843f8-2ae5-11e8-863c-6fe3fbd9e2b7.png">

<img width="634" alt="screen shot 2018-03-18 at 19 45 35" src="https://user-images.githubusercontent.com/3539802/37565946-13400ae4-2ae5-11e8-8aae-4cd9497c8539.png">


Patch:
diff --git a/docs/cli.md b/docs/cli.md
index 952f1b8..1dafa8a 100644
--- a/docs/cli.md
+++ b/docs/cli.md
@@ -79,7 +79,7 @@ If an instance of `Error` is logged, Pino adds `"type":"Error"` to the logged JS
 Thus, when prettifying the output, Pino will transform the JSON:
 
 ```js
-{"level":50,"time":1457537229339,"msg":"Error message.","pid":44127,"hostname":"MacBook-Pro-3.home","type":"Error","stack":"Stack of the error","statusCode":500,"v":1}
+{"level":50,"time":1457537229339,"msg":"Error message.","pid":44127,"hostname":"MacBook-Pro-3.home","type":"Error","stack":"Stack of the error","statusCode":500,"dataBaseSpecificError":{"errorType":"some database error type","erroMessage":"some database error message","evenMoreSpecificStuff":{"someErrorRelatedObject":"error"}},"v":1}
 ```
 
 To:
@@ -91,10 +91,10 @@ ERROR [2016-03-09T15:27:09.339Z] (44127 on MacBook-Pro-3.home): Error message.
 
 To log additional properties of `Error` objects, supply the `--errorProps <properties>` flag.
 
-For example, `pino --errorProps statusCode,v` will transform:
+For example, `pino --errorProps statusCode` will transform:
 
 ```js
-{"level":50,"time":1457537229339,"msg":"Error message.","pid":44127,"hostname":"MacBook-Pro-3.home","type":"Error","stack":"Stack of the error","statusCode":500,"v":1}
+{"level":50,"time":1457537229339,"msg":"Error message.","pid":44127,"hostname":"MacBook-Pro-3.home","type":"Error","stack":"Stack of the error","statusCode":500,"dataBaseSpecificError":{"errorType":"some database error type","erroMessage":"some database error message","evenMoreSpecificStuff":{"someErrorRelatedObject":"error"}},"v":1}
 ```
 
 To:
@@ -103,5 +103,29 @@ To:
 ERROR [2016-03-09T15:27:09.339Z] (44127 on MacBook-Pro-3.home): Error message.
     Stack of the error
 statusCode: 500
-v: 1
-```
\ No newline at end of file
+```
+
+In order to print all nested properties of `Error` objects, you can use `--errorProps` flag with `*` property.
+
+Note: you must quote or escape the `*` (asterisk) to avoid shell expansion.
+
+`pino --errorProps '*'` will transform:
+
+```js
+{"level":50,"time":1457537229339,"msg":"Error message.","pid":44127,"hostname":"MacBook-Pro-3.home","type":"Error","stack":"Stack of the error","statusCode":500,"dataBaseSpecificError":{"errorType":"some database error type","erroMessage":"some database error message","evenMoreSpecificStuff":{"someErrorRelatedObject":"error"}},"v":1}
+```
+
+To:
+
+```sh
+[2016-03-09T15:27:09.339Z] ERROR (44127 on MacBook-Pro-3.home): Error message.
+    Stack of the error
+statusCode: 500
+dataBaseSpecificError: {
+    errorType: "some database error type"
+    erroMessage: "some database error message"
+    evenMoreSpecificStuff: {
+      "someErrorRelatedObject": "error"
+    }
+}
+```
diff --git a/pretty.js b/pretty.js
index 0209f8b..4b61b0d 100755
--- a/pretty.js
+++ b/pretty.js
@@ -51,9 +51,14 @@ function withSpaces (value, eol) {
   return lines.join(eol)
 }
 
-function filter (value, messageKey, eol) {
+function filter (value, messageKey, eol, excludeStandardKeys) {
   var keys = Object.keys(value)
-  var filteredKeys = standardKeys.concat([messageKey])
+  var filteredKeys = [messageKey]
+
+  if (excludeStandardKeys !== false) {
+    filteredKeys = filteredKeys.concat(standardKeys)
+  }
+
   var result = ''
 
   for (var i = 0; i < keys.length; i++) {
@@ -174,11 +179,35 @@ function pretty (opts) {
     if (value.type === 'Error') {
       line += '    ' + withSpaces(value.stack, eol) + eol
 
+      var propsForPrint
       if (errorProps && errorProps.length > 0) {
-        for (var i = 0; i < errorProps.length; i++) {
-          var key = errorProps[i]
+        // don't need print these props for 'Error' object
+        var excludedProps = standardKeys.concat([messageKey, 'type', 'stack'])
+
+        if (errorProps[0] === '*') {
+          // print all value props excluding 'excludedProps'
+          propsForPrint = Object.keys(value).filter(function (prop) {
+            return excludedProps.indexOf(prop) < 0
+          })
+        } else {
+          // print props from 'errorProps' only
+          // but exclude 'excludedProps'
+          propsForPrint = errorProps.filter(function (prop) {
+            return excludedProps.indexOf(prop) < 0
+          })
+        }
+
+        for (var i = 0; i < propsForPrint.length; i++) {
+          var key = propsForPrint[i]
+
           if (value.hasOwnProperty(key)) {
-            line += key + ': ' + value[key] + eol
+            if (value[key] instanceof Object) {
+              // call 'filter' with 'excludeStandardKeys' = false
+              // because nested property might contain property from 'standardKeys'
+              line += key + ': {' + eol + filter(value[key], '', eol, false) + '}' + eol
+            } else {
+              line += key + ': ' + value[key] + eol
+            }
           }
         }
       }


Test patch:
diff --git a/test/pretty.test.js b/test/pretty.test.js
index afc818c..af41aaf 100644
--- a/test/pretty.test.js
+++ b/test/pretty.test.js
@@ -420,7 +420,7 @@ test('pino pretty dateFormat flag', function (t) {
   instance.info('>' + Date.now() + '< hello world')
 })
 
-test('pino pretty errorProps flag', function (t) {
+test('pino pretty errorProps flag with certain props', function (t) {
   t.plan(3)
   var prettier = pretty({ errorProps: ['statusCode', 'originalStack'] })
 
@@ -447,3 +447,43 @@ test('pino pretty errorProps flag', function (t) {
 
   instance.error(error)
 })
+
+test('pino pretty errorProps flag with "*" (print all nested props)', function (t) {
+  t.plan(9)
+  var prettier = pretty({ errorProps: ['*'] })
+
+  var expectedLines = [
+    undefined,
+    '    error stack',
+    'statusCode: 500',
+    'originalStack: original stack',
+    'dataBaseSpecificError: {',
+    '    erroMessage: "some database error message"',
+    '    evenMoreSpecificStuff: {',
+    '      "someErrorRelatedObject": "error"',
+    '    }',
+    '}'
+  ]
+
+  prettier.pipe(split(function (line) {
+    var expectedLine = expectedLines.shift()
+    if (expectedLine !== undefined) {
+      t.equal(line, expectedLine, 'prettifies the line')
+    }
+  }))
+
+  var instance = pino(prettier)
+
+  var error = new Error('error message')
+  error.stack = 'error stack'
+  error.statusCode = 500
+  error.originalStack = 'original stack'
+  error.dataBaseSpecificError = {
+    erroMessage: 'some database error message',
+    evenMoreSpecificStuff: {
+      someErrorRelatedObject: 'error'
+    }
+  }
+
+  instance.error(error)
+})