JSON.stringify safely
This commit is contained in:
parent
25fe445cff
commit
c2338b117c
2 changed files with 93 additions and 19 deletions
|
@ -69,7 +69,7 @@ if (!format) {
|
||||||
switch (x) {
|
switch (x) {
|
||||||
case '%s': return String(args[i++]);
|
case '%s': return String(args[i++]);
|
||||||
case '%d': return Number(args[i++]);
|
case '%d': return Number(args[i++]);
|
||||||
case '%j': return JSON.stringify(args[i++]);
|
case '%j': return JSON.stringify(args[i++], safeCycles());
|
||||||
case '%%': return '%';
|
case '%%': return '%';
|
||||||
default:
|
default:
|
||||||
return x;
|
return x;
|
||||||
|
@ -672,24 +672,7 @@ Logger.prototype._emit = function (rec) {
|
||||||
// Stringify the object. Attempt to warn/recover on error.
|
// Stringify the object. Attempt to warn/recover on error.
|
||||||
var str;
|
var str;
|
||||||
if (this.haveNonRawStreams) {
|
if (this.haveNonRawStreams) {
|
||||||
try {
|
str = JSON.stringify(obj, safeCycles()) + '\n';
|
||||||
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});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < this.streams.length; i++) {
|
for (i = 0; i < this.streams.length; i++) {
|
||||||
|
@ -1022,6 +1005,21 @@ var errSerializer = Logger.stdSerializers.err = function err(err) {
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A JSON stringifier that handles cycles safely.
|
||||||
|
// Usage: JSON.stringify(obj, safeCycles())
|
||||||
|
function safeCycles() {
|
||||||
|
var seen = [];
|
||||||
|
return function(key, val) {
|
||||||
|
if (!val || typeof val !== 'object') {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
if (seen.indexOf(val) !== -1) {
|
||||||
|
return '[Circular]';
|
||||||
|
}
|
||||||
|
seen.push(val);
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RingBuffer is a Writable Stream that just stores the last N records in
|
* RingBuffer is a Writable Stream that just stores the last N records in
|
||||||
|
|
76
test/cycles.test.js
Normal file
76
test/cycles.test.js
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2012 Trent Mick. All rights reserved.
|
||||||
|
*
|
||||||
|
* Make sure cycles are safe.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var test = require('tap').test;
|
||||||
|
var Logger = require('../lib/bunyan.js');
|
||||||
|
|
||||||
|
var Stream = require('stream').Stream;
|
||||||
|
var outstr = new Stream;
|
||||||
|
outstr.writable = true;
|
||||||
|
var output = [];
|
||||||
|
outstr.write = function (c) {
|
||||||
|
output.push(JSON.parse(c + ''));
|
||||||
|
};
|
||||||
|
outstr.end = function (c) {
|
||||||
|
if (c) this.write(c);
|
||||||
|
this.emit('end');
|
||||||
|
};
|
||||||
|
|
||||||
|
// these are lacking a few fields that will probably never match
|
||||||
|
var expect =
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "blammo",
|
||||||
|
"level": 30,
|
||||||
|
"msg": "bango { bang: 'boom', KABOOM: [Circular] }",
|
||||||
|
"v": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "blammo",
|
||||||
|
"level": 30,
|
||||||
|
"msg": "kaboom { bang: 'boom', KABOOM: [Circular] }",
|
||||||
|
"v": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "blammo",
|
||||||
|
"level": 30,
|
||||||
|
"bang": "boom",
|
||||||
|
"KABOOM": {
|
||||||
|
"bang": "boom",
|
||||||
|
"KABOOM": "[Circular]"
|
||||||
|
},
|
||||||
|
"msg": "",
|
||||||
|
"v": 0
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
var log = new Logger({
|
||||||
|
name: 'blammo',
|
||||||
|
streams: [
|
||||||
|
{
|
||||||
|
type: 'stream',
|
||||||
|
level: 'info',
|
||||||
|
stream: outstr
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cycles', function (t) {
|
||||||
|
outstr.on('end', function () {
|
||||||
|
output.forEach(function (o, i) {
|
||||||
|
t.has(o, expect[i], 'log item ' + i + ' matches');
|
||||||
|
});
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
var obj = { bang: 'boom' };
|
||||||
|
obj.KABOOM = obj;
|
||||||
|
log.info('bango', obj);
|
||||||
|
log.info('kaboom', obj.KABOOM);
|
||||||
|
log.info(obj);
|
||||||
|
outstr.end();
|
||||||
|
t.pass('did not throw');
|
||||||
|
});
|
Loading…
Reference in a new issue