Как console.log ошибка с трассировкой стека в node.js?

Я пытаюсь отлаживать приложение node, чтобы найти источник ошибки в моем журнале, который отображается только как "Error: Can't set headers after they are sent", без информации трассировки или какого-либо контекста.

Как бы то ни было, я думаю, что теперь я исправил это... Я использую connect-timeout, и я продолжал обрабатывать обратный вызов передается в асинхронную сетевую операцию, в результате которого обратный вызов в конечном итоге попытается выполнить res.send(), несмотря на то, что req.timedout был установлен на 'true' на connect-timeout во время сетевой операции.

НО я все еще не понимаю, почему мой журнал не показывал информацию о трассировке для этой ошибки. В любом случае, когда в моем коде возвращается ошибка, я заношу ее в консоль с помощью:

console.log(err);

Если в объекте err есть информация о трассировке, и это кажется помещенным в err.stack, не следует, чтобы приведенный выше оператор выгружал все содержимое err (включая err.stack) на консоль журнал? Мое понимание заключается в том, что я не потерял бы какую-либо информацию, выполнив вышеуказанное, сравненное, например, в:

console.log(err.stack);

Но сообщения, такие как этот, кажется, предлагают другое (хотя связанный пост теперь обновлен).

Я действительно пойду дальше и добавлю соответствующий текст, чтобы помочь найти ошибку:

console.log('error in dodgyFunction:', err);

Но несмотря на это, я все еще получал только "Error: Can't set headers after they are sent", без какого-либо контекста, который я бы сказал. Будет ли это потому, что это сообщение об ошибке консоли выводится во внешней библиотеке (например, express)? Я думал, что внешние библиотеки должны отправлять ошибки обратно в основной код, с которым нужно иметь дело соответственно?

Изменить: здесь пример того, где я поставил свою проверку ошибок и тайм-аута, в верхней части функции обратного вызова, переданной в операцию async:

var execFile = require('child_process').execFile;
execFile('dodgycommand', options, function(error, stdout, stderr) {
    if (req.timedout) {
        console.log('timeout detected whilst running dodgycommand, so aborting...');
        return;
    }
    if (error) {
        console.log('error running dodgycommand:', error);
        res.sendStatus(400);
        return;
    }

    // ... it safe to continue ...

}

В основном я придерживаюсь этой же схемы.

Ответ 1

Я только что решил, что происходит, и я надеюсь, что это поможет другим избежать этой ошибки начинающего.

Для некоторых моих журналов ошибок я использовал что-то вроде следующего, используя конкатенацию строк для создания сообщения об ошибке:

console.log('error in function abc: ' + err + ' whilst doing xyz');

тогда как в другом месте я использовал что-то вроде следующего, просто передав фрагменты сообщения об ошибке в виде отдельных аргументов console.log:

console.log('error in function xyz:', err, 'whilst doing abc');

Теперь я вижу, что они дают разные результаты!

Первый должен подстроить err так, чтобы он мог быть объединен с другими частями сообщения, и согласно this, в при этом он просто использует часть сообщения.

Однако в последнем виде объект err должен обрабатываться console.log unadulterated и сбрасываться как целое.

Это объясняет, почему я иногда не видел весь контент ошибки, как я ожидал, и другие времена, когда я был.

Что касается сообщений в консольном журнале, размещенных там другими библиотеками, что-то еще, чтобы проверить, что вы не отфильтровываете части "стека" сообщений журнала в вашем средстве просмотра журналов... оказывается, что я был (в порядке для сохранения на квоте журнала... я использую papertrail)... d'oh. Я делал это, отфильтровывая любые строки, начинающиеся с ____at (четыре пробела, за которыми следует 'at'), например ____at Request.self.callback.

Ответ 2

Ваш шаблон выглядит общепринятым, хотя я скажу, что, как правило, мне это не нравится, больше на секунду.

Что касается вашего основного вопроса, очень сложно ответить на него, основываясь на том, что вы предоставили. Если вы показываете фактический код, а не "я обычно следую этому шаблону", это может помочь. Но в равной степени возможно, что ошибка была выбрана где-то, чего вы не ожидали, и поэтому ваш console.log вообще не вызывался.

Похоже, вы ищете лучшие практики, поэтому я дам вам то, что, на мой взгляд, лучшее, что я нашел до сих пор.

Во-первых, не используйте console.log для ведения журнала. Это не ужасно, но вы можете сделать многое, намного лучше. Моим любимым является использование morgan в качестве промежуточного программного обеспечения для записи информации о запросе на журнал, а debug для ведения журнала приложений.

С помощью debug вы можете настроить собственные уровни журналов и прослушать любой уровень, который вы хотите, с любым уровнем детализации, который вы хотите. Все это контролируется установкой переменной среды DEBUG, и в процессе производства вы можете перенаправить файл или любой другой пункт назначения, который вы хотите. Кроме того, многие модули node (включая Express и Connect) используют Debug в качестве своего регистратора под капотом, поэтому, настраивая переменную DEBUG, вы можете видеть столько или меньше своего внутреннего ведения журнала, сколько хотите. очень полезно для выяснения того, что пошло не так.

Во-вторых, как я уже сказал, я не использую шаблон, который у вас есть, когда дело доходит до маршрутизации. Мне было легко случайно отправлять заголовки более одного раза, если я не осторожен, поэтому мое промежуточное ПО всегда возвращает next(), а ответы отправляются только в фактических обработчиках, что я могу быть уверенным только один раз. Когда дело доходит до ошибки, я всегда передаю next(e), который я могу обработать в функции обработчика ошибок. Я также создал библиотеку praeter, чтобы обеспечить стандартные ошибки на основе кодов состояния сети и общего обработчика ошибок.

Образец выглядит примерно так:

// middleware function to put something on the request object
app.use((req, res, next) => {
  MyModel.doSomething((e, thing) => {
    if (e) return next(e);
    if (!thing) return next(new NotFound()); // NotFound is an error in praeter that equates to a 404. 
    req.thing = thing;
    return next();
  });
});

Затем позже

// log in here is a reference to my debug configured log object
app.use((err, req, res, next) => {
  log.error(err);
  log.error(err.stack);
  return res.status(err.statusCode || 500).send(err.message)
});

Обратите внимание, что это простой пример конечного обработчика ошибок. У меня часто есть несколько из них, где я могу обрабатывать разные коды ошибок по-разному, в зависимости от потребностей приложения.

Ответ 3

Я установил n, и я могу подтвердить следующее:

Node 4.0.0

Использование console.log(err) выводит только сообщение об ошибке.

Node 7.7.0 (последняя)

Использование console.log(err) выводит сообщение об ошибке и полный стек.


Я подтвердил, что это поведение изменилось в версии 6.0.0. Итак, если вы используете более старую версию, я предлагаю вам обновить ваш Node.js или использовать console.log(err.stack) вместо этого, чтобы распечатать полный стек.