Add log.level(...) and log.levels(...) API for changing logger stream levels.

This commit is contained in:
Trent Mick 2012-02-06 15:04:47 -08:00
parent 632c044ad3
commit 352c4177b7
5 changed files with 181 additions and 16 deletions

View file

@ -2,6 +2,9 @@
## bunyan 0.5.0 (not yet released)
- Add `log.level(...)` and `log.levels(...)` API for changing logger stream
levels.
- Add `TRACE|DEBUG|INFO|WARN|ERROR|FATAL` level constants to exports.
- Add `log.info(err)` special case for logging an `Error` instance. For
example `log.info(new TypeError("boom")` will produce:

View file

@ -220,6 +220,22 @@ Integers are used for the actual level values (1 for "trace", ..., 6 for
"fatal") and constants are defined for the (Logger.TRACE ... Logger.DEBUG).
The lowercase level names are aliases supported in the API.
Here is the API for changing levels in an existing logger:
log.level() -> INFO // gets current level (lowest level of all streams)
log.level(INFO) // set all streams to level INFO
log.level("info") // set all streams to level INFO
log.levels() -> [DEBUG, INFO] // get array of levels of all streams
log.levels(0) -> DEBUG // get level of stream at index 0
log.levels("foo") // get level of stream with name "foo"
log.levels(0, INFO) // set level of stream 0 to INFO
log.levels(0, "info") // can use "info" et al aliases
log.levels("foo", WARN) // set stream named "foo" to WARN
# Log Record Fields

View file

@ -1,8 +1,8 @@
- line/file: possible to get quickly with v8? Yunong asked.
file/line/func (?)
- Logger.setLevel()? How to change level for a given stream. Default all,
else, give an index... or type ... or support stream "names". Some positives
to stream names.
- service -> name
- 10, 20,...
- bunyan cli: more layouts (http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/EnhancedPatternLayout.html)
Custom log formats (in config file? in '-f' arg) using printf or hogan.js
or whatever. Dap wants field width control for lining up. Hogan.js is

47
examples/level.js Normal file
View file

@ -0,0 +1,47 @@
// Play with setting levels.
//
// TODO: put this in a damn test suite
var Logger = require('../lib/bunyan'),
DEBUG = Logger.DEBUG,
INFO = Logger.INFO,
WARN = Logger.WARN;
var assert = require('assert');
// Basic usage.
var log = new Logger({
service: 'example-level',
streams: [
{
name: 'stdout',
stream: process.stdout,
level: 'debug'
},
{
name: 'stderr',
stream: process.stderr
}
]
});
assert.equal(log.level(), DEBUG);
assert.equal(log.levels()[0], DEBUG);
assert.equal(log.levels()[1], INFO);
assert.equal(log.levels(0), DEBUG);
assert.equal(log.levels(1), INFO);
assert.equal(log.levels('stdout'), DEBUG)
try {
log.levels('foo')
} catch (e) {
assert.ok(e.message.indexOf('name') !== -1)
}
log.trace("no one should see this")
log.debug("should see this once (on stdout)")
log.info("should see this twice")
log.levels('stdout', INFO)
log.debug("no one should see this either")
log.level('trace')
log.trace('should see this twice as 4th and 5th emitted log messages')

View file

@ -22,6 +22,7 @@ var xxx = function xxx() {}; // uncomment to turn of debug logging
var os = require('os');
var fs = require('fs');
var util = require('util');
var assert = require('assert');
@ -55,7 +56,7 @@ if (!format) {
}
return objects.join(' ');
}
var i = 1;
var args = arguments;
var len = args.length;
@ -178,7 +179,7 @@ function Logger(options, _childOptions, _childSimple) {
if (! this instanceof Logger) {
return new Logger(options, _childOptions);
}
// Input arg validation.
var parent;
if (_childOptions !== undefined) {
@ -194,14 +195,14 @@ function Logger(options, _childOptions, _childSimple) {
if ((options.stream || options.level) && options.streams) {
throw new TypeError('cannot mix "streams" with "stream" or "level" options');
}
// Fast path for simple child creation.
if (parent && _childSimple) {
// Single to stream close handling that this child owns none of its
// streams.
this._isSimpleChild = true;
this.level = parent.level;
this._level = parent._level;
this.streams = parent.streams;
this.serializers = parent.serializers;
this.src = parent.src;
@ -217,7 +218,7 @@ function Logger(options, _childOptions, _childSimple) {
// Null values.
var self = this;
if (parent) {
this.level = parent.level;
this._level = parent._level;
this.streams = [];
for (var i = 0; i < parent.streams.length; i++) {
var s = objCopy(parent.streams[i]);
@ -228,13 +229,13 @@ function Logger(options, _childOptions, _childSimple) {
this.src = parent.src;
this.fields = objCopy(parent.fields);
} else {
this.level = Number.POSITIVE_INFINITY;
this._level = Number.POSITIVE_INFINITY;
this.streams = [];
this.serializers = null;
this.src = false;
this.fields = {};
}
// Helpers
function addStream(s) {
s = objCopy(s);
@ -254,8 +255,8 @@ function Logger(options, _childOptions, _childSimple) {
} else {
s.level = INFO;
}
if (s.level < self.level) {
self.level = s.level;
if (s.level < self._level) {
self._level = s.level;
}
switch (s.type) {
@ -324,7 +325,7 @@ function Logger(options, _childOptions, _childSimple) {
this.src = true;
}
xxx("Logger: ", self)
// Fields.
// These are the default fields for log records (minus the attributes
// removed in this constructor). To allow storing raw log records
@ -399,6 +400,98 @@ Logger.prototype.child = function (options, simple) {
//}
/**
* Get/set the level of all streams on this logger.
*
* Get Usage:
* // Returns the current log level (lowest level of all its streams).
* log.level() -> INFO
*
* Set Usage:
* log.level(INFO) // set all streams to level INFO
* log.level("info") // can use "info" et al aliases
*/
Logger.prototype.level = function level(value) {
if (value === undefined) {
return this._level;
}
var newLevel = resolveLevel(value);
var len = this.streams.length;
for (var i = 0; i < len; i++) {
this.streams[i].level = newLevel;
}
this._level = newLevel;
}
/**
* Get/set the level of a particular stream on this logger.
*
* Get Usage:
* // Returns an array of the levels of each stream.
* log.levels() -> [TRACE, INFO]
*
* // Returns a level of the identified stream.
* log.levels(0) -> TRACE // level of stream at index 0
* log.levels("foo") // level of stream with name "foo"
*
* Set Usage:
* log.levels(0, INFO) // set level of stream 0 to INFO
* log.levels(0, "info") // can use "info" et al aliases
* log.levels("foo", WARN) // set stream named "foo" to WARN
*
* Stream names: When streams are defined, they can optionally be given
* a name. For example,
* log = new Logger({
* streams: [
* {
* name: 'foo', // <--- proposed new option, yucky controlling uniqueness
* path: '/var/log/my-service/foo.log'
* level: 'trace'
* },
* ...
*
* @param name {String|Number} The stream index or name.
* @param value {Number|String} The level value (INFO) or alias ("info").
* If not given, this is a 'get' operation.
* @throws {Error} If there is no stream with the given name.
*/
Logger.prototype.levels = function levels(name, value) {
if (name === undefined) {
assert.equal(value, undefined);
return this.streams.map(function (s) { return s.level });
}
var stream;
if (typeof name === 'number') {
stream = this.streams[name];
if (stream === undefined) {
throw new Error('invalid stream index: ' + name);
}
} else {
var len = this.streams.length;
for (var i = 0; i < len; i++) {
var s = this.streams[i];
if (s.name === name) {
stream = s;
break;
}
}
if (!stream) {
throw new Error(format('no stream with name "%s"', name));
}
}
if (value === undefined) {
return stream.level;
} else {
var newLevel = resolveLevel(value);
stream.level = newLevel;
if (newLevel < this._level) {
this._level = newLevel;
}
}
}
/**
* Apply registered serializers to the appropriate keys in the given fields.
*
@ -418,9 +511,9 @@ Logger.prototype._applySerializers = function (fields, keys) {
applyKeys[keys[i]] = true;
}
}
xxx('_applySerializers: applyKeys', applyKeys);
// Check each serializer against these (presuming number of serializers
// is typically less than number of fields).
Object.keys(this.serializers).forEach(function (name) {
@ -760,7 +853,13 @@ var errSerializer = Logger.stdSerializers.err = function err(err) {
//---- Exports
module.exports = Logger;
module.exports.TRACE = TRACE;
module.exports.DEBUG = DEBUG;
module.exports.INFO = INFO;
module.exports.WARN = WARN;
module.exports.ERROR = ERROR;
module.exports.FATAL = FATAL;
module.exports.VERSION = VERSION;
module.exports.LOG_VERSION = LOG_VERSION;