172 lines
4.3 KiB
JavaScript
172 lines
4.3 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2014,2015,2016 Telefónica I+D
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
'use strict';
|
|
|
|
var isFunction = require('lodash').isFunction,
|
|
isObject = require('lodash').isObject,
|
|
util = require('util');
|
|
|
|
module.exports = function serializeErr(err) {
|
|
return {
|
|
toObject: toObject.bind(null, err),
|
|
toString: toString.bind(null, err)
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Serializes an error as a literal object
|
|
*
|
|
* @param {Error|*} error The error to serialize
|
|
* @param {Boolean} withStack Add the stack trace
|
|
* @returns {Object} The literal object ready to be serialized
|
|
*/
|
|
function toObject(error, withStack) {
|
|
var ret = formatObject(error);
|
|
|
|
var causes = getCauses(error).map(formatObject);
|
|
if (causes.length) {
|
|
ret.$$causes = causes;
|
|
}
|
|
|
|
if (withStack && error instanceof Error) {
|
|
ret.stack = formatTitle(error) + '\n' + getFullErrorStack(error);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Serializes an error as a String
|
|
*
|
|
* @param {Error|*} error The error to serialize
|
|
* @param {Boolean} withStack Add the stack trace
|
|
* @returns {Object} The string representation
|
|
*/
|
|
function toString(error, withStack) {
|
|
if (withStack && error instanceof Error) {
|
|
return formatTitleWithProperties(error) + '\n' + getFullErrorStack(error);
|
|
} else {
|
|
|
|
return [formatTitleWithProperties(error)]
|
|
.concat(getCauses(error).map(formatMessage))
|
|
.join(': ');
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This function gets error causes returned by error.cause()
|
|
* method. The error classes from
|
|
* [verror](https://github.com/davepacheco/node-verror)
|
|
* [restify v2.0](https://github.com/mcavage/node-restify)
|
|
* [therror v1.0](https://github.com/therror/therror)
|
|
* have them
|
|
*
|
|
* Inspired by `getFullErrorStack` from
|
|
* https://github.com/trentm/node-bunyan/blob/master/lib/bunyan.js
|
|
*/
|
|
function getCauses(error, acc) {
|
|
acc = acc || [];
|
|
error = error || {};
|
|
|
|
var cause = isFunction(error.cause) && error.cause();
|
|
if (cause) {
|
|
acc.push(cause);
|
|
return getCauses(cause, acc);
|
|
}
|
|
return acc;
|
|
}
|
|
|
|
function getFullErrorStack(error) {
|
|
return [formatStack(error)]
|
|
.concat(getCauses(error).map(function(err) {
|
|
var stack = formatStack(err);
|
|
return formatTitleWithProperties(err) + (stack.length ? '\n' + stack : '');
|
|
}))
|
|
.join('\nCaused by: ');
|
|
}
|
|
|
|
function getEnumerableProperties(error) {
|
|
|
|
return Object.keys(error || {}).reduce(function(memo, key) {
|
|
if (!isFunction(error[key])) {
|
|
// Dont want functions in the resulting object, like magic ones as toJSON
|
|
// that can cause a this module to not work
|
|
memo[key] = error[key];
|
|
}
|
|
return memo;
|
|
}, {});
|
|
}
|
|
|
|
function formatObject(error) {
|
|
if (!isObject(error)) {
|
|
var ret = {
|
|
message: formatTitle(error)
|
|
};
|
|
|
|
if (error && error.constructor) {
|
|
ret.constructor = error.constructor.name || error.constructor.prototype.name;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
var ret = getEnumerableProperties(error);
|
|
ret.message = formatMessage(error);
|
|
|
|
if (error.name) {
|
|
ret.name = error.name;
|
|
}
|
|
|
|
if (error.constructor) {
|
|
ret.constructor = error.constructor.name || error.constructor.prototype.name;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
function formatStack(error) {
|
|
if (error instanceof Error && error.stack != null) {
|
|
var stack = error.stack;
|
|
return stack.substring(stack.indexOf('\n') + 1);
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
function formatTitle(error) {
|
|
return '' + (error instanceof Error ?
|
|
(error.name ? error.name + ': ' : '') + error.message :
|
|
util.format(error));
|
|
}
|
|
|
|
function formatTitleWithProperties(error) {
|
|
var title = formatTitle(error);
|
|
if (error instanceof Error) {
|
|
var prop = getEnumerableProperties(error);
|
|
if (Object.keys(prop).length) {
|
|
title += ' ' + util.inspect(prop);
|
|
}
|
|
}
|
|
return title;
|
|
}
|
|
|
|
function formatMessage(error) {
|
|
return '' + (error instanceof Error ?
|
|
error.message :
|
|
util.format(error));
|
|
}
|