per issue #8 change the way a raw stream is handled

"raw" is now a stream "type" isntead of a separate attribute. The
idea is that writing raw objects (instead of JSON-stringified stings)
to, e.g. a file WriteStream isn't useful. So, lets re-use the stream
"type" field instead of an additional "raw" field.
This commit is contained in:
Trent Mick 2012-06-21 14:49:04 -07:00
parent 0047bc060f
commit 37a1447f22
7 changed files with 84 additions and 70 deletions

View file

@ -13,17 +13,19 @@
recent, and thus hopefully relevant, log messages. (by @dapsays, recent, and thus hopefully relevant, log messages. (by @dapsays,
github.com/davepacheco) github.com/davepacheco)
Potential uses: Live debugging Potential uses: Live debugging if a running process could inspect those
if a running process could inspect those messages. One could dump recent messages. One could dump recent log messages at a finer log level than is
log messages at a finer log level than is typically logged on typically logged on
[`uncaughtException`](http://nodejs.org/docs/latest/api/all.html#all_event_uncaughtexception). [`uncaughtException`](http://nodejs.org/docs/latest/api/all.html#all_event_uncaughtexception).
var ringbuffer = new bunyan.RingBuffer({ limit: 100 }); var ringbuffer = new bunyan.RingBuffer({ limit: 100 });
var log = new bunyan({ var log = new bunyan({
name: "foo", name: 'foo',
raw: true, streams: [{
type: 'raw',
stream: ringbuffer, stream: ringbuffer,
level: "debug" level: 'debug'
}]
}); });
log.info('hello world'); log.info('hello world');
@ -40,16 +42,18 @@
} }
var log = new Logger({ var log = new Logger({
name: 'mylog', name: 'mylog',
stream: new Collector(), streams: [{
raw: true type: 'raw',
stream: new Collector()
}]
}); });
See "examples/raw-stream.js". I expect raw streams to be useful for See "examples/raw-stream.js". I expect raw streams to be useful for
piping Bunyan logging to separate services (e.g. <http://log.ly>, piping Bunyan logging to separate services (e.g. <http://www.loggly.com/>,
<https://github.com/etsy/statsd>) or to separate in-process handling. <https://github.com/etsy/statsd>) or to separate in-process handling.
- Add test/corpus/*.log files (accidentally excluded) so that test suite - Add test/corpus/*.log files (accidentally excluded) so the test suite
works(!). actually works(!).
## bunyan 0.8.0 ## bunyan 0.8.0

View file

@ -10,8 +10,8 @@ Also: log4j is way more than you need.
# Current Status # Current Status
Basic functionality there. Still a fair amount of planned work, but I'm using Solid core functionality is there. Joyent is using this for a number of
it for some production services. production services.
Currently supports node 0.4+, but I'll probably make the jump to node 0.6+ as a Currently supports node 0.4+, but I'll probably make the jump to node 0.6+ as a
base soonish. base soonish.
@ -420,8 +420,7 @@ In general streams are specified with the "streams" option:
}) })
For convenience, if there is only one stream, it can specified with the For convenience, if there is only one stream, it can specified with the
"stream", "level" and "raw" options (internal converted to a "stream" and "level" options (internal converted to a `Logger.streams`):
`Logger.streams`):
var log = new Logger({ var log = new Logger({
name: "foo", name: "foo",
@ -434,19 +433,22 @@ If none are specified, the default is a stream on `process.stdout` at the
`Logger.streams` is an array of stream objects with the following attributes: `Logger.streams` is an array of stream objects with the following attributes:
- `type`: Typically implied. E.g. "stream" or "file". See supported types - `type`: One of "stream", "file" or "raw". See below. Often this is
below. implied from the other arguments.
- `path`: A file path for a file stream. If `path` is given and `type` is
not specified, then `type` will be set to "file".
- `stream`: This is the "Writable Stream", e.g. a std handle or an open - `stream`: This is the "Writable Stream", e.g. a std handle or an open
file write stream. file write stream. If `stream` is given and `type` is not specified, then
`type` will be set to "stream".
- `level`: The level at which logging to this stream is enabled. If not - `level`: The level at which logging to this stream is enabled. If not
specified it defaults to INFO. specified it defaults to INFO.
- `raw`: A boolean indicating if the `write()` method of this stream should
be given the raw log record object instead of the JSON-stringified
string. See "examples/raw-stream.js".
Supported stream types are: Supported stream types are:
- `stream`: A "stream" argument is given. - `stream`: A plain ol' node.js [Writable
Stream](http://nodejs.org/docs/latest/api/all.html#writable_Stream).
A "stream" (the writeable stream) value is required.
- `file`: A "path" argument is given. Bunyan will open this file for - `file`: A "path" argument is given. Bunyan will open this file for
appending. E.g.: appending. E.g.:
@ -463,6 +465,14 @@ Supported stream types are:
// Handle stream write or create error here. // Handle stream write or create error here.
}); });
- `raw`: Similar to a "stream" writeable stream, except that the write method
is given raw log record *Object*s instead of a JSON-stringified string.
This can be useful for hooking on further processing to all Bunyan logging:
pushing to an external service, a RingBuffer (see below), etc.
## RingBuffer Stream
Bunyan comes with a special stream called a RingBuffer which keeps the last N Bunyan comes with a special stream called a RingBuffer which keeps the last N
records in memory and does *not* write the data anywhere else. One common records in memory and does *not* write the data anywhere else. One common
strategy is to log 'info' and higher to a normal log file but log all records strategy is to log 'info' and higher to a normal log file but log all records
@ -475,10 +485,18 @@ To use a RingBuffer:
var bunyan = require('bunyan'); var bunyan = require('bunyan');
var ringbuffer = new bunyan.RingBuffer({ limit: 100 }); var ringbuffer = new bunyan.RingBuffer({ limit: 100 });
var log = new bunyan({ var log = new bunyan({
name: "foo", name: 'foo',
raw: true, streams: [
stream: ringbuffer, {
level: "debug" level: 'info',
stream: process.stdout
},
{
level: 'trace',
type: 'raw', // use 'raw' to get raw log record objects
stream: ringbuffer
}
]
}); });
log.info('hello world'); log.info('hello world');
@ -495,11 +513,13 @@ This example emits:
v: 0 } ] v: 0 } ]
# License # License
MIT. MIT.
# Future # Future
See "TODO.md". See "TODO.md".

View file

@ -27,7 +27,7 @@ var log = new Logger({
{ {
level: 'info', level: 'info',
stream: new MyRawStream(), stream: new MyRawStream(),
raw: true type: 'raw'
}, },
] ]
}); });

View file

@ -3,9 +3,11 @@ var bunyan = require('..');
var ringbuffer = new bunyan.RingBuffer({ limit: 100 }); var ringbuffer = new bunyan.RingBuffer({ limit: 100 });
var log = new bunyan({ var log = new bunyan({
name: 'foo', name: 'foo',
raw: true, streams: [{
type: 'raw',
stream: ringbuffer, stream: ringbuffer,
level: 'debug' level: 'debug'
}]
}); });
log.info('hello world'); log.info('hello world');

View file

@ -175,13 +175,17 @@ function resolveLevel(nameOrNum) {
* *
* @param options {Object} See documentation for full details. At minimum * @param options {Object} See documentation for full details. At minimum
* this must include a 'name' string key. Configuration keys: * this must include a 'name' string key. Configuration keys:
* - streams: specify the logger output streams. This is an array of * - `streams`: specify the logger output streams. This is an array of
* objects of the form: * objects with these fields:
* { * - `type`: The stream type. See README.md for full details.
* 'level': 'info', // optional, "info" default * Often this is implied by the other fields. Examples are
* 'stream': process.stdout, // 'stream' or "path" is required * "file", "stream" and "raw".
* 'closeOnExit': false // optional, default depends * - `level`: Defaults to "info".
* } * - `path` or `stream`: The specify the file path or writeable
* stream to which log records are written. E.g.
* `stream: process.stdout`.
* - `closeOnExit` (boolean): Optional. Default is true for a
* "file" stream when `path` is given, false otherwise.
* See README.md for full details. * See README.md for full details.
* - `level`: set the level for a single output stream (cannot be used * - `level`: set the level for a single output stream (cannot be used
* with `streams`) * with `streams`)
@ -300,6 +304,7 @@ function Logger(options, _childOptions, _childSimple) {
s.type = 'file' s.type = 'file'
} }
} }
s.raw = (s.type === 'raw'); // PERF: Allow for faster check in `_emit`.
if (s.level) { if (s.level) {
s.level = resolveLevel(s.level); s.level = resolveLevel(s.level);
@ -310,8 +315,6 @@ function Logger(options, _childOptions, _childSimple) {
self._level = s.level; self._level = s.level;
} }
s.raw = !!s.raw;
switch (s.type) { switch (s.type) {
case 'stream': case 'stream':
if (!s.closeOnExit) { if (!s.closeOnExit) {
@ -334,6 +337,11 @@ function Logger(options, _childOptions, _childSimple) {
} }
} }
break; break;
case 'raw':
if (!s.closeOnExit) {
s.closeOnExit = false;
}
break;
default: default:
throw new TypeError('unknown stream type "' + s.type + '"'); throw new TypeError('unknown stream type "' + s.type + '"');
} }
@ -362,8 +370,7 @@ function Logger(options, _childOptions, _childSimple) {
type: 'stream', type: 'stream',
stream: options.stream, stream: options.stream,
closeOnExit: false, closeOnExit: false,
level: (options.level ? resolveLevel(options.level) : INFO), level: (options.level ? resolveLevel(options.level) : INFO)
raw: (options.raw ? options.raw : false)
}); });
} else if (options.streams) { } else if (options.streams) {
options.streams.forEach(addStream); options.streams.forEach(addStream);
@ -372,8 +379,7 @@ function Logger(options, _childOptions, _childSimple) {
type: 'stream', type: 'stream',
stream: process.stdout, stream: process.stdout,
closeOnExit: false, closeOnExit: false,
level: (options.level ? resolveLevel(options.level) : INFO), level: (options.level ? resolveLevel(options.level) : INFO)
raw: (options.raw ? options.raw : false)
}); });
} }
if (options.serializers) { if (options.serializers) {
@ -683,8 +689,8 @@ Logger.prototype._emit = function (rec) {
for (i = 0; i < this.streams.length; i++) { for (i = 0; i < this.streams.length; i++) {
var s = this.streams[i]; var s = this.streams[i];
if (s.level <= level) { if (s.level <= level) {
xxx('writing log rec "%s" to "%s" stream (%s, %d <= %d): %j', xxx('writing log rec "%s" to "%s" stream (%d <= %d): %j',
obj.msg, s.type, (s.raw ? 'raw' : 'not raw'), s.level, level, obj); obj.msg, s.type, s.level, level, obj);
s.stream.write(s.raw ? obj : str); s.stream.write(s.raw ? obj : str);
} }
}; };

View file

@ -1,7 +1,7 @@
/* /*
* Copyright (c) 2012 Trent Mick. All rights reserved. * Copyright (c) 2012 Trent Mick. All rights reserved.
* *
* Test `raw: true` option on a Logger stream. * Test `type: 'raw'` Logger streams.
*/ */
var format = require('util').format; var format = require('util').format;
@ -26,7 +26,7 @@ test('raw stream', function (t) {
streams: [ streams: [
{ {
stream: new CapturingStream(recs), stream: new CapturingStream(recs),
raw: true type: 'raw'
} }
] ]
}); });
@ -40,24 +40,6 @@ test('raw stream', function (t) {
}); });
test('raw stream (short constructor)', function (t) {
var recs = [];
var log = new Logger({
name: 'raw-stream-test',
stream: new CapturingStream(recs),
raw: true
});
log.info('first');
log.info({two: 'deux'}, 'second');
t.equal(recs.length, 2);
t.equal(typeof (recs[0]), 'object', 'first rec is an object');
t.equal(recs[1].two, 'deux', '"two" field made it through');
t.end();
});
test('raw streams and regular streams can mix', function (t) { test('raw streams and regular streams can mix', function (t) {
var rawRecs = []; var rawRecs = [];
var nonRawRecs = []; var nonRawRecs = [];
@ -67,7 +49,7 @@ test('raw streams and regular streams can mix', function (t) {
streams: [ streams: [
{ {
stream: new CapturingStream(rawRecs), stream: new CapturingStream(rawRecs),
raw: true type: 'raw'
}, },
{ {
stream: new CapturingStream(nonRawRecs) stream: new CapturingStream(nonRawRecs)
@ -98,7 +80,7 @@ test('child adding a non-raw stream works', function (t) {
streams: [ streams: [
{ {
stream: new CapturingStream(parentRawRecs), stream: new CapturingStream(parentRawRecs),
raw: true type: 'raw'
} }
] ]
}); });
@ -107,7 +89,7 @@ test('child adding a non-raw stream works', function (t) {
streams: [ streams: [
{ {
stream: new CapturingStream(rawRecs), stream: new CapturingStream(rawRecs),
raw: true type: 'raw'
}, },
{ {
stream: new CapturingStream(nonRawRecs) stream: new CapturingStream(nonRawRecs)

View file

@ -11,7 +11,7 @@ var log1 = new Logger({
streams: [ streams: [
{ {
stream: ringbuffer, stream: ringbuffer,
raw: true, type: 'raw',
level: 'info' level: 'info'
} }
] ]