Add log.level(...)
and log.levels(...)
API for changing logger stream levels.
This commit is contained in:
parent
632c044ad3
commit
352c4177b7
5 changed files with 181 additions and 16 deletions
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
## bunyan 0.5.0 (not yet released)
|
## 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
|
- Add `log.info(err)` special case for logging an `Error` instance. For
|
||||||
example `log.info(new TypeError("boom")` will produce:
|
example `log.info(new TypeError("boom")` will produce:
|
||||||
|
|
||||||
|
|
16
README.md
16
README.md
|
@ -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).
|
"fatal") and constants are defined for the (Logger.TRACE ... Logger.DEBUG).
|
||||||
The lowercase level names are aliases supported in the API.
|
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
|
# Log Record Fields
|
||||||
|
|
||||||
|
|
4
TODO.md
4
TODO.md
|
@ -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,
|
- Logger.setLevel()? How to change level for a given stream. Default all,
|
||||||
else, give an index... or type ... or support stream "names". Some positives
|
else, give an index... or type ... or support stream "names". Some positives
|
||||||
to stream names.
|
to stream names.
|
||||||
|
- service -> name
|
||||||
|
- 10, 20,...
|
||||||
- bunyan cli: more layouts (http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/EnhancedPatternLayout.html)
|
- 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
|
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
|
or whatever. Dap wants field width control for lining up. Hogan.js is
|
||||||
|
|
47
examples/level.js
Normal file
47
examples/level.js
Normal 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')
|
||||||
|
|
127
lib/bunyan.js
127
lib/bunyan.js
|
@ -22,6 +22,7 @@ var xxx = function xxx() {}; // uncomment to turn of debug logging
|
||||||
var os = require('os');
|
var os = require('os');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
var assert = require('assert');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,7 +56,7 @@ if (!format) {
|
||||||
}
|
}
|
||||||
return objects.join(' ');
|
return objects.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
var i = 1;
|
var i = 1;
|
||||||
var args = arguments;
|
var args = arguments;
|
||||||
var len = args.length;
|
var len = args.length;
|
||||||
|
@ -178,7 +179,7 @@ function Logger(options, _childOptions, _childSimple) {
|
||||||
if (! this instanceof Logger) {
|
if (! this instanceof Logger) {
|
||||||
return new Logger(options, _childOptions);
|
return new Logger(options, _childOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input arg validation.
|
// Input arg validation.
|
||||||
var parent;
|
var parent;
|
||||||
if (_childOptions !== undefined) {
|
if (_childOptions !== undefined) {
|
||||||
|
@ -194,14 +195,14 @@ function Logger(options, _childOptions, _childSimple) {
|
||||||
if ((options.stream || options.level) && options.streams) {
|
if ((options.stream || options.level) && options.streams) {
|
||||||
throw new TypeError('cannot mix "streams" with "stream" or "level" options');
|
throw new TypeError('cannot mix "streams" with "stream" or "level" options');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast path for simple child creation.
|
// Fast path for simple child creation.
|
||||||
if (parent && _childSimple) {
|
if (parent && _childSimple) {
|
||||||
// Single to stream close handling that this child owns none of its
|
// Single to stream close handling that this child owns none of its
|
||||||
// streams.
|
// streams.
|
||||||
this._isSimpleChild = true;
|
this._isSimpleChild = true;
|
||||||
|
|
||||||
this.level = parent.level;
|
this._level = parent._level;
|
||||||
this.streams = parent.streams;
|
this.streams = parent.streams;
|
||||||
this.serializers = parent.serializers;
|
this.serializers = parent.serializers;
|
||||||
this.src = parent.src;
|
this.src = parent.src;
|
||||||
|
@ -217,7 +218,7 @@ function Logger(options, _childOptions, _childSimple) {
|
||||||
// Null values.
|
// Null values.
|
||||||
var self = this;
|
var self = this;
|
||||||
if (parent) {
|
if (parent) {
|
||||||
this.level = parent.level;
|
this._level = parent._level;
|
||||||
this.streams = [];
|
this.streams = [];
|
||||||
for (var i = 0; i < parent.streams.length; i++) {
|
for (var i = 0; i < parent.streams.length; i++) {
|
||||||
var s = objCopy(parent.streams[i]);
|
var s = objCopy(parent.streams[i]);
|
||||||
|
@ -228,13 +229,13 @@ function Logger(options, _childOptions, _childSimple) {
|
||||||
this.src = parent.src;
|
this.src = parent.src;
|
||||||
this.fields = objCopy(parent.fields);
|
this.fields = objCopy(parent.fields);
|
||||||
} else {
|
} else {
|
||||||
this.level = Number.POSITIVE_INFINITY;
|
this._level = Number.POSITIVE_INFINITY;
|
||||||
this.streams = [];
|
this.streams = [];
|
||||||
this.serializers = null;
|
this.serializers = null;
|
||||||
this.src = false;
|
this.src = false;
|
||||||
this.fields = {};
|
this.fields = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
function addStream(s) {
|
function addStream(s) {
|
||||||
s = objCopy(s);
|
s = objCopy(s);
|
||||||
|
@ -254,8 +255,8 @@ function Logger(options, _childOptions, _childSimple) {
|
||||||
} else {
|
} else {
|
||||||
s.level = INFO;
|
s.level = INFO;
|
||||||
}
|
}
|
||||||
if (s.level < self.level) {
|
if (s.level < self._level) {
|
||||||
self.level = s.level;
|
self._level = s.level;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (s.type) {
|
switch (s.type) {
|
||||||
|
@ -324,7 +325,7 @@ function Logger(options, _childOptions, _childSimple) {
|
||||||
this.src = true;
|
this.src = true;
|
||||||
}
|
}
|
||||||
xxx("Logger: ", self)
|
xxx("Logger: ", self)
|
||||||
|
|
||||||
// Fields.
|
// Fields.
|
||||||
// These are the default fields for log records (minus the attributes
|
// These are the default fields for log records (minus the attributes
|
||||||
// removed in this constructor). To allow storing raw log records
|
// 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.
|
* 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;
|
applyKeys[keys[i]] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
xxx('_applySerializers: applyKeys', applyKeys);
|
xxx('_applySerializers: applyKeys', applyKeys);
|
||||||
|
|
||||||
// Check each serializer against these (presuming number of serializers
|
// Check each serializer against these (presuming number of serializers
|
||||||
// is typically less than number of fields).
|
// is typically less than number of fields).
|
||||||
Object.keys(this.serializers).forEach(function (name) {
|
Object.keys(this.serializers).forEach(function (name) {
|
||||||
|
@ -760,7 +853,13 @@ var errSerializer = Logger.stdSerializers.err = function err(err) {
|
||||||
//---- Exports
|
//---- Exports
|
||||||
|
|
||||||
module.exports = Logger;
|
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.VERSION = VERSION;
|
||||||
module.exports.LOG_VERSION = LOG_VERSION;
|
module.exports.LOG_VERSION = LOG_VERSION;
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue