From 8931f99521f3d4d4573c13b30636f11334fad591 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Mon, 7 Mar 2016 10:15:51 +0100 Subject: [PATCH 1/5] Added http request automatic recognition. --- pino.js | 16 ++++++++++++++++ test.js | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/pino.js b/pino.js index 97f5fecbc..72f585c06 100644 --- a/pino.js +++ b/pino.js @@ -76,6 +76,10 @@ 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 { params = [a, b, c, d, e, f, g, h, i, j, k] } @@ -122,4 +126,16 @@ function pino (opts, stream) { function noop () {} +function mapHttpRequest (req) { + return { + req: { + method: req.method, + url: req.url, + headers: req.headers, + remoteAddress: req.connection.remoteAddress, + remotePort: req.connection.remotePort + } + } +} + module.exports = pino diff --git a/test.js b/test.js index 7f8dc2b88..00381b9cc 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,41 @@ 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() + }) + }) +}) From e01a9fb098e9f63b6a2651b4ace5b5f18228a45b Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Mon, 7 Mar 2016 10:25:39 +0100 Subject: [PATCH 2/5] Added serializers. --- pino.js | 30 +++++++++++++++++++++--------- test.js | 44 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/pino.js b/pino.js index 72f585c06..e76a58534 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) @@ -96,6 +97,7 @@ function pino (opts, stream) { msg = obj.message } var data = JSON.stringify(new Message(num, msg)) + var value if (obj) { data = data.slice(0, data.length - 1) @@ -103,8 +105,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 += '}' @@ -128,14 +132,22 @@ function noop () {} function mapHttpRequest (req) { return { - req: { - method: req.method, - url: req.url, - headers: req.headers, - remoteAddress: req.connection.remoteAddress, - remotePort: req.connection.remotePort - } + req: asReqValue(req) + } +} + +function asReqValue (req) { + return { + method: req.method, + url: req.url, + headers: req.headers, + remoteAddress: req.connection.remoteAddress, + remotePort: req.connection.remotePort } } module.exports = pino + +module.exports.stdSerializers = { + req: asReqValue +} diff --git a/test.js b/test.js index 00381b9cc..8f01c22c2 100644 --- a/test.js +++ b/test.js @@ -255,7 +255,7 @@ test('set properties defined in the prototype chain', function (t) { test('http request support', function (t) { t.plan(3) - var originalReq; + 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 @@ -289,3 +289,45 @@ test('http request support', function (t) { }) }) }) + +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() + }) + }) +}) From c129f758239a3d15b4659c6e54dec73adb940278 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Mon, 7 Mar 2016 11:41:53 +0000 Subject: [PATCH 3/5] Added sponsored by nearForm acknowledgement --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 42aa39781..f2c50b200 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 @@ -190,6 +191,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 From 031b27e0ef7e611d735b2bfcead6751b25b791b0 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Mon, 7 Mar 2016 12:13:51 +0000 Subject: [PATCH 4/5] Added http response serializer. --- pino.js | 18 +++++++++++++- test.js | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/pino.js b/pino.js index e76a58534..352f667df 100644 --- a/pino.js +++ b/pino.js @@ -80,6 +80,8 @@ function pino (opts, stream) { 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] @@ -136,6 +138,12 @@ function mapHttpRequest (req) { } } +function mapHttpResponse (res) { + return { + res: asResValue(res) + } +} + function asReqValue (req) { return { method: req.method, @@ -146,8 +154,16 @@ function asReqValue (req) { } } +function asResValue (res) { + return { + statusCode: res.statusCode, + header: res._header + } +} + module.exports = pino module.exports.stdSerializers = { - req: asReqValue + req: asReqValue, + res: asResValue } diff --git a/test.js b/test.js index 8f01c22c2..bcfa15f24 100644 --- a/test.js +++ b/test.js @@ -331,3 +331,77 @@ test('http request support via serializer', function (t) { }) }) }) + +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() + }) + }) +}) From 6b9611965f8f0ff717fa7a5b024a7f8cc66a8075 Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Mon, 7 Mar 2016 12:24:27 +0000 Subject: [PATCH 5/5] Added serializers in the docs. --- README.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/README.md b/README.md index f2c50b200..5b7f1991a 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,8 @@ This produces: * logger.info() * logger.debug() * logger.trace() + * pino.stdSerializers.req + * pino.stdSerializers.res ### pino([opts], [stream]) @@ -72,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 @@ -140,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