如何在node.js中使用堆栈跟踪调度出一个错误?

时间:2017-03-01 10:03:34

标签: javascript node.js error-handling console.log

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

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

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

connect-timeout

如果console.log(err); 对象中有可用的跟踪信息,并且这似乎放在err中,则上述语句不应转储{的整个内容 {1}}(包括err.stack)到控制台日志?我的理解是,通过上述操作,我不会丢失任何信息,例如,到:

err

但像Automatic Semicolon Insertion这样的帖子似乎暗示了其他情况(虽然链接的帖子现已更新)。

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

err.stack

但是尽管如此,我还是只得到了“console.log(err.stack); ”,而没有我提出的任何背景。这是因为此控制台错误消息是在外部库(如console.log('error in dodgyFunction:', err); )中输出的吗?我认为外部库应该将错误发送回主代码以进行相应的处理?

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

Error: Can't set headers after they are sent

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

3 个答案:

答案 0 :(得分:6)

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

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

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纯粹处理,并作为一个整体进行转储。

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

至于其他库放置的控制台日志消息,还有其他要检查的是你没有过滤掉日志查看器中日志消息的“堆栈”部分...结果我(为了节省日志配额......我正在使用papertrail)... d'oh。我这样做是通过过滤掉以____at开头的所有行(四个空格后跟'at'),例如____at Request.self.callback

答案 1 :(得分:5)

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

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

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

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

使用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();
  });
});

之后

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

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

答案 2 :(得分:3)

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

节点4.0.0

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

节点7.7.0 (最新)

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


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