UNPKG

snack-root-cause

Version:

Prints root cause error stack for async callback

100 lines (89 loc) 2.8 kB
var ROOT_CAUSE_NAME = 'root cause'; var _currentAsyncStack = null; // Wrap callback with stack info function wrapCallback(callback, name) { var e = new Error(name); e.name = ROOT_CAUSE_NAME; e.stack = e.stack + (_currentAsyncStack ? '\n' + _currentAsyncStack : ''); return function() { _currentAsyncStack = e.stack; return callback.apply(this, arguments); }; } /** * Format stack use origin stack array. * * @param {Error} e * @param {CallSite[]} stack * * Don't use "e.stack" directly in Error.prepareStackTrace. * It will cause "RangeError: Maximum call stack size exceeded" in node 0.10.x */ function formatStack(e, stack) { var lines = []; lines.push(e.name + (e.message ? ': ' + e.message : '')); stack.forEach(function(s) { // remove me from stack s = s.toString(); if (s.indexOf(__filename) === -1) { lines.push(' at ' + s); } }); return lines.join('\n'); } // Hook for v8-engine for new Error(...) Error.prepareStackTrace = function(e, stack) { if (e.name === ROOT_CAUSE_NAME) { return formatStack(e, stack); } else { return formatStack(e, stack) + (_currentAsyncStack ? '\n' + _currentAsyncStack : ''); } }; /** * Registers node.js async function with wrapped function. * * @example * * var _setTimeout = global.setTimeout; * * global.setTimeout = function(callback) { * var args = Array.prototype.slice.call(arguments); * args[0] = wrapCallback(callback, 'setTimeout'); * return _setTimeout.apply(this, args); * }; */ function registerFunction(object, name) { var originFn = object[name]; if (typeof originFn === 'function') { var fullName = name; if (object !== global && object.constructor.name) { fullName = object.constructor.name + '.' + name; } object[name] = function(callback) { var args = Array.prototype.slice.call(arguments); args[0] = wrapCallback(callback, fullName); return originFn.apply(this, args); }; } } // Sets the stack trace limit, default is 10 Error.stackTraceLimit = Infinity; // Registers global async function // jshint browser: true if (typeof window === 'object') { // in browser registerFunction(window, 'setTimeout'); registerFunction(window, 'setInterval'); registerFunction(window, 'setImmediate'); } else { // in node.js if (typeof global === 'object') { registerFunction(global, 'setTimeout'); registerFunction(global, 'setInterval'); registerFunction(global, 'setImmediate'); } if (typeof process === 'object') { registerFunction(process, 'nextTick'); registerFunction(process, 'nextDomainTick'); } }