diff --git a/README.md b/README.md
index 0af19308b..5bc258c08 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,7 @@ It also includes a shell utility to pretty-print its log files.
* [API](#api)
* [Benchmarks](#benchmarks)
* [How do I rotate log files?](#rotate)
+* [Acknowledgements](#acknowledgements)
* [License](#license)
## Install
@@ -62,6 +63,8 @@ This produces:
* logger.info()
* logger.debug()
* logger.trace()
+ * pino.stdSerializers.req
+ * pino.stdSerializers.res
### pino([opts], [stream])
@@ -71,9 +74,28 @@ Returns a new logger. Allowed options are:
* `safe`: avoid error causes by circular references in the object tree,
default `true`
* `name`: the name of the logger, default `undefined`
+* `serializers`: an object containing functions for custom serialization
+ of objects. These functions should return an jsonificable object and
+ they should never throw.
`stream` is a Writable stream, defaults to `process.stdout`.
+Example:
+
+```js
+'use strict'
+
+var pino = require('pino')
+var instance = pino({
+ name: 'myapp',
+ safe: true,
+ serializers: {
+ req: pino.stdSerializers.req
+ res: pino.stdSerializers.res
+ }
+}
+```
+
### logger.level
@@ -139,6 +161,59 @@ object, all its properties will be included in the JSON line.
If more args follows `msg`, these will be used to format `msg` using
[`util.format`](https://nodejs.org/api/util.html#util_util_format_format)
+
+### pino.stdSerializers.req
+
+Function to generate a jsonificable object out of an HTTP request from
+node HTTP server.
+
+It returns an object in the form:
+
+```js
+{
+ pid: 93535,
+ hostname: 'your host',
+ level: 30,
+ msg: 'my request',
+ time: '2016-03-07T12:21:48.766Z',
+ v: 0,
+ req: {
+ method: 'GET',
+ url: '/',
+ headers: {
+ host: 'localhost:50201',
+ connection: 'close'
+ },
+ remoteAddress: '::ffff:127.0.0.1',
+ remotePort: 50202
+ }
+}
+```
+
+
+### pino.stdSerializers.res
+
+Function to generate a jsonificable object out of an HTTP
+response from
+node HTTP server.
+
+It returns an object in the form:
+
+```js
+{
+ pid: 93581,
+ hostname: 'myhost',
+ level: 30,
+ msg: 'my response',
+ time: '2016-03-07T12:23:18.041Z',
+ v: 0,
+ res: {
+ statusCode: 200,
+ header: 'HTTP/1.1 200 OK\r\nDate: Mon, 07 Mar 2016 12:23:18 GMT\r\nConnection: close\r\nContent-Length: 5\r\n\r\n'
+ }
+}
+```
+
## Benchmarks
@@ -190,6 +265,11 @@ In order to rotate your log files, add in `/etc/logrotate.d/myapp`:
}
```
+
+## Acknowledgements
+
+This project was kindly sponsored by [nearForm](http://nearform.com).
+
## License
MIT
diff --git a/pino.js b/pino.js
index 3aec8cd08..e7a083355 100644
--- a/pino.js
+++ b/pino.js
@@ -35,6 +35,7 @@ function pino (opts, stream) {
debug: null,
trace: null
}
+ var serializers = opts.serializers || {}
for (var key in levels) {
funcs[key] = genLogFunction(key)
@@ -76,6 +77,12 @@ function pino (opts, stream) {
obj = a
params = [b, c, d, e, f, g, h, i, j, k]
base = 1
+
+ if (obj.method && obj.headers && obj.socket) {
+ obj = mapHttpRequest(obj)
+ } else if (obj.statusCode) {
+ obj = mapHttpResponse(obj)
+ }
} else {
params = [a, b, c, d, e, f, g, h, i, j, k]
}
@@ -92,6 +99,7 @@ function pino (opts, stream) {
msg = obj.message
}
var data = message(num, msg)
+ var value
if (obj) {
data = data.slice(0, data.length - 1)
@@ -99,8 +107,10 @@ function pino (opts, stream) {
data += ',"type":"Error","stack":' + stringify(obj.stack) + '}'
} else {
for (var key in obj) {
- if (obj.hasOwnProperty(key) && obj[key] !== undefined) {
- data += ',"' + key + '":' + stringify(obj[key])
+ value = obj[key]
+ if (obj.hasOwnProperty(key) && value !== undefined) {
+ value = serializers[key] ? serializers[key](value) : value
+ data += ',"' + key + '":' + stringify(value)
}
}
data += '}'
@@ -122,4 +132,38 @@ function pino (opts, stream) {
function noop () {}
+function mapHttpRequest (req) {
+ return {
+ req: asReqValue(req)
+ }
+}
+
+function mapHttpResponse (res) {
+ return {
+ res: asResValue(res)
+ }
+}
+
+function asReqValue (req) {
+ return {
+ method: req.method,
+ url: req.url,
+ headers: req.headers,
+ remoteAddress: req.connection.remoteAddress,
+ remotePort: req.connection.remotePort
+ }
+}
+
+function asResValue (res) {
+ return {
+ statusCode: res.statusCode,
+ header: res._header
+ }
+}
+
module.exports = pino
+
+module.exports.stdSerializers = {
+ req: asReqValue,
+ res: asResValue
+}
diff --git a/test.js b/test.js
index 7f8dc2b88..bcfa15f24 100644
--- a/test.js
+++ b/test.js
@@ -5,6 +5,7 @@ var pino = require('./')
var writeStream = require('flush-write-stream')
var os = require('os')
var split = require('split2')
+var http = require('http')
var pid = process.pid
var hostname = os.hostname()
@@ -250,3 +251,157 @@ test('set properties defined in the prototype chain', function (t) {
instance.info(new MyObject())
})
+
+test('http request support', function (t) {
+ t.plan(3)
+
+ var originalReq
+ var instance = pino(sink(function (chunk, enc, cb) {
+ t.ok(Date.parse(chunk.time) <= new Date(), 'time is greater than Date.now()')
+ delete chunk.time
+ t.deepEqual(chunk, {
+ pid: pid,
+ hostname: hostname,
+ level: 30,
+ msg: 'my request',
+ v: 0,
+ req: {
+ method: originalReq.method,
+ url: originalReq.url,
+ headers: originalReq.headers,
+ remoteAddress: originalReq.connection.remoteAddress,
+ remotePort: originalReq.connection.remotePort
+ }
+ })
+ cb()
+ }))
+
+ var server = http.createServer(function (req, res) {
+ originalReq = req
+ instance.info(req, 'my request')
+ res.end('hello')
+ }).listen(function (err) {
+ t.error(err)
+ t.teardown(server.close.bind(server))
+
+ http.get('http://localhost:' + server.address().port, function (res) {
+ res.resume()
+ })
+ })
+})
+
+test('http request support via serializer', function (t) {
+ t.plan(3)
+
+ var originalReq
+ var instance = pino({
+ serializers: {
+ req: pino.stdSerializers.req
+ }
+ }, sink(function (chunk, enc, cb) {
+ t.ok(Date.parse(chunk.time) <= new Date(), 'time is greater than Date.now()')
+ delete chunk.time
+ t.deepEqual(chunk, {
+ pid: pid,
+ hostname: hostname,
+ level: 30,
+ msg: 'my request',
+ v: 0,
+ req: {
+ method: originalReq.method,
+ url: originalReq.url,
+ headers: originalReq.headers,
+ remoteAddress: originalReq.connection.remoteAddress,
+ remotePort: originalReq.connection.remotePort
+ }
+ })
+ cb()
+ }))
+
+ var server = http.createServer(function (req, res) {
+ originalReq = req
+ instance.info({ req: req }, 'my request')
+ res.end('hello')
+ }).listen(function (err) {
+ t.error(err)
+ t.teardown(server.close.bind(server))
+
+ http.get('http://localhost:' + server.address().port, function (res) {
+ res.resume()
+ })
+ })
+})
+
+test('http response support', function (t) {
+ t.plan(3)
+
+ var originalRes
+ var instance = pino(sink(function (chunk, enc, cb) {
+ t.ok(Date.parse(chunk.time) <= new Date(), 'time is greater than Date.now()')
+ delete chunk.time
+ t.deepEqual(chunk, {
+ pid: pid,
+ hostname: hostname,
+ level: 30,
+ msg: 'my response',
+ v: 0,
+ res: {
+ statusCode: originalRes.statusCode,
+ header: originalRes._header
+ }
+ })
+ cb()
+ }))
+
+ var server = http.createServer(function (req, res) {
+ originalRes = res
+ res.end('hello')
+ instance.info(res, 'my response')
+ }).listen(function (err) {
+ t.error(err)
+ t.teardown(server.close.bind(server))
+
+ http.get('http://localhost:' + server.address().port, function (res) {
+ res.resume()
+ })
+ })
+})
+
+test('http response support via a serializer', function (t) {
+ t.plan(3)
+
+ var originalRes
+ var instance = pino({
+ serializers: {
+ res: pino.stdSerializers.res
+ }
+ }, sink(function (chunk, enc, cb) {
+ t.ok(Date.parse(chunk.time) <= new Date(), 'time is greater than Date.now()')
+ delete chunk.time
+ t.deepEqual(chunk, {
+ pid: pid,
+ hostname: hostname,
+ level: 30,
+ msg: 'my response',
+ v: 0,
+ res: {
+ statusCode: originalRes.statusCode,
+ header: originalRes._header
+ }
+ })
+ cb()
+ }))
+
+ var server = http.createServer(function (req, res) {
+ originalRes = res
+ res.end('hello')
+ instance.info({ res: res }, 'my response')
+ }).listen(function (err) {
+ t.error(err)
+ t.teardown(server.close.bind(server))
+
+ http.get('http://localhost:' + server.address().port, function (res) {
+ res.resume()
+ })
+ })
+})