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) {
|
||||
case '%s': return String(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 '%';
|
||||
default:
|
||||
return x;
|
||||
|
@ -672,24 +672,7 @@ Logger.prototype._emit = function (rec) {
|
|||
// Stringify the object. Attempt to warn/recover on error.
|
||||
var str;
|
||||
if (this.haveNonRawStreams) {
|
||||
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});
|
||||
}
|
||||
}
|
||||
str = JSON.stringify(obj, safeCycles()) + '\n';
|
||||
}
|
||||
|
||||
for (i = 0; i < this.streams.length; i++) {
|
||||
|
@ -1022,6 +1005,21 @@ var errSerializer = Logger.stdSerializers.err = function err(err) {
|
|||
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
|
||||
|
|
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