[issue #49] Allow a log.child() to specify the level of inherited streams.

This commit is contained in:
Trent Mick 2012-10-22 22:30:00 -07:00
parent a8279bd90d
commit 677a485348
4 changed files with 152 additions and 17 deletions

View file

@ -2,6 +2,16 @@
## bunyan 0.14.6 (not yet released) ## bunyan 0.14.6 (not yet released)
- [issue #49] Allow a `log.child()` to specify the level of inherited streams.
For example:
# Before
var childLog = log.child({...});
childLog.level('debug');
# After
var childLog = log.child({..., level: 'debug'});
- Improve the crash message to make it easier to provide relevant details in a - Improve the crash message to make it easier to provide relevant details in a
bug report. bug report.

View file

@ -238,9 +238,8 @@ function Logger(options, _childOptions, _childSimple) {
throw new TypeError('invalid options.name: child cannot set logger name'); throw new TypeError('invalid options.name: child cannot set logger name');
} }
} }
if ((options.stream || options.level) && options.streams) { if (options.stream && options.streams) {
throw new TypeError( throw new TypeError('cannot mix "streams" and "stream" options');
'cannot mix "streams" with "stream" or "level" options');
} }
if (options.streams && !Array.isArray(options.streams)) { if (options.streams && !Array.isArray(options.streams)) {
throw new TypeError('invalid options.streams: must be an array') throw new TypeError('invalid options.streams: must be an array')
@ -289,6 +288,9 @@ function Logger(options, _childOptions, _childSimple) {
this.serializers = objCopy(parent.serializers); this.serializers = objCopy(parent.serializers);
this.src = parent.src; this.src = parent.src;
this.fields = objCopy(parent.fields); this.fields = objCopy(parent.fields);
if (options.level) {
this.level(options.level);
}
} else { } else {
this._level = Number.POSITIVE_INFINITY; this._level = Number.POSITIVE_INFINITY;
this.streams = []; this.streams = [];
@ -314,6 +316,8 @@ function Logger(options, _childOptions, _childSimple) {
if (s.level) { if (s.level) {
s.level = resolveLevel(s.level); s.level = resolveLevel(s.level);
} else if (options.level) {
s.level = resolveLevel(options.level);
} else { } else {
s.level = INFO; s.level = INFO;
} }
@ -370,7 +374,8 @@ function Logger(options, _childOptions, _childSimple) {
}); });
} }
// Handle *config* options. // Handle *config* options (i.e. options that are not just plain data
// for log records).
if (options.stream) { if (options.stream) {
addStream({ addStream({
type: 'stream', type: 'stream',
@ -380,6 +385,8 @@ function Logger(options, _childOptions, _childSimple) {
}); });
} else if (options.streams) { } else if (options.streams) {
options.streams.forEach(addStream); options.streams.forEach(addStream);
} else if (parent && options.level) {
this.level(options.level);
} else if (!parent) { } else if (!parent) {
addStream({ addStream({
type: 'stream', type: 'stream',
@ -439,9 +446,12 @@ util.inherits(Logger, EventEmitter);
* @param options {Object} Optional. Set of options to apply to the child. * @param options {Object} Optional. Set of options to apply to the child.
* All of the same options for a new Logger apply here. Notes: * All of the same options for a new Logger apply here. Notes:
* - The parent's streams are inherited and cannot be removed in this * - The parent's streams are inherited and cannot be removed in this
* call. * call. Any given `streams` are *added* to the set inherited from
* the parent.
* - The parent's serializers are inherited, though can effectively be * - The parent's serializers are inherited, though can effectively be
* overwritten by using duplicate keys. * overwritten by using duplicate keys.
* - Can use `level` to set the level of the streams inherited from
* the parent. The level for the parent is NOT affected.
* @param simple {Boolean} Optional. Set to true to assert that `options` * @param simple {Boolean} Optional. Set to true to assert that `options`
* (a) only add fields (no config) and (b) no serialization handling is * (a) only add fields (no config) and (b) no serialization handling is
* required for them. IOW, this is a fast path for frequent child * required for them. IOW, this is a fast path for frequent child

View file

@ -0,0 +1,127 @@
/*
* Copyright (c) 2012 Trent Mick. All rights reserved.
*
* Test some `<Logger>.child(...)` behaviour.
*/
var test = require('tap').test;
var bunyan = require('../lib/bunyan');
function CapturingStream(recs) {
this.recs = recs || [];
}
CapturingStream.prototype.write = function (rec) {
this.recs.push(rec);
}
test('child can add stream', function (t) {
var dadStream = new CapturingStream();
var dad = bunyan.createLogger({
name: 'surname',
streams: [{
type: 'raw',
stream: dadStream,
level: 'info'
}]
});
var sonStream = new CapturingStream();
var son = dad.child({
component: 'son',
streams: [{
type: 'raw',
stream: sonStream,
level: 'debug'
}]
});
dad.info('info from dad');
dad.debug('debug from dad');
son.debug('debug from son');
var rec;
t.equal(dadStream.recs.length, 1);
rec = dadStream.recs[0];
t.equal(rec.msg, 'info from dad');
t.equal(sonStream.recs.length, 1);
rec = sonStream.recs[0];
t.equal(rec.msg, 'debug from son');
t.end();
});
test('child can set level of inherited streams', function (t) {
var dadStream = new CapturingStream();
var dad = bunyan.createLogger({
name: 'surname',
streams: [{
type: 'raw',
stream: dadStream,
level: 'info'
}]
});
// Intention here is that the inherited `dadStream` logs at 'debug' level
// for the son.
var son = dad.child({
component: 'son',
level: 'debug'
});
dad.info('info from dad');
dad.debug('debug from dad');
son.debug('debug from son');
var rec;
t.equal(dadStream.recs.length, 2);
rec = dadStream.recs[0];
t.equal(rec.msg, 'info from dad');
rec = dadStream.recs[1];
t.equal(rec.msg, 'debug from son');
t.end();
});
test('child can set level of inherited streams and add streams', function (t) {
var dadStream = new CapturingStream();
var dad = bunyan.createLogger({
name: 'surname',
streams: [{
type: 'raw',
stream: dadStream,
level: 'info'
}]
});
// Intention here is that the inherited `dadStream` logs at 'debug' level
// for the son.
var sonStream = new CapturingStream();
var son = dad.child({
component: 'son',
level: 'trace',
streams: [{
type: 'raw',
stream: sonStream,
level: 'debug'
}]
});
dad.info('info from dad');
dad.trace('trace from dad');
son.trace('trace from son');
son.debug('debug from son');
t.equal(dadStream.recs.length, 3);
t.equal(dadStream.recs[0].msg, 'info from dad');
t.equal(dadStream.recs[1].msg, 'trace from son');
t.equal(dadStream.recs[2].msg, 'debug from son');
t.equal(sonStream.recs.length, 1);
t.equal(sonStream.recs[0].msg, 'debug from son');
t.end();
});

View file

@ -26,10 +26,6 @@ test('ensure Logger creation options', function (t) {
t.throws(function () { new Logger(options); }, t.throws(function () { new Logger(options); },
'cannot use "stream" and "streams"'); 'cannot use "stream" and "streams"');
options = {name: 'foo', level: 'info', streams: []};
t.throws(function () { new Logger(options); },
'cannot use "level" and "streams"');
// https://github.com/trentm/node-bunyan/issues/3 // https://github.com/trentm/node-bunyan/issues/3
options = {name: 'foo', streams: {}}; options = {name: 'foo', streams: {}};
t.throws(function () { new Logger(options); }, t.throws(function () { new Logger(options); },
@ -72,10 +68,6 @@ test('ensure Logger creation options (createLogger)', function (t) {
t.throws(function () { bunyan.createLogger(options); }, t.throws(function () { bunyan.createLogger(options); },
'cannot use "stream" and "streams"'); 'cannot use "stream" and "streams"');
options = {name: 'foo', level: 'info', streams: []};
t.throws(function () { bunyan.createLogger(options); },
'cannot use "level" and "streams"');
// https://github.com/trentm/node-bunyan/issues/3 // https://github.com/trentm/node-bunyan/issues/3
options = {name: 'foo', streams: {}}; options = {name: 'foo', streams: {}};
t.throws(function () { bunyan.createLogger(options); }, t.throws(function () { bunyan.createLogger(options); },
@ -122,10 +114,6 @@ test('ensure Logger child() options', function (t) {
t.throws(function () { log.child(options); }, t.throws(function () { log.child(options); },
'cannot use "stream" and "streams"'); 'cannot use "stream" and "streams"');
options = {level: 'info', streams: []};
t.throws(function () { log.child(options); },
'cannot use "level" and "streams"');
// https://github.com/trentm/node-bunyan/issues/3 // https://github.com/trentm/node-bunyan/issues/3
options = {streams: {}}; options = {streams: {}};
t.throws(function () { log.child(options); }, t.throws(function () { log.child(options); },