Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 1 addition & 73 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,77 +114,6 @@ function makeInstance(constructor, constructorOpt, args) {




/**
* built in bunyan serializer for restify errors. it's more or less the
* standard bunyan serializer with support for the context property. borrows
* liberally from:
* https://github.com/trentm/node-bunyan/blob/master/lib/bunyan.js
* @public
* @function serializer
* @param {Object} err an error object
* @returns {Object} serialized object for bunyan output
*/
function serializer(err) {

if (!err || !err.stack) {
return err;
}

function getSerializedContext(ex) {

var ret = '';

if (ex.context && _.keys(ex.context).length > -1) {
ret += ' (';
_.forEach(ex.context, function(val, key) {
ret += key + '=' + val.toString() + ', ';
});
// remove last comma
ret = ret.slice(0, -2);
ret += ')';
}

return ret + '\n';
}

function getFullErrorStack(ex) {
var e = ex;
var out = '';
var first = true;

do {
if (first !== true) {
out += '\nCaused by: ';
}

// parse out first new line of stack trace, append context
// there.
var stackString = (e.stack || e.toString()).split('\n');

out += stackString.shift() + getSerializedContext(e);
out += stackString.join('\n');
e = (e.cause) ? e.cause() : null;
first = false;
} while (e);

// remove last new line char
out = out.slice(0, -2);

return out;
}

return {
message: err.message,
name: err.name,
stack: getFullErrorStack(err),
code: err.code,
signal: err.signal
};
}



module.exports = _.assign({}, httpErrors, restErrors, {
// export base classes
HttpError: HttpError,
Expand All @@ -199,6 +128,5 @@ module.exports = _.assign({}, httpErrors, restErrors, {
codeToHttpError: makeErrFromCode,

// built in bunyan serializer
bunyanSerializer: serializer
bunyanSerializer: require('./serializer')
});

136 changes: 136 additions & 0 deletions lib/serializer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
'use strict';

// external modules
var _ = require('lodash');
var safeJsonStringify;

// try to require optional dependency
try {
safeJsonStringify = require('safe-json-stringify');
} catch (e) {
safeJsonStringify = null;
}


/**
* built in bunyan serializer for restify errors. it's more or less the
* standard bunyan serializer with support for the context property.
* @public
* @function serializer
* @param {Object} err an error object
* @returns {Object} serialized object for bunyan output
*/
function serializer(err) {

if (!err || !err.stack) {
return err;
}

return {
message: err.message,
name: err.name,
stack: getFullErrorStack(err),
code: err.code,
signal: err.signal
};
}


/**
* loop through all cause() errors and build a stack trace output
* @param {Object} err an error object
* @returns {String} stack trace string
*/
function getFullErrorStack(err) {
var e = err;
var out = '';
var first = true;

do {
if (first !== true) {
out += '\nCaused by: ';
}

// parse out first new line of stack trace, append context there.
var stackString = (e.stack || e.toString()).split('\n');

out += stackString.shift() + getSerializedContext(e);
out += stackString.join('\n');
e = (e.cause) ? e.cause() : null;
first = false;
} while (e);

// remove last new line char
out = out.slice(0, -2);

return out;
}


/* jscs:disable maximumLineLength */
/**
* serialize the error context object into a string. borrows liberally from
* bunyan's serializer:
* https://github.com/trentm/node-bunyan/blob/6fdc5ff20965b81ab15f8f408fe11917e06306f6/lib/bunyan.js#L865
* @param {Object} err an error object
* @return {String} serialized context obj
*/
/* jscs:enable maximumLineLength */
function getSerializedContext(err) {

var ret = '';

if (err.context && _.keys(err.context).length > 0) {
ret += ' (';
_.forEach(err.context, function(val, key) {
var stringVal;

try {
stringVal = JSON.stringify(val, safeCycles());
} catch (e) {
if (safeJsonStringify) {
stringVal = safeJsonStringify(val);
} else {
stringVal = 'unserializable! you can install ' +
'"safe-json"stringify" module for safer ' +
'stringification';
}
}

ret += key + '=' + stringVal + ', ';
});
// remove last comma
ret = ret.slice(0, -2);
ret += ')';
}

return ret + '\n';
}


/**
* copy pasta-ed from bunyan.
* A JSON stringifier that handles cycles safely.
* Usage: JSON.stringify(obj, safeCycles())
* @returns {Function}
*/
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;
};
}



module.exports = serializer;
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"restify": "^4.0.3",
"restify-clients": "^1.1.1"
},
"optionalDependencies": {
"safe-json-stringify": "^1.0.3"
},
"dependencies": {
"assert-plus": "^1.0.0",
"lodash": "^4.2.1",
Expand Down
3 changes: 2 additions & 1 deletion test/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"mocha": true
},
"rules": {
"no-unused-expressions": [ 0 ]
"no-unused-expressions": [ 0 ],
"no-undefined": [ 0 ]
}
}
20 changes: 20 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -771,5 +771,25 @@ describe('restify-errors node module.', function() {

done();
});

it('should handle circular refs', function(done) {

var a = {};
var b = { foo: a };
a.foo = b;

var err = new RestError({
message: 'boom',
context: a
});

assert.doesNotThrow(function() {
logger.error({
err: err
}, 'wrapped error');
});

done();
});
});
});