Repository: pinojs/pino
Pull request #818 (2020-04-10 09:28:51)

Update docs interpolation values examples to match v6
Fixes: https://github.com/pinojs/pino/issues/817
See: https://github.com/pinojs/pino/pull/795
See: https://github.com/pinojs/pino/issues/793


Patch:
diff --git a/docs/api.md b/docs/api.md
index 7634768..48d70b6 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -11,6 +11,7 @@
   * [logger.warn()](#warn)
   * [logger.error()](#error)
   * [logger.fatal()](#fatal)
+  * [logger.silent()](#silent)
   * [logger.child()](#child)
   * [logger.bindings()](#bindings)
   * [logger.flush()](#flush)
@@ -143,6 +144,7 @@ If an object is supplied, three options can be specified:
 An object mapping to hook functions. Hook functions allow for customizing
 internal logger operations. Hook functions ***must*** be synchronous functions.
 
+<a id="logmethod"></a>
 ##### `logMethod`
 
 Allows for manipulating the parameters passed to logger methods. The signature
@@ -483,21 +485,38 @@ to any supplied printf-style placeholders (`%s`, `%d`, `%o`|`%O`|`%j`) to form
 the final output `msg` value for the JSON log line.
 
 ```js
-logger.info('hello', 'world')
-// {"level":30,"time":1531257618044,"msg":"hello world","pid":55956,"hostname":"x"}
+logger.info('%o hello %s', {worldly: 1}, 'world')
+// {"level":30,"time":1531257826880,"msg":"{\"worldly\":1} hello world","pid":55956,"hostname":"x"}
 ```
 
+Since pino v6, we do not automatically concatenate and cast to string
+consecutive parameters:
+
 ```js
-logger.info('hello', {worldly: 1})
-// {"level":30,"time":1531257797727,"msg":"hello {\"worldly\":1}","pid":55956,"hostname":"x"}
+logger.info('hello', 'world')
+// {"level":30,"time":1531257618044,"msg":"hello","pid":55956,"hostname":"x"}
+// world is missing
 ```
 
+However, it's possible to inject a hook to modify this behavior:
+
 ```js
-logger.info('%o hello', {worldly: 1})
-// {"level":30,"time":1531257826880,"msg":"{\"worldly\":1} hello","pid":55956,"hostname":"x"}
+const pinoOptions = {
+  hooks: { logMethod }
+}
+
+function logMethod (args, method) {
+  if (args.length === 2) {
+    args[0] = `${args[0]} %j`
+  }
+  method.apply(this, args)
+}
+
+const logger = pino(pinoOptions)
 ```
 
 * See [`message` log method parameter](#message)
+* See [`logMethod` hook](#logmethod)
 
 <a id="trace"></a>
 ### `logger.trace([mergingObject], [message], [...interpolationValues])`
@@ -560,6 +579,10 @@ the process crashes or exits.
 * See [`message` log method parameter](#message)
 * See [`...interpolationValues` log method parameter](#interpolationvalues)
 
+<a id="silent"><a>
+### `logger.silent()`
+
+Noop function.
 
 <a id="child"></a>
 ### `logger.child(bindings) => logger`
@@ -669,7 +692,7 @@ The logging level is a *minimum* level based on the associated value of that lev
 For instance if `logger.level` is `info` *(30)* then `info` *(30)*, `warn` *(40)*, `error` *(50)* and `fatal` *(60)* log methods will be enabled but the `trace` *(10)* and `debug` *(20)* methods, being less than 30, will not.
 
 The `silent` logging level is a specialized level which will disable all logging,
-there is no `silent` log method.
+the `silent` log method is a noop function.
 
 <a id="islevelenabled"></a>
 ### `logger.isLevelEnabled(level)`
diff --git a/docs/help.md b/docs/help.md
index db9f1cb..37826bf 100644
--- a/docs/help.md
+++ b/docs/help.md
@@ -6,6 +6,7 @@
 * [Saving to multiple files](#multiple)
 * [Log filtering](#filter-logs)
 * [Transports and systemd](#transport-systemd)
+* [Log to different streams](#multi-stream)
 * [Duplicate keys](#dupe-keys)
 * [Log levels as labels instead of numbers](#level-string)
 * [Pino with `debug`](#debug)
@@ -156,6 +157,32 @@ this challenge is to use a subshell:
 ExecStart=/bin/sh -c '/path/to/node app.js | pino-transport'
 ```
 
+<a id="multi-stream"></a>
+## Log to different streams
+
+Pino's default log destination is the singular destination of `stdout`. While
+not recommended for performance reasons, multiple destinations can be targeted
+by using [`pino-multi-stream`](https://github.com/pinojs/pino-multi-stream).
+
+In this example we use `stderr` for `error` level logs and `stdout` as default
+for all other levels (e.g. `debug`, `info`, and `warn`).
+
+```js
+const pino = require('pino')
+const { multistream } = require('pino-multi-stream')
+var streams = [
+  {level: 'debug', stream: process.stdout},
+  {level: 'error', stream: process.stderr},
+  {level: 'fatal', stream: process.stderr}
+]
+
+const logger = pino({
+  name: 'my-app',
+  level: 'info',
+}, multistream(streams))
+```
+
+
 <a id="dupe-keys"></a>
 ## How Pino handles duplicate keys
 
diff --git a/pino.js b/pino.js
index 680d031..2c5a5f1 100644
--- a/pino.js
+++ b/pino.js
@@ -13,7 +13,8 @@ const {
   final,
   stringify,
   buildSafeSonicBoom,
-  buildFormatters
+  buildFormatters,
+  noop
 } = require('./lib/tools')
 const { version } = require('./lib/meta')
 const {
@@ -165,7 +166,8 @@ function pino (...args) {
     [mixinSym]: mixin,
     [chindingsSym]: chindings,
     [formattersSym]: allFormatters,
-    [hooksSym]: hooks
+    [hooksSym]: hooks,
+    silent: noop
   })
   Object.setPrototypeOf(instance, proto)
 


Test patch:
diff --git a/test/levels.test.js b/test/levels.test.js
index dd79970..a71cfd3 100644
--- a/test/levels.test.js
+++ b/test/levels.test.js
@@ -498,3 +498,55 @@ test('fatal method should call async when sync-flushing fails', ({ equal, fail,
   const instance = pino(stream)
   doesNotThrow(() => instance.fatal(messages[0]))
 })
+
+test('calling silent method on logger instance', async ({ fail }) => {
+  const instance = pino({ level: 'silent' }, sink((result, enc) => {
+    fail('no data should be logged')
+  }))
+  instance.silent('hello world')
+})
+
+test('calling silent method on child logger', async ({ fail }) => {
+  const child = pino({ level: 'silent' }, sink((result, enc) => {
+    fail('no data should be logged')
+  })).child({})
+  child.silent('hello world')
+})
+
+test('changing level from info to silent and back to info', async ({ is }) => {
+  const expected = {
+    level: 30,
+    msg: 'hello world'
+  }
+  const stream = sink()
+  const instance = pino({ level: 'info' }, stream)
+
+  instance.level = 'silent'
+  instance.info('hello world')
+  let result = stream.read()
+  is(result, null)
+
+  instance.level = 'info'
+  instance.info('hello world')
+  result = await once(stream, 'data')
+  check(is, result, expected.level, expected.msg)
+})
+
+test('changing level from info to silent and back to info in child logger', async ({ is }) => {
+  const expected = {
+    level: 30,
+    msg: 'hello world'
+  }
+  const stream = sink()
+  const child = pino({ level: 'info' }, stream).child({})
+
+  child.level = 'silent'
+  child.info('hello world')
+  let result = stream.read()
+  is(result, null)
+
+  child.level = 'info'
+  child.info('hello world')
+  result = await once(stream, 'data')
+  check(is, result, expected.level, expected.msg)
+})