/** * @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)); }