Skip to content

Commit fdf4e59

Browse files
committed
feat(audit): Add the ability to specify a custom audit log serializer (for err, req and res)
1 parent bb97ac0 commit fdf4e59

File tree

6 files changed

+135
-104
lines changed

6 files changed

+135
-104
lines changed

lib/plugins/audit.js

+67-51
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ function getResponseHeaders(res) {
5050
* the audit log object programmatically
5151
* @param {boolean} [opts.printLog=true] - Whether to print the log
5252
* via the logger.
53+
* @param {Object} [opts.serializers] - Override the default logger serializers
54+
* for err, req and res
5355
* @returns {Function} Handler
5456
* @fires audit when an audit log has been generated
5557
* @example
@@ -175,6 +177,7 @@ function auditLogger(opts) {
175177
assert.optionalFunc(opts.context, 'opts.context');
176178
assert.optionalObject(opts.server, 'opts.server');
177179
assert.optionalBool(opts.printLog, 'opts.printLog');
180+
assert.optionalObject(opts.serializers, 'opts.serializers');
178181

179182
if (
180183
opts.event !== 'after' &&
@@ -198,67 +201,80 @@ function auditLogger(opts) {
198201
}
199202
var errSerializer = bunyan.stdSerializers.err;
200203

204+
// don't break legacy use, where there was no top level opts.serializer
201205
if (opts.log.serializers && opts.log.serializers.err) {
202206
errSerializer = opts.log.serializers.err;
203207
}
204208

205-
var log = opts.log.child({
206-
audit: true,
207-
component: opts.event,
208-
serializers: {
209-
err: errSerializer,
210-
req: function auditRequestSerializer(req) {
211-
if (!req) {
212-
return false;
213-
}
209+
if (opts.serializers && opts.serializers.err) {
210+
errSerializer = opts.serializers.err;
211+
}
214212

215-
var timers = {};
216-
(req.timers || []).forEach(function forEach(time) {
217-
var t = time.time;
218-
var _t = Math.floor(1000000 * t[0] + t[1] / 1000);
219-
timers[time.name] = (timers[time.name] || 0) + _t;
220-
});
221-
return {
222-
// account for native and queryParser plugin usage
223-
query:
224-
typeof req.query === 'function'
225-
? req.query()
226-
: req.query,
227-
method: req.method,
228-
url: req.url,
229-
headers: req.headers,
230-
httpVersion: req.httpVersion,
231-
trailers: req.trailers,
232-
version: req.version(),
233-
body: opts.body === true ? req.body : undefined,
234-
timers: timers,
235-
connectionState:
236-
req.connectionState && req.connectionState()
237-
};
238-
},
239-
res: function auditResponseSerializer(res) {
240-
if (!res) {
241-
return false;
242-
}
213+
var reqSerializer = function auditRequestSerializer(req) {
214+
if (!req) {
215+
return false;
216+
}
217+
218+
var timers = {};
219+
(req.timers || []).forEach(function forEach(time) {
220+
var t = time.time;
221+
var _t = Math.floor(1000000 * t[0] + t[1] / 1000);
222+
timers[time.name] = (timers[time.name] || 0) + _t;
223+
});
224+
return {
225+
// account for native and queryParser plugin usage
226+
query: typeof req.query === 'function' ? req.query() : req.query,
227+
method: req.method,
228+
url: req.url,
229+
headers: req.headers,
230+
httpVersion: req.httpVersion,
231+
trailers: req.trailers,
232+
version: req.version(),
233+
body: opts.body === true ? req.body : undefined,
234+
timers: timers,
235+
connectionState: req.connectionState && req.connectionState()
236+
};
237+
};
243238

244-
var body;
239+
if (opts.serializers && opts.serializers.req) {
240+
reqSerializer = opts.serializers.req;
241+
}
242+
243+
var resSerialzer = function auditResponseSerializer(res) {
244+
if (!res) {
245+
return false;
246+
}
245247

246-
if (opts.body === true) {
247-
if (res._body instanceof HttpError) {
248-
body = res._body.body;
249-
} else {
250-
body = res._body;
251-
}
252-
}
248+
var body;
253249

254-
return {
255-
statusCode: res.statusCode,
256-
headers: getResponseHeaders(res),
257-
trailer: res._trailer || false,
258-
body: body
259-
};
250+
if (opts.body === true) {
251+
if (res._body instanceof HttpError) {
252+
body = res._body.body;
253+
} else {
254+
body = res._body;
260255
}
261256
}
257+
258+
return {
259+
statusCode: res.statusCode,
260+
headers: getResponseHeaders(res),
261+
trailer: res._trailer || false,
262+
body: body
263+
};
264+
};
265+
266+
if (opts.serializers && opts.serializers.res) {
267+
resSerialzer = opts.serializers.res;
268+
}
269+
270+
var log = opts.log.child({
271+
audit: true,
272+
component: opts.event,
273+
serializers: {
274+
err: errSerializer,
275+
req: reqSerializer,
276+
res: resSerialzer
277+
}
262278
});
263279

264280
function audit(req, res, route, err) {

test/plugins/audit.test.js

+46
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,52 @@ describe('audit logger', function() {
165165
});
166166
});
167167

168+
it('test custom serializers', function(done) {
169+
// Dirty hack to capture the log record using a ring buffer.
170+
var ringbuffer = new bunyan.RingBuffer({ limit: 1 });
171+
172+
SERVER.once(
173+
'after',
174+
restify.plugins.auditLogger({
175+
log: bunyan.createLogger({
176+
name: 'audit',
177+
streams: [
178+
{
179+
level: 'info',
180+
type: 'raw',
181+
stream: ringbuffer
182+
}
183+
]
184+
}),
185+
event: 'after',
186+
serializers: {
187+
req: function(req) {
188+
return { fooReq: 'barReq' };
189+
},
190+
res: function(res) {
191+
return { fooRes: 'barRes' };
192+
}
193+
}
194+
})
195+
);
196+
197+
SERVER.get('/audit', function aTestHandler(req, res, next) {
198+
res.send('');
199+
return next();
200+
});
201+
202+
SERVER.on('after', function() {
203+
var record = ringbuffer.records && ringbuffer.records[0];
204+
assert.equal(record.req.fooReq, 'barReq');
205+
assert.equal(record.res.fooRes, 'barRes');
206+
done();
207+
});
208+
209+
CLIENT.get('/audit', function(err, req, res) {
210+
assert.ifError(err);
211+
});
212+
});
213+
168214
it('should log handler timers', function(done) {
169215
// Dirty hack to capture the log record using a ring buffer.
170216
var ringbuffer = new bunyan.RingBuffer({ limit: 1 });

test/plugins/jsonBodyParser.test.js

+6-12
Original file line numberDiff line numberDiff line change
@@ -257,12 +257,9 @@ describe('JSON body parser', function() {
257257
'\r\n' +
258258
'{"foo":"bar"}';
259259

260-
var client = net.connect(
261-
{ host: '127.0.0.1', port: PORT },
262-
function() {
263-
client.write(request);
264-
}
265-
);
260+
var client = net.connect({ host: '127.0.0.1', port: PORT }, function() {
261+
client.write(request);
262+
});
266263
client.once('data', function(data) {
267264
client.end();
268265
});
@@ -293,12 +290,9 @@ describe('JSON body parser', function() {
293290
'\r\n' +
294291
'{"foo":"bar"}';
295292

296-
var client = net.connect(
297-
{ host: '127.0.0.1', port: PORT },
298-
function() {
299-
client.write(request);
300-
}
301-
);
293+
var client = net.connect({ host: '127.0.0.1', port: PORT }, function() {
294+
client.write(request);
295+
});
302296

303297
client.once('data', function(data) {
304298
client.end();

test/plugins/static.test.js

+11-23
Original file line numberDiff line numberDiff line change
@@ -321,17 +321,11 @@ describe('static resource plugin', function() {
321321
});
322322
});
323323

324-
socket.connect(
325-
{ host: '127.0.0.1', port: PORT },
326-
function() {
327-
socket.write(RAW_REQUEST, 'utf-8', function(
328-
err2,
329-
data
330-
) {
331-
assert.ifError(err2);
332-
});
333-
}
334-
);
324+
socket.connect({ host: '127.0.0.1', port: PORT }, function() {
325+
socket.write(RAW_REQUEST, 'utf-8', function(err2, data) {
326+
assert.ifError(err2);
327+
});
328+
});
335329
});
336330
}
337331
);
@@ -369,18 +363,12 @@ describe('static resource plugin', function() {
369363
});
370364

371365
var socket = new net.Socket();
372-
socket.connect(
373-
{ host: '127.0.0.1', port: PORT },
374-
function() {
375-
socket.write(RAW_REQUEST, 'utf-8', function(
376-
err2,
377-
data
378-
) {
379-
assert.ifError(err2);
380-
socket.end();
381-
});
382-
}
383-
);
366+
socket.connect({ host: '127.0.0.1', port: PORT }, function() {
367+
socket.write(RAW_REQUEST, 'utf-8', function(err2, data) {
368+
assert.ifError(err2);
369+
socket.end();
370+
});
371+
});
384372
});
385373
}
386374
);

test/serverHttp2.test.js

+3-6
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,9 @@ before(function(cb) {
5353
});
5454
SERVER.listen(PORT, '127.0.0.1', function() {
5555
PORT = SERVER.address().port;
56-
CLIENT = http2.connect(
57-
'https://127.0.0.1:' + PORT,
58-
{
59-
rejectUnauthorized: false
60-
}
61-
);
56+
CLIENT = http2.connect('https://127.0.0.1:' + PORT, {
57+
rejectUnauthorized: false
58+
});
6259

6360
cb();
6461
});

test/upgrade.test.js

+2-12
Original file line numberDiff line numberDiff line change
@@ -304,12 +304,7 @@ test('GET with upgrade headers', function(t) {
304304
t.equal(typeof socket, 'object');
305305
t.ok(Buffer.isBuffer(head), 'head is Buffer');
306306
t.doesNotThrow(function() {
307-
var shed = WATERSHED.connect(
308-
res,
309-
socket,
310-
head,
311-
wskey
312-
);
307+
var shed = WATERSHED.connect(res, socket, head, wskey);
313308
SHEDLIST.push(shed);
314309
shed.end('ok, done');
315310
shed.on('error', function(err3) {
@@ -380,12 +375,7 @@ test('GET with some websocket traffic', function(t) {
380375
t.equal(typeof socket, 'object');
381376
t.ok(Buffer.isBuffer(head), 'head is Buffer');
382377
t.doesNotThrow(function() {
383-
var shed = WATERSHED.connect(
384-
res,
385-
socket,
386-
head,
387-
wskey
388-
);
378+
var shed = WATERSHED.connect(res, socket, head, wskey);
389379
SHEDLIST.push(shed);
390380
shed.on('error', function(err3) {
391381
t.ifError(err3);

0 commit comments

Comments
 (0)