如何在node.js中使用stack trace记录错误?

drm*_*wer 6 javascript error-handling node.js console.log

我一直在尝试调试我的节点应用程序,以便在我的日志中找到错误的来源,只显示为" Error: Can't set headers after they are sent",没有跟踪信息或任何上下文.

碰巧,我想我现在已经解决了这个......我正在使用connect-timeout并且我正在继续处理传递给异步网络操作的回调res.send(),尽管req.timedout已经设置为'true' ,回调最终会尝试执行connect-timeout在网络运营期间.

但我仍然无法理解为什么我的日志没有显示此错误的跟踪信息.在我的代码中返回错误的任何地方我将其记录到控制台:

console.log(err);
Run Code Online (Sandbox Code Playgroud)

如果跟踪的可用信息err的对象,这似乎被放置在err.stack,不应该上述声明转储全部内容err(包括err.stack)到控制台日志?我的理解是,通过上述操作,我不会丢失任何信息,例如:

console.log(err.stack);
Run Code Online (Sandbox Code Playgroud)

但是像这样的帖子似乎暗示了其他情况(虽然链接的帖子现在已经更新).

我实际上更进一步,并添加一些相关的文本,以帮助找到错误:

console.log('error in dodgyFunction:', err);
Run Code Online (Sandbox Code Playgroud)

但尽管如此,我仍然只是" Error: Can't set headers after they are sent",没有任何上下文我会说.这是因为这个控制台错误消息是在外部库中输出的(比如express)?我认为外部库应该将错误发送回主代码进行相应的处理?

编辑:这是我将错误和超时检查放在传递给异步操作的回调函数顶部的示例:

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's safe to continue ...

}
Run Code Online (Sandbox Code Playgroud)

我基本上都遵循相同的模式.

Zan*_*non 8

我现在已经安装了n,我可以确认以下内容:

节点 4.0.0

使用console.log(err)仅打印错误消息。

节点 7.7.0(最新)

使用console.log(err)打印错误消息和完整堆栈。


我已经确认此行为在 6.0.0 版上发生了变化。因此,如果您使用旧版本,我建议您更新 Node.js 或使用console.log(err.stack)打印完整堆栈。


drm*_*wer 7

我刚刚弄清楚发生了什么,我希望这会帮助其他人避免这个初学者的错误.

对于我的一些错误记录,我使用类似下面的内容,使用字符串连接来构造错误消息:

console.log('error in function abc: ' + err + ' whilst doing xyz');
Run Code Online (Sandbox Code Playgroud)

而在其他地方,我使用类似下面的内容,只是将错误消息的各个部分作为单独的参数传递给console.log:

console.log('error in function xyz:', err, 'whilst doing abc');
Run Code Online (Sandbox Code Playgroud)

我现在看到这些给出了不同的结果!

前者必须进行字符串化,err以便它可以与消息的其他部分连接,并且根据这一点,在这样做时它只使用消息部分.

但是,在后一种形式中,err对象必须由console.log纯粹处理,并作为一个整体倾倒.

这就解释了为什么我有时没有看到错误的全部内容,正如我所期待的那样,有时我也是如此.

对于由其他库放置的控制台日志消息,要检查的其他内容是您没有过滤掉日志查看器中日志消息的"堆栈"部分...原来我(为了节省日志配额......我正在使用papertrail)... d'oh.我这样做是通过过滤掉任何以____at(四个空格后跟'at')开头的行____at Request.self.callback.


Pau*_*aul 5

你的模式看起来很普遍,不过我会说通常我不喜欢它,更多的是在一秒钟内.

至于你的主要问题,根据你提供的内容很难回答它.如果您显示实际代码而不是"我通常遵循此模式",它可能会有所帮助.但同样可能的是,错误被抛到了你没想到的地方,所以你console.log根本就没有被调用.

看起来你正在寻找最佳实践,所以我会告诉你我认为迄今为止我发现的最好的东西.

首先,不要console.log用于日志记录.这并不可怕,但你可以做得更好,更好.我最喜欢的是使用morgan作为中间件来记录请求信息,并调试应用程序日志记录.

随着debug您可以设置自定义日志级别,听你想和你想要的粒度任何级别的任何水平.它都是通过设置DEBUG环境变量来控制的,在生产中你可以重定向到文件或你想要的任何其他目的地.此外,许多节点模块(包括Express和Connect)使用Debug作为其记录器,因此通过调整DEBUG变量,您可以根据需要查看内部日志记录的多少. 对于弄清楚哪里出了问题非常有用.

其次,正如我所说,在路由方面,我根本不使用你所拥有的模式.如果我不小心的话,我发现很容易意外地多次发送标题,所以我的中间件总是返回next()并且响应仅在实际处理程序中发送,我可以确定只发送一次.当涉及到错误时,我总是传递next(e)我可以在错误处理函数中处理的内容.我还创建了praeter库,以根据Web状态代码和通用错误处理程序提供标准错误.

模式看起来像这样:

// 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();
  });
});
Run Code Online (Sandbox Code Playgroud)

然后

// 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)
});
Run Code Online (Sandbox Code Playgroud)

请注意,这是最终错误处理程序的简单示例.我经常有几个这样的地方,根据应用需求,我可能会以不同的方式处理不同的错误代码.

  • 实际上,我刚刚发现了一些关键的东西......对于我的一些错误记录,我使用了类似`console.log('dodgyFunction中的错误:'+ err)`而在其他地方我有`console.log('anotherDodgyFunction中的错误:',错误)`.我现在看到这些给出了不同的结果!前者必须将`err`字符串化,以便它可以与第一部分连接,并根据https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString使用只是"消息"部分.但是,后者必须由`console.log`进行处理,并且作为一个整体进行转储. (2认同)