diff --git a/docs/api.md b/docs/api.md
index 3efe63782..15570949a 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -30,6 +30,12 @@
* [pino.stdTimeFunctions](#pino-stdtimefunctions)
* [pino.symbols](#pino-symbols)
* [pino.version](#pino-version)
+* [Interfaces](#interfaces)
+ * [MultiStreamRes](#multistreamres)
+ * [StreamEntry](#streamentry)
+ * [DestinationStream](#destinationstream)
+* [Types](#types)
+ * [Level](#level-1)
## `pino([options], [destination]) => logger`
@@ -1164,9 +1170,10 @@ finalLogger.info('exiting...')
-### `pino.multistream(options) => Stream`
+### `pino.multistream(streamsArray, opts) => MultiStreamRes`
-Create a stream composed by multiple destination streams:
+Create a stream composed by multiple destination streams and returns an
+object implementing the [MultiStreamRes](#multistreamres) interface.
```js
var fs = require('fs')
@@ -1272,3 +1279,56 @@ for general use.
Exposes the Pino package version. Also available on the logger instance.
* See [`logger.version`](#version)
+
+## Interfaces
+
+
+### `MultiStreamRes`
+ Properties:
+
+ * `write(data)`
+ - `data` Object | string
+ - Returns: void
+
+ Write `data` onto the streams held by the current instance.
+ * `add(dest)`
+ - `dest` [StreamEntry](#streamentry) | [DestinationStream](#destinationstream)
+ - Returns: [MultiStreamRes](#multistreamres)
+
+ Add `dest` stream to the array of streams of the current instance.
+ * `flushSync()`
+ - Returns: `undefined`
+
+ Call `flushSync` on each stream held by the current instance.
+ * `minLevel`
+ - number
+
+ The minimum level amongst all the streams held by the current instance.
+ * `streams`
+ - Returns: [StreamEntry[]](#streamentry)
+
+ The array of streams currently held by the current instance.
+ * `clone(level)`
+ - `level` [Level](#level-1)
+ - Returns: [MultiStreamRes](#multistreamres)
+
+ Returns a cloned object of the current instance but with the the provided `level`.
+
+### `StreamEntry`
+ Properties:
+
+ * `stream`
+ - DestinationStream
+ * `level`
+ - Optional: [Level](#level-1)
+
+### `DestinationStream`
+ Properties:
+
+ * `write(msg)`
+ - `msg` string
+
+## Types
+### `Level`
+
+ * Values: `"fatal"` | `"error"` | `"warn"` | `"info"` | `"debug"` | `"trace"`
diff --git a/lib/multistream.js b/lib/multistream.js
index 96527986b..45213edd2 100644
--- a/lib/multistream.js
+++ b/lib/multistream.js
@@ -6,9 +6,10 @@ const { levels } = require('./levels')
const defaultLevels = Object.create(levels)
defaultLevels.silent = Infinity
+const DEFAULT_INFO_LEVEL = levels.info
+
function multistream (streamsArray, opts) {
let counter = 0
-
streamsArray = streamsArray || []
opts = opts || { dedupe: false }
@@ -77,22 +78,39 @@ function multistream (streamsArray, opts) {
}
function add (dest) {
+ if (!dest) {
+ return res
+ }
+
+ // Check that dest implements either StreamEntry or DestinationStream
+ const isStream = typeof dest.write === 'function' || dest.stream
+ const stream_ = dest.write ? dest : dest.stream
+ // This is necessary to provide a meaningful error message, otherwise it throws somewhere inside write()
+ if (!isStream) {
+ throw Error('stream object needs to implement either StreamEntry or DestinationStream interface')
+ }
+
const { streams } = this
- if (typeof dest.write === 'function') {
- return add.call(this, { stream: dest })
- } else if (typeof dest.levelVal === 'number') {
- return add.call(this, Object.assign({}, dest, { level: dest.levelVal, levelVal: undefined }))
+
+ let level
+ if (typeof dest.levelVal === 'number') {
+ level = dest.levelVal
} else if (typeof dest.level === 'string') {
- return add.call(this, Object.assign({}, dest, { level: levels[dest.level] }))
- } else if (typeof dest.level !== 'number') {
- // we default level to 'info'
- dest = Object.assign({}, dest, { level: 30 })
+ level = levels[dest.level]
+ } else if (typeof dest.level === 'number') {
+ level = dest.level
} else {
- dest = Object.assign({}, dest)
+ level = DEFAULT_INFO_LEVEL
+ }
+
+ const dest_ = {
+ stream: stream_,
+ level,
+ levelVal: undefined,
+ id: counter++
}
- dest.id = counter++
- streams.unshift(dest)
+ streams.unshift(dest_)
streams.sort(compareByLevel)
this.minLevel = streams[0].level
diff --git a/pino.d.ts b/pino.d.ts
index 31efc4f7b..b1be3867f 100644
--- a/pino.d.ts
+++ b/pino.d.ts
@@ -261,10 +261,10 @@ declare namespace pino {
interface MultiStreamRes {
write: (data: any) => void,
- add: (dest: Record) => MultiStreamRes,
+ add: (dest: StreamEntry | DestinationStream) => MultiStreamRes,
flushSync: () => void,
minLevel: number,
- streams: ({ stream: DestinationStream, level: number, id: number })[],
+ streams: StreamEntry[],
clone(level: Level): MultiStreamRes,
}
diff --git a/test/multistream.test.js b/test/multistream.test.js
index 94ae848bf..7073148a2 100644
--- a/test/multistream.test.js
+++ b/test/multistream.test.js
@@ -481,7 +481,7 @@ test('no stream', function (t) {
t.end()
})
-test('add a stream', function (t) {
+test('one stream', function (t) {
let messageCount = 0
const stream = writeStream(function (data, enc, cb) {
messageCount += 1
@@ -497,6 +497,43 @@ test('add a stream', function (t) {
t.end()
})
+test('add a stream', function (t) {
+ let messageCount = 0
+ const stream = writeStream(function (data, enc, cb) {
+ messageCount += 1
+ cb()
+ })
+
+ const log = pino({
+ level: 'trace'
+ }, multistream().add(stream))
+ log.info('info stream')
+ log.debug('debug stream')
+ log.fatal('fatal stream')
+ t.equal(messageCount, 2)
+ t.end()
+})
+
+test('multistream.add throws if not a stream', function (t) {
+ try {
+ pino({
+ level: 'trace'
+ }, multistream().add({}))
+ } catch (_) {
+ t.end()
+ }
+})
+
+test('multistream throws if not a stream', function (t) {
+ try {
+ pino({
+ level: 'trace'
+ }, multistream({}))
+ } catch (_) {
+ t.end()
+ }
+})
+
test('flushSync', function (t) {
const tmp = join(
os.tmpdir(),
diff --git a/test/types/pino-multistream.test-d.ts b/test/types/pino-multistream.test-d.ts
index cc2f6863c..6006f9c53 100644
--- a/test/types/pino-multistream.test-d.ts
+++ b/test/types/pino-multistream.test-d.ts
@@ -21,5 +21,6 @@ expectType(pino.multistream(streams))
expectType(pino.multistream(streams, {}))
expectType(pino.multistream(streams, { levels: { 'info': 30 } }))
expectType(pino.multistream(streams, { dedupe: true }))
+expectType(pino.multistream(streams[0]).add(streams[1]))
expectType(multistream(process.stdout));