Skip to content

Commit e3b0fc4

Browse files
committed
Merge branch 'master' into stringify-opt
2 parents c897432 + 6b96119 commit e3b0fc4

File tree

3 files changed

+281
-2
lines changed

3 files changed

+281
-2
lines changed

README.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ It also includes a shell utility to pretty-print its log files.
1010
* [API](#api)
1111
* [Benchmarks](#benchmarks)
1212
* [How do I rotate log files?](#rotate)
13+
* [Acknowledgements](#acknowledgements)
1314
* [License](#license)
1415

1516
## Install
@@ -62,6 +63,8 @@ This produces:
6263
* <a href="#info"><code>logger.<b>info()</b></code></a>
6364
* <a href="#debug"><code>logger.<b>debug()</b></code></a>
6465
* <a href="#trace"><code>logger.<b>trace()</b></code></a>
66+
* <a href="#reqSerializer"><code>pino.stdSerializers.<b>req</b></code></a>
67+
* <a href="#resSerializer"><code>pino.stdSerializers.<b>res</b></code></a>
6568

6669
<a name="constructor"></a>
6770
### pino([opts], [stream])
@@ -71,9 +74,28 @@ Returns a new logger. Allowed options are:
7174
* `safe`: avoid error causes by circular references in the object tree,
7275
default `true`
7376
* `name`: the name of the logger, default `undefined`
77+
* `serializers`: an object containing functions for custom serialization
78+
of objects. These functions should return an jsonificable object and
79+
they should never throw.
7480

7581
`stream` is a Writable stream, defaults to `process.stdout`.
7682

83+
Example:
84+
85+
```js
86+
'use strict'
87+
88+
var pino = require('pino')
89+
var instance = pino({
90+
name: 'myapp',
91+
safe: true,
92+
serializers: {
93+
req: pino.stdSerializers.req
94+
res: pino.stdSerializers.res
95+
}
96+
}
97+
```
98+
7799
<a name="level"></a>
78100
### logger.level
79101
@@ -139,6 +161,59 @@ object, all its properties will be included in the JSON line.
139161
If more args follows `msg`, these will be used to format `msg` using
140162
[`util.format`](https://nodejs.org/api/util.html#util_util_format_format)
141163
164+
<a name="reqSerializer"></a>
165+
### pino.stdSerializers.req
166+
167+
Function to generate a jsonificable object out of an HTTP request from
168+
node HTTP server.
169+
170+
It returns an object in the form:
171+
172+
```js
173+
{
174+
pid: 93535,
175+
hostname: 'your host',
176+
level: 30,
177+
msg: 'my request',
178+
time: '2016-03-07T12:21:48.766Z',
179+
v: 0,
180+
req: {
181+
method: 'GET',
182+
url: '/',
183+
headers: {
184+
host: 'localhost:50201',
185+
connection: 'close'
186+
},
187+
remoteAddress: '::ffff:127.0.0.1',
188+
remotePort: 50202
189+
}
190+
}
191+
```
192+
193+
<a name="resSerializer"></a>
194+
### pino.stdSerializers.res
195+
196+
Function to generate a jsonificable object out of an HTTP
197+
response from
198+
node HTTP server.
199+
200+
It returns an object in the form:
201+
202+
```js
203+
{
204+
pid: 93581,
205+
hostname: 'myhost',
206+
level: 30,
207+
msg: 'my response',
208+
time: '2016-03-07T12:23:18.041Z',
209+
v: 0,
210+
res: {
211+
statusCode: 200,
212+
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'
213+
}
214+
}
215+
```
216+
142217
<a name="benchmarks"></a>
143218
## Benchmarks
144219
@@ -190,6 +265,11 @@ In order to rotate your log files, add in `/etc/logrotate.d/myapp`:
190265
}
191266
```
192267
268+
<a name="acknowledgements"></a>
269+
## Acknowledgements
270+
271+
This project was kindly sponsored by [nearForm](http://nearform.com).
272+
193273
## License
194274
195275
MIT

pino.js

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ function pino (opts, stream) {
3535
debug: null,
3636
trace: null
3737
}
38+
var serializers = opts.serializers || {}
3839

3940
for (var key in levels) {
4041
funcs[key] = genLogFunction(key)
@@ -76,6 +77,12 @@ function pino (opts, stream) {
7677
obj = a
7778
params = [b, c, d, e, f, g, h, i, j, k]
7879
base = 1
80+
81+
if (obj.method && obj.headers && obj.socket) {
82+
obj = mapHttpRequest(obj)
83+
} else if (obj.statusCode) {
84+
obj = mapHttpResponse(obj)
85+
}
7986
} else {
8087
params = [a, b, c, d, e, f, g, h, i, j, k]
8188
}
@@ -92,15 +99,18 @@ function pino (opts, stream) {
9299
msg = obj.message
93100
}
94101
var data = message(num, msg)
102+
var value
95103
if (obj) {
96104
data = data.slice(0, data.length - 1)
97105

98106
if (obj instanceof Error) {
99107
data += ',"type":"Error","stack":' + stringify(obj.stack) + '}'
100108
} else {
101109
for (var key in obj) {
102-
if (obj.hasOwnProperty(key) && obj[key] !== undefined) {
103-
data += ',"' + key + '":' + stringify(obj[key])
110+
value = obj[key]
111+
if (obj.hasOwnProperty(key) && value !== undefined) {
112+
value = serializers[key] ? serializers[key](value) : value
113+
data += ',"' + key + '":' + stringify(value)
104114
}
105115
}
106116
data += '}'
@@ -122,4 +132,38 @@ function pino (opts, stream) {
122132

123133
function noop () {}
124134

135+
function mapHttpRequest (req) {
136+
return {
137+
req: asReqValue(req)
138+
}
139+
}
140+
141+
function mapHttpResponse (res) {
142+
return {
143+
res: asResValue(res)
144+
}
145+
}
146+
147+
function asReqValue (req) {
148+
return {
149+
method: req.method,
150+
url: req.url,
151+
headers: req.headers,
152+
remoteAddress: req.connection.remoteAddress,
153+
remotePort: req.connection.remotePort
154+
}
155+
}
156+
157+
function asResValue (res) {
158+
return {
159+
statusCode: res.statusCode,
160+
header: res._header
161+
}
162+
}
163+
125164
module.exports = pino
165+
166+
module.exports.stdSerializers = {
167+
req: asReqValue,
168+
res: asResValue
169+
}

test.js

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var pino = require('./')
55
var writeStream = require('flush-write-stream')
66
var os = require('os')
77
var split = require('split2')
8+
var http = require('http')
89
var pid = process.pid
910
var hostname = os.hostname()
1011

@@ -250,3 +251,157 @@ test('set properties defined in the prototype chain', function (t) {
250251

251252
instance.info(new MyObject())
252253
})
254+
255+
test('http request support', function (t) {
256+
t.plan(3)
257+
258+
var originalReq
259+
var instance = pino(sink(function (chunk, enc, cb) {
260+
t.ok(Date.parse(chunk.time) <= new Date(), 'time is greater than Date.now()')
261+
delete chunk.time
262+
t.deepEqual(chunk, {
263+
pid: pid,
264+
hostname: hostname,
265+
level: 30,
266+
msg: 'my request',
267+
v: 0,
268+
req: {
269+
method: originalReq.method,
270+
url: originalReq.url,
271+
headers: originalReq.headers,
272+
remoteAddress: originalReq.connection.remoteAddress,
273+
remotePort: originalReq.connection.remotePort
274+
}
275+
})
276+
cb()
277+
}))
278+
279+
var server = http.createServer(function (req, res) {
280+
originalReq = req
281+
instance.info(req, 'my request')
282+
res.end('hello')
283+
}).listen(function (err) {
284+
t.error(err)
285+
t.teardown(server.close.bind(server))
286+
287+
http.get('http://localhost:' + server.address().port, function (res) {
288+
res.resume()
289+
})
290+
})
291+
})
292+
293+
test('http request support via serializer', function (t) {
294+
t.plan(3)
295+
296+
var originalReq
297+
var instance = pino({
298+
serializers: {
299+
req: pino.stdSerializers.req
300+
}
301+
}, sink(function (chunk, enc, cb) {
302+
t.ok(Date.parse(chunk.time) <= new Date(), 'time is greater than Date.now()')
303+
delete chunk.time
304+
t.deepEqual(chunk, {
305+
pid: pid,
306+
hostname: hostname,
307+
level: 30,
308+
msg: 'my request',
309+
v: 0,
310+
req: {
311+
method: originalReq.method,
312+
url: originalReq.url,
313+
headers: originalReq.headers,
314+
remoteAddress: originalReq.connection.remoteAddress,
315+
remotePort: originalReq.connection.remotePort
316+
}
317+
})
318+
cb()
319+
}))
320+
321+
var server = http.createServer(function (req, res) {
322+
originalReq = req
323+
instance.info({ req: req }, 'my request')
324+
res.end('hello')
325+
}).listen(function (err) {
326+
t.error(err)
327+
t.teardown(server.close.bind(server))
328+
329+
http.get('http://localhost:' + server.address().port, function (res) {
330+
res.resume()
331+
})
332+
})
333+
})
334+
335+
test('http response support', function (t) {
336+
t.plan(3)
337+
338+
var originalRes
339+
var instance = pino(sink(function (chunk, enc, cb) {
340+
t.ok(Date.parse(chunk.time) <= new Date(), 'time is greater than Date.now()')
341+
delete chunk.time
342+
t.deepEqual(chunk, {
343+
pid: pid,
344+
hostname: hostname,
345+
level: 30,
346+
msg: 'my response',
347+
v: 0,
348+
res: {
349+
statusCode: originalRes.statusCode,
350+
header: originalRes._header
351+
}
352+
})
353+
cb()
354+
}))
355+
356+
var server = http.createServer(function (req, res) {
357+
originalRes = res
358+
res.end('hello')
359+
instance.info(res, 'my response')
360+
}).listen(function (err) {
361+
t.error(err)
362+
t.teardown(server.close.bind(server))
363+
364+
http.get('http://localhost:' + server.address().port, function (res) {
365+
res.resume()
366+
})
367+
})
368+
})
369+
370+
test('http response support via a serializer', function (t) {
371+
t.plan(3)
372+
373+
var originalRes
374+
var instance = pino({
375+
serializers: {
376+
res: pino.stdSerializers.res
377+
}
378+
}, sink(function (chunk, enc, cb) {
379+
t.ok(Date.parse(chunk.time) <= new Date(), 'time is greater than Date.now()')
380+
delete chunk.time
381+
t.deepEqual(chunk, {
382+
pid: pid,
383+
hostname: hostname,
384+
level: 30,
385+
msg: 'my response',
386+
v: 0,
387+
res: {
388+
statusCode: originalRes.statusCode,
389+
header: originalRes._header
390+
}
391+
})
392+
cb()
393+
}))
394+
395+
var server = http.createServer(function (req, res) {
396+
originalRes = res
397+
res.end('hello')
398+
instance.info({ res: res }, 'my response')
399+
}).listen(function (err) {
400+
t.error(err)
401+
t.teardown(server.close.bind(server))
402+
403+
http.get('http://localhost:' + server.address().port, function (res) {
404+
res.resume()
405+
})
406+
})
407+
})

0 commit comments

Comments
 (0)