Skip to content

Commit

Permalink
Merge f37ddca into b74045a
Browse files Browse the repository at this point in the history
  • Loading branch information
mcollina committed Apr 20, 2021
2 parents b74045a + f37ddca commit 6af5e7a
Show file tree
Hide file tree
Showing 16 changed files with 999 additions and 75 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Expand Up @@ -16,7 +16,7 @@ jobs:
fail-fast: false
matrix:
os: [macOS-latest, windows-latest, ubuntu-latest]
node-version: [10, 12, 13, 14]
node-version: [12, 14]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
Expand Down Expand Up @@ -51,4 +51,4 @@ jobs:
- uses: fastify/github-action-merge-dependabot@v2.0.0
if: ${{ github.actor == 'dependabot[bot]' && github.event_name == 'pull_request' }}
with:
github-token: ${{secrets.github_token}}
github-token: ${{secrets.github_token}}
98 changes: 98 additions & 0 deletions benchmarks/multistream.js
@@ -0,0 +1,98 @@
'use strict'

const bench = require('fastbench')
const bunyan = require('bunyan')
const pino = require('../')
const fs = require('fs')
const dest = fs.createWriteStream('/dev/null')

const tenStreams = [
{ stream: dest },
{ stream: dest },
{ stream: dest },
{ stream: dest },
{ stream: dest },
{ level: 'debug', stream: dest },
{ level: 'debug', stream: dest },
{ level: 'trace', stream: dest },
{ level: 'warn', stream: dest },
{ level: 'fatal', stream: dest }
]
const pinomsTen = pino({ level: 'debug' }, pino.multistream(tenStreams))

const fourStreams = [
{ stream: dest },
{ stream: dest },
{ level: 'debug', stream: dest },
{ level: 'trace', stream: dest }
]
const pinomsFour = pino({ level: 'debug' }, pino.multistream(fourStreams))

const pinomsOne = pino({ level: 'info' }, pino.multistream(dest))
const blogOne = bunyan.createLogger({
name: 'myapp',
streams: [{ stream: dest }]
})

const blogTen = bunyan.createLogger({
name: 'myapp',
streams: tenStreams
})
const blogFour = bunyan.createLogger({
name: 'myapp',
streams: fourStreams
})

const max = 10
const run = bench([
function benchBunyanTen (cb) {
for (let i = 0; i < max; i++) {
blogTen.info('hello world')
blogTen.debug('hello world')
blogTen.trace('hello world')
blogTen.warn('hello world')
blogTen.fatal('hello world')
}
setImmediate(cb)
},
function benchPinoMSTen (cb) {
for (let i = 0; i < max; i++) {
pinomsTen.info('hello world')
pinomsTen.debug('hello world')
pinomsTen.trace('hello world')
pinomsTen.warn('hello world')
pinomsTen.fatal('hello world')
}
setImmediate(cb)
},
function benchBunyanFour (cb) {
for (let i = 0; i < max; i++) {
blogFour.info('hello world')
blogFour.debug('hello world')
blogFour.trace('hello world')
}
setImmediate(cb)
},
function benchPinoMSFour (cb) {
for (let i = 0; i < max; i++) {
pinomsFour.info('hello world')
pinomsFour.debug('hello world')
pinomsFour.trace('hello world')
}
setImmediate(cb)
},
function benchBunyanOne (cb) {
for (let i = 0; i < max; i++) {
blogOne.info('hello world')
}
setImmediate(cb)
},
function benchPinoMSOne (cb) {
for (let i = 0; i < max; i++) {
pinomsOne.info('hello world')
}
setImmediate(cb)
}
], 10000)

run()
78 changes: 78 additions & 0 deletions docs/api.md
Expand Up @@ -24,6 +24,7 @@
* [Statics](#statics)
* [pino.destination()](#pino-destination)
* [pino.final()](#pino-final)
* [pino.multistream()](#pino-multistream)
* [pino.stdSerializers](#pino-stdserializers)
* [pino.stdTimeFunctions](#pino-stdtimefunctions)
* [pino.symbols](#pino-symbols)
Expand Down Expand Up @@ -257,6 +258,10 @@ These functions should return an JSONifiable object and they
should never throw. When logging an object, each top-level property
matching the exact key of a serializer will be serialized using the defined serializer.

The serializers are applied when a property in the logged object matches a property
in the serializers. The only exception is the `err` serializer as it is also applied in case
the object is an instance of `Error`, e.g. `logger.info(new Error('kaboom'))`.

* See [pino.stdSerializers](#pino-stdserializers)

##### `serializers[Symbol.for('pino.*')]` (Function) - DEPRECATED
Expand Down Expand Up @@ -478,6 +483,9 @@ logger.info({MIX: {IN: true}})
// {"level":30,"time":1531254555820,"pid":55956,"hostname":"x","MIX":{"IN":true}}
```

If the object is of type Error, it is wrapped in an object containing a property err (`{ err: mergingObject }`).
This allows for a unified error handling flow.

<a id="message"></a>
#### `message` (String)

Expand All @@ -499,6 +507,9 @@ the `message` parameter not the value of the `msg` property on the `mergedObject
See [Avoid Message Conflict](/docs/help.md#avoid-message-conflict) for information
on how to overcome this limitation.

If no `message` parameter is provided, and the `mergedObject` is of type `Error` or it has a property named `err`, the
`message` parameter is set to the `message` value of the error.

The `messageKey` option can be used at instantiation time to change the namespace
from `msg` to another string as preferred.

Expand Down Expand Up @@ -928,6 +939,73 @@ finalLogger.info('exiting...')
* See [Asynchronous logging ⇗](/docs/asynchronous.md)
* See [Log loss prevention ⇗](/docs/asynchronous.md#log-loss-prevention)

<a id="pino-multistream"></a>

### `pino.multistream(options) => Stream`

Create a stream composed by multiple destination streams:

```js
var fs = require('fs')
var pino = require('pino')
var streams = [
{stream: fs.createWriteStream('/tmp/info.stream.out')},
{level: 'debug', stream: fs.createWriteStream('/tmp/debug.stream.out')},
{level: 'fatal', stream: fs.createWriteStream('/tmp/fatal.stream.out')}
]

var log = pino({
level: 'debug' // this MUST be set at the lowest level of the
// destinations
}, pino.multistream(streams))

log.debug('this will be written to /tmp/debug.stream.out')
log.info('this will be written to /tmp/debug.stream.out and /tmp/info.stream.out')
log.fatal('this will be written to /tmp/debug.stream.out, /tmp/info.stream.out and /tmp/fatal.stream.out')
```

In order for `multistream` to work, the log level __must__ be set to the lowest level used in the streams array.

#### Options

* `levels`: Pass custom log level definitions to the instance as an object.

+ `dedupe`: Set this to `true` to send logs only to the stream with the higher level. Default: `false`

`dedupe` flag can be useful for example when using pino-multi-stream to redirect `error` logs to `process.stderr` and others to `process.stdout`:

```js
var pino = require('pino')
var multistream = pino.multistream
var streams = [
{stream: process.stdout},
{level: 'error', stream: process.stderr},
]

var opts = {
levels: {
silent: Infinity,
fatal: 60,
error: 50,
warn: 50,
info: 30,
debug: 20,
trace: 10
},
dedupe: true,
}

var log = pino({
level: 'debug' // this MUST be set at the lowest level of the
// destinations
}, multistream(streams, opts))

log.debug('this will be written ONLY to process.stdout')
log.info('this will be written ONLY to process.stdout')
log.error('this will be written ONLY to process.stderr')
log.fatal('this will be written ONLY to process.stderr')
```

<a id="pino-stdserializers"></a>
### `pino.stdSerializers` (Object)

Expand Down
23 changes: 4 additions & 19 deletions docs/help.md
Expand Up @@ -118,20 +118,7 @@ Given a similar scenario as in the [Log rotation](#rotate) section a basic
<a id="multiple"></a>
## Saving to multiple files

Let's assume we want to store all error messages to a separate log file.

Install [pino-tee](https://npm.im/pino-tee) with:

```bash
npm i pino-tee -g
```

The following writes the log output of `app.js` to `./all-logs`, while
writing only warnings and errors to `./warn-log:

```bash
node app.js | pino-tee warn ./warn-logs > ./all-logs
```
See [`pino.multistream`](/doc/api.md#pino-multistream).

<a id="filter-logs"></a>
## Log Filtering
Expand Down Expand Up @@ -164,14 +151,13 @@ ExecStart=/bin/sh -c '/path/to/node app.js | pino-transport'

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).
by using [`pino.multistream`](/doc/api.md#pino-multistream).

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},
Expand All @@ -180,11 +166,10 @@ var streams = [

const logger = pino({
name: 'my-app',
level: 'info',
}, multistream(streams))
level: 'debug', // must be the lowest level of all streams
}, pino.multistream(streams))
```


<a id="dupe-keys"></a>
## How Pino handles duplicate keys

Expand Down
4 changes: 2 additions & 2 deletions lib/levels.js
@@ -1,6 +1,5 @@
'use strict'
/* eslint no-prototype-builtins: 0 */
const flatstr = require('flatstr')
const {
lsCacheSym,
levelValSym,
Expand Down Expand Up @@ -47,7 +46,7 @@ const nums = Object.keys(levels).reduce((o, k) => {
}, {})

const initialLsCache = Object.keys(nums).reduce((o, k) => {
o[k] = flatstr('{"level":' + Number(k))
o[k] = '{"level":' + Number(k)
return o
}, {})

Expand Down Expand Up @@ -188,6 +187,7 @@ module.exports = {
setLevel,
isLevelEnabled,
mappings,
levels,
assertNoLevelCollisions,
assertDefaultLevelFound
}

0 comments on commit 6af5e7a

Please sign in to comment.