Как уменьшить трассировку стека при выдаче ошибки (указать на сайт вызова)

У меня есть такая функция:

function foo() {
  throw new Error('`foo` has been removed in favor of `bar`')
}

Когда кто-то звонит foo, я хочу, чтобы трассировка стека (вывод ошибки) указывала на сайт вызова foo, а не на строку throw внутри foo.

Например, я получаю следующее:

$ node test.js

/home/ubuntu/tmp/test.js:2
  throw new Error('`foo` has been removed in favor of `bar`')
        ^
Error: `foo` has been removed in favor of `bar`
    at foo (/home/ubuntu/tmp/test.js:2:9)
    at Object.<anonymous> (/home/ubuntu/tmp/test.js:5:1)
    ...

Как мне это получить?

$ node test.js

/home/ubuntu/tmp/test.js:5
  foo()
  ^
Error: `foo` has been removed in favor of `bar`
    at Object.<anonymous> (/home/ubuntu/tmp/test.js:5:1)
    ...

Ответ 1

Шаг 1: Определите пользовательский объект Error.. Для получения дополнительной информации: Строка не является ошибкой.

function CustomError (msg) {
  Error.call(this);

  // By default, V8 limits the stack trace size to 10 frames.
  Error.stackTraceLimit = 10;

  // Customizing stack traces
  Error.prepareStackTrace = function (err, stack) {
    return stack;
  };

  Error.captureStackTrace(this, arguments.callee);

  this.message = msg;
  this.name = 'CustomError';
};

CustomError.prototype.__proto__ = Error.prototype;

Шаг 2: Используйте Domain, чтобы поймать неперехваченную ошибку.

function foo() {
  throw new CustomError('`foo` has been removed in favorof `bar`');
};

var d = require('domain').create();

d.on('error', function(err) {
    /*
     * customize the output here.
     */
});

d.run(function() {
  foo();
});

Шаг 3. Настройте вывод. Трассировка структурированного стека представляет собой массив объектов CallSite, каждый из которых представляет собой стек стека. Объект CallSite определяет эти методы.

  for(var index=0; index<err.stack.length; index++){
    var frame = err.stack[index];

    var unit = frame.getFunctionName() || frame.getMethodName();
    if (unit === null) {
      unit = 'function()';
    } else {
      unit += '()'
    }

    if (index === 0) {
      console.error('%s:%d:%d\n  %s\n  ^',
        frame.getFileName(),
        frame.getLineNumber(),
        frame.getColumnNumber(),
        unit);

      console.error('Error: ' + err.message);

    } else {
      console.error('    at %s (%s:%d:%d)',
        unit,
        frame.getFileName(),
        frame.getLineNumber(),
        frame.getColumnNumber());
    };
  }; // END. stack trace

Запустите эту программу, и мы получим следующий вывод:

/home/ray/dev/test/error.js:57:9
  foo()
  ^
Error: `foo` has been removed in favorof `bar`
    at function() (/home/ray/dev/test/error.js:53:3)
    at b() (domain.js:183:18)
    at Domain.run() (domain.js:123:23)
    at function() (/home/ray/dev/test/error.js:52:3)
    at Module._compile() (module.js:456:26)
    at Module._extensions..js() (module.js:474:10)
    at Module.load() (module.js:356:32)
    at Module._load() (module.js:312:12)
    at Module.runMain() (module.js:497:10)