issue #2: Add guard on JSON.stringify
ing of log records before emission.
This commit is contained in:
parent
fd2bd3f77f
commit
1a0cd4be62
4 changed files with 109 additions and 4 deletions
22
CHANGES.md
22
CHANGES.md
|
@ -2,7 +2,27 @@
|
||||||
|
|
||||||
## bunyan 0.5.1 (not yet released)
|
## bunyan 0.5.1 (not yet released)
|
||||||
|
|
||||||
(nothing yet)
|
- Add guard on `JSON.stringify`ing of log records before emission. This will
|
||||||
|
prevent `log.info` et al throwing on record fields that cannot be
|
||||||
|
represented as JSON. An error will be printed on stderr and a clipped
|
||||||
|
log record emitted with a 'bunyanMsg' key including error details. E.g.:
|
||||||
|
|
||||||
|
bunyan: ERROR: could not stringify log record from /Users/trentm/tm/node-bunyan/examples/unstringifyable.js:12: TypeError: Converting circular structure to JSON
|
||||||
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"hostname": "banana.local",
|
||||||
|
"bunyanMsg": "bunyan: ERROR: could not stringify log record from /Users/trentm/tm/node-bunyan/examples/unstringifyable.js:12: TypeError: Converting circular structure to JSON",
|
||||||
|
...
|
||||||
|
|
||||||
|
Some timing shows this does effect log speed:
|
||||||
|
|
||||||
|
$ node tools/timeguard.js # before
|
||||||
|
Time try/catch-guard on JSON.stringify:
|
||||||
|
- log.info: 0.07365ms per iteration
|
||||||
|
$ node tools/timeguard.js # after
|
||||||
|
Time try/catch-guard on JSON.stringify:
|
||||||
|
- log.info: 0.07368ms per iteration
|
||||||
|
|
||||||
|
|
||||||
## bunyan 0.5.0
|
## bunyan 0.5.0
|
||||||
|
|
||||||
|
|
13
examples/unstringifyable.js
Normal file
13
examples/unstringifyable.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// See how bunyan behaves with an un-stringify-able object.
|
||||||
|
var Logger = require("../lib/bunyan");
|
||||||
|
|
||||||
|
var log = new Logger({src: true, name: "foo"});
|
||||||
|
|
||||||
|
// Make a circular object (cannot be JSON-ified).
|
||||||
|
var myobj = {
|
||||||
|
foo: 'bar'
|
||||||
|
};
|
||||||
|
myobj.myobj = myobj;
|
||||||
|
|
||||||
|
log.info({obj: myobj}, "hi there"); // <--- here
|
||||||
|
|
|
@ -108,6 +108,30 @@ function getCaller3Info() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warn about an bunyan processing error.
|
||||||
|
*
|
||||||
|
* If file/line are given, this makes an attempt to warn on stderr only once.
|
||||||
|
*
|
||||||
|
* @param msg {String} Message with which to warn.
|
||||||
|
* @param file {String} Optional. File path relevant for the warning.
|
||||||
|
* @param line {String} Optional. Line number in `file` path relevant for
|
||||||
|
* the warning.
|
||||||
|
*/
|
||||||
|
function _warn(msg, file, line) {
|
||||||
|
assert.ok(msg);
|
||||||
|
var key;
|
||||||
|
if (file && line) {
|
||||||
|
key = file + ':' + line;
|
||||||
|
if (_warned[key]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_warned[key] = true;
|
||||||
|
}
|
||||||
|
process.stderr.write(msg + '\n');
|
||||||
|
}
|
||||||
|
var _warned = {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---- Levels
|
//---- Levels
|
||||||
|
@ -524,7 +548,7 @@ Logger.prototype._applySerializers = function (fields, keys) {
|
||||||
try {
|
try {
|
||||||
fields[name] = self.serializers[name](fields[name]);
|
fields[name] = self.serializers[name](fields[name]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
process.stderr.write(format('bunyan: ERROR: This should never happen. '
|
_warn(format('bunyan: ERROR: This should never happen. '
|
||||||
+ 'This is a bug in <https://github.com/trentm/node-bunyan> or '
|
+ 'This is a bug in <https://github.com/trentm/node-bunyan> or '
|
||||||
+ 'in this application. Exception from "%s" Logger serializer: %s',
|
+ 'in this application. Exception from "%s" Logger serializer: %s',
|
||||||
name, err.stack || err));
|
name, err.stack || err));
|
||||||
|
@ -579,8 +603,27 @@ Logger.prototype._emit = function (rec) {
|
||||||
}
|
}
|
||||||
obj.v = LOG_VERSION;
|
obj.v = LOG_VERSION;
|
||||||
|
|
||||||
xxx('_emit: stringify this:', obj);
|
// Stringify the object. Attempt to warn/recover on error.
|
||||||
var str = JSON.stringify(obj) + '\n';
|
var str;
|
||||||
|
try {
|
||||||
|
str = JSON.stringify(obj) + '\n';
|
||||||
|
} catch (e) {
|
||||||
|
var src = ((obj.src && obj.src.file) ? obj.src : getCaller3Info());
|
||||||
|
var emsg = format("bunyan: ERROR: could not stringify log record from "
|
||||||
|
+ "%s:%d: %s", src.file, src.line, e);
|
||||||
|
var eobj = objCopy(rec[0]);
|
||||||
|
eobj.bunyanMsg = emsg;
|
||||||
|
eobj.msg = obj.msg;
|
||||||
|
eobj.time = obj.time;
|
||||||
|
eobj.v = LOG_VERSION;
|
||||||
|
_warn(emsg, src.file, src.line);
|
||||||
|
try {
|
||||||
|
str = JSON.stringify(eobj) + '\n';
|
||||||
|
} catch (e2) {
|
||||||
|
str = JSON.stringify({bunyanMsg: emsg});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.streams.forEach(function(s) {
|
this.streams.forEach(function(s) {
|
||||||
if (s.level <= level) {
|
if (s.level <= level) {
|
||||||
xxx('writing log rec "%s" to "%s" stream (%d <= %d)', obj.msg, s.type,
|
xxx('writing log rec "%s" to "%s" stream (%d <= %d)', obj.msg, s.type,
|
||||||
|
|
29
tools/timeguard.js
Executable file
29
tools/timeguard.js
Executable file
|
@ -0,0 +1,29 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
/*
|
||||||
|
* Time logging with/without a try/catch-guard on the JSON.stringify.
|
||||||
|
*/
|
||||||
|
|
||||||
|
console.log("Time try/catch-guard on JSON.stringify:");
|
||||||
|
|
||||||
|
var ben = require('ben'); // npm install ben
|
||||||
|
var Logger = require('../lib/bunyan');
|
||||||
|
|
||||||
|
var records = [];
|
||||||
|
function Collector() {
|
||||||
|
}
|
||||||
|
Collector.prototype.write = function(s) {
|
||||||
|
//records.push(s);
|
||||||
|
}
|
||||||
|
var collector = new Collector();
|
||||||
|
|
||||||
|
var log = new Logger({
|
||||||
|
name: "timeguard",
|
||||||
|
src: true,
|
||||||
|
stream: collector
|
||||||
|
});
|
||||||
|
|
||||||
|
var ms = ben(1e5, function () {
|
||||||
|
log.info("hi");
|
||||||
|
});
|
||||||
|
console.log(' - log.info: %dms per iteration', ms);
|
||||||
|
|
Loading…
Reference in a new issue