Skip to content

Commit

Permalink
browser serializers, closes #219
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmarkclements committed Oct 18, 2017
1 parent 663ed24 commit 8c9b4b0
Show file tree
Hide file tree
Showing 5 changed files with 520 additions and 36 deletions.
63 changes: 62 additions & 1 deletion README.md
Expand Up @@ -193,7 +193,7 @@ By default, in the browser,
### Browser Options

Pino can be passed a `browser` object in the options object,
which can have a `write` property or an `asObject` property.
which can have the following properties:

#### `asObject` (Boolean)

Expand Down Expand Up @@ -241,6 +241,67 @@ var pino = require('pino')({browser: {write: {
}}})
```

#### `serialize`: (Boolean | Array)

The serializers provided to `pino` are ignored by default in the browser, including
the standard serializers provided with Pino. Since the default destination for log
messages is the console, values such as `Error` objects are enhanced for inspection,
which they otherwise wouldn't be if the Error serializer was enabled.

We can turn all serializers on,

```js
var pino = require('pino')({
browser: {
serialize: true
}
})
```

Or we can selectively enable them via an array:

```js
var pino = require('pino')({
serializers: {
custom: myCustomSerializer,
another: anotherSerializer
},
browser: {
serialize: ['custom']
}
})
// following will apply myCustomSerializer to the custom property,
// but will not apply anotherSerizlier to another key
pino.info({custom: 'a', another: 'b'})
```

When `serialize` is `true` the standard error serializer is also enabled (see https://github.com/pinojs/pino/blob/master/docs/API.md#stdSerializers).
This is a global serializer which will apply to any `Error` objects passed to the logger methods.

If `serialize` is an array the standard error serializer is also automatically enabled, it can
be explicitly disabled by including a string in the serialize array: `!stdSerializers.err`, like so:

```js
var pino = require('pino')({
serializers: {
custom: myCustomSerializer,
another: anotherSerializer
},
browser: {
serialize: ['!stdSerializers.err', 'custom'] //will not serialize Errors, will serialize `custom` keys
}
})
```

The `serialize` array also applies to any child logger serializers (see https://github.com/pinojs/pino/blob/master/docs/API.md#discussion-2
for how to set child-bound serializers).

Unlike server pino the serializers apply to every object passed to the logger method,
if the `isObject` option is `true`, this results in the serializers applying to the
first object (as in server pino).

For more info on serializers see https://github.com/pinojs/pino/blob/master/docs/API.md#parameters.

<a name="caveats"></a>
## Caveats

Expand Down
87 changes: 80 additions & 7 deletions browser.js
Expand Up @@ -5,12 +5,29 @@ var format = require('quick-format-unescaped')
module.exports = pino

var _console = global.console || {}
var stdSerializers = {
req: mock,
res: mock,
err: asErrValue
}

function pino (opts) {
opts = opts || {}
opts.browser = opts.browser || {}
var proto = opts.browser.write || _console
if (opts.browser.write) opts.browser.asObject = true
var serializers = opts.serializers || {}
var serialize = Array.isArray(opts.browser.serialize)
? opts.browser.serialize.filter(function (k) {
return k !== '!stdSerializers.err'
})
: opts.browser.serialize === true ? Object.keys(serializers) : false
var stdErrSerialize = opts.browser.serialize

if (
Array.isArray(opts.browser.serialize) &&
opts.browser.serialize.indexOf('!stdSerializers.err') > -1
) stdErrSerialize = false

var levels = ['error', 'fatal', 'warn', 'info', 'debug', 'trace']

Expand Down Expand Up @@ -39,12 +56,23 @@ function pino (opts) {
logger.removeAllListeners = logger.listeners =
logger.listenerCount = logger.eventNames =
logger.write = logger.flush = noop

logger.serializers = serializers
logger._serialize = serialize
logger._stdErrSerialize = stdErrSerialize
logger.child = child

function child (bindings) {
if (!bindings) {
throw new Error('missing bindings for child Pino')
}
if (serialize && bindings.serializers) {
var childSerializers = Object.assign({}, serializers, bindings.serializers)
var childSerialize = opts.browser.serialize === true
? Object.keys(childSerializers)
: serialize
delete bindings.serializers
applySerializers([bindings], childSerialize, childSerializers, this._stdErrSerialize)
}
function Child (parent) {
this._childLevel = (parent._childLevel | 0) + 1
this.error = bind(parent, bindings, 'error')
Expand All @@ -53,14 +81,48 @@ function pino (opts) {
this.info = bind(parent, bindings, 'info')
this.debug = bind(parent, bindings, 'debug')
this.trace = bind(parent, bindings, 'trace')
if (childSerializers) {
this.serializers = childSerializers
this._serialize = childSerialize
}
}
Child.prototype = this
return new Child(this)
}
logger.levels = pino.levels
if (serialize && !opts.browser.asObject) serializerWrap(logger, levels)
return !opts.browser.asObject ? logger : asObject(logger, levels)
}

function serializerWrap (logger, levels) {
var k
for (var i = 0; i < levels.length; i++) {
k = levels[i]
logger[k] = (function (write) {
return function LOG () {
var args = new Array(arguments.length)
for (var i = 0; i < args.length; i++) args[i] = arguments[i]
applySerializers(args, this._serialize, this.serializers, this._stdErrSerialize)
write.apply(this, args)
}
})(logger[k])
}
}

function applySerializers (args, serialize, serializers, stdErrSerialize) {
for (var i in args) {
if (stdErrSerialize && args[i] instanceof Error) {
args[i] = pino.stdSerializers.err(args[i])
} else if (typeof args[i] === 'object' && !Array.isArray(args[i])) {
for (var k in args[i]) {
if (serialize.indexOf(k) > -1 && k in serializers) {
args[i][k] = serializers[k](args[i][k])
}
}
}
}
}

function asObject (logger, levels) {
var k
for (var i = 0; i < levels.length; i++) {
Expand All @@ -69,6 +131,7 @@ function asObject (logger, levels) {
return function LOG () {
var args = new Array(arguments.length)
for (var i = 0; i < args.length; i++) args[i] = arguments[i]
if (this._serialize) applySerializers(args, this._serialize, this.serializers, this._stdErrSerialize)
var msg = args[0]
var o = { time: Date.now(), level: pino.levels.values[k] }
var lvl = (this._childLevel | 0) + 1
Expand All @@ -81,7 +144,7 @@ function asObject (logger, levels) {
msg = args.length ? format(args) : undefined
} else if (typeof msg === 'string') msg = format(args)
if (msg !== undefined) o.msg = msg
write.call(logger, o)
write.call(this, o)
}
})(logger[k], k)
}
Expand Down Expand Up @@ -109,11 +172,7 @@ pino.levels = {
}
}

pino.stdSerializers = {
req: mock,
res: mock,
err: mock
}
pino.stdSerializers = stdSerializers

function bind (parent, bindings, level) {
return function () {
Expand All @@ -131,5 +190,19 @@ function set (logger, val, level, fallback) {
: (logger[level] ? logger[level] : (_console[level] || _console[fallback] || noop))
}

function asErrValue (err) {
var obj = {
type: err.constructor.name,
msg: err.message,
stack: err.stack
}
for (var key in err) {
if (obj[key] === undefined) {
obj[key] = err[key]
}
}
return obj
}

function mock () { return {} }
function noop () {}

0 comments on commit 8c9b4b0

Please sign in to comment.