【问题标题】:How to console.log an error with stack trace in node.js?如何使用 node.js 中的堆栈跟踪来控制台记录错误?
【发布时间】:2017-07-20 14:24:21
【问题描述】:

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

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

但我仍然不明白为什么我的日志没有显示此错误的跟踪信息。在我的代码中返回错误的任何地方,我都会将其记录到控制台:

console.log(err);

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

console.log(err.stack);

this one 之类的帖子似乎另有说明(尽管链接的帖子现已更新)。

我其实更进一步,添加一些相关的文字来帮助定位错误:

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

但尽管如此,我仍然只得到“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 ...

}

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

【问题讨论】:

  • 你的console.log()声明放在哪里?
  • @JyotmanSingh 我已经更新了我的问题,以举例说明我将console.log() 语句放在哪里
  • 好消息,您可以找到问题!现在,为了更好地帮助未来的读者(并遵循 Stack Overflow 规则),您可以从问题中删除 EDIT2 消息并将其作为答案发布。之后,将您的答案标记为正确答案。
  • 回答我自己的问题似乎是错误的,但我现在已经这样做了!
  • 顺便说一句,您应该考虑使用 console.error 而不是 console.log 以便它记录到 std:err

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


【解决方案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 处理,并作为一个整体转储。

这解释了为什么有时我没有看到错误的全部内容,正如我所期望的那样,而其他时候我却看到了。

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

【讨论】:

    【解决方案2】:

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

    节点 4.0.0

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

    节点 7.7.0(最新)

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


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

    【讨论】:

    • 有趣,非常有趣。谢谢。我正在使用节点v6.9.4,所以我应该只使用err 来获得完整的堆栈,这很好,因为这是我已经散布在我的代码中的内容。我怀疑我看到的无堆栈错误消息来自我的代码之外,只是没有任何堆栈可显示。
    • @drmrbrewer 如果第三方库使用throw "error message" 而不是像throw new Error("error message") 那样创建对象,您将无法找到堆栈跟踪。使用字符串设置 err 变量会导致无法检索原始堆栈跟踪。
    • 好的,谢谢,我开始对这一切有了更好的理解。让我有点困惑的是,如果我这样做,例如res.send() 上的 res 其标头已设置(已发送),那么显示控制台错误的会是 express 吗? express 不会转储完整的堆栈跟踪,还是被认为是不好的做法?也许我应该尝试@Paul 提到的“调试”模块,因为这可能会暴露express 的更多日志记录。
    • 有谁知道如何在 Node 7+ 中仅打印错误堆栈跟踪?当我尝试打印 error.stack 时,我仍然收到完整的错误消息(当我只想要堆栈时)。有趣的是,打印error.message 只会打印消息。好像最新节点中的error.stack 现在也包含错误消息?
    【解决方案3】:

    您的模式看起来很普遍,但我会说我通常不喜欢它,稍后再详细说明。

    至于您的主要问题,根据您提供的内容很难回答。如果您显示实际代码而不是“我通常遵循这种模式”,它可能会有所帮助。但是同样有可能错误被抛出到你没有预料到的地方,所以你的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)
    });
    

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

    【讨论】:

    • 感谢这个有用的答案。至于基本问题“console.log(err) 是否显示err.stack 以及err 中的所有其他内容?”,答案是什么?在我看来,记录err 比记录err.stack 更好,因为前者你得到了一切?或者console.log() 检查是否在错误对象中传递了什么,如果只传递了err,则只显示基本消息?
    • 不,当我做console.log(new Error('ouch')) 作为测试时,我得到了消息和完整的堆栈。当我执行 `console.log(new Error('ouch').stack) 时,我会得到完全相同的输出到控制台。正如我所说,我怀疑您看到的日志来自不是您的代码的地方。对于生产系统,始终记录完整堆栈并不是一个好主意(无论您使用哪种记录方法),因此我会以仅包含足够信息的标准格式显式模板化日志消息。使用日志级别,您只能在“详细”日志级别上记录堆栈。
    • 实际上,我刚刚发现了一些至关重要的东西......对于我的一些错误日志记录,我使用类似console.log('error in dodgyFunction: ' + err) 的东西,而在其他地方我使用console.log('error in anotherDodgyFunction:', err)。我现在看到这些给出了不同的结果!前者必须对err 进行字符串化,以便它可以与第一部分连接,根据developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…,这仅使用message 部分。但是,后者必须由console.log处理,不掺假,并作为一个整体倾倒。
    • 所以@Paul 我正在尝试实施关于在我的中间件中使用next() 的最佳实践(目前我没有)......我能澄清一下你所说的话:“所以我的中间件总是返回next() 并且响应仅在实际处理程序中发送”。因此,即使在成功请求之后,您也不会执行例如sendStatus(200) 在中间件本身中,但只是 return next() 并且有一个中间件在 之后 假设,因为它已经到达而不是错误处理程序,所以一切都必须正常并且它可以发送200 回复?可以通过req 传递信息(例如消息)吗?
    • 是的,通过将新属性附加到 req 来传递附加信息是可以的,这很常见。我的意思是总是传递下一个是我区分hatbi打算处理请求而不响应的函数(我使用app.use附加)和那些通过响应客户端实际处理请求的函数(我使用路由器附加和动词,如Router.get、router.post等
    猜你喜欢
    • 2012-03-22
    • 2018-10-21
    • 2010-12-29
    • 2022-12-23
    • 1970-01-01
    • 1970-01-01
    • 2017-10-24
    • 2021-02-07
    • 2011-12-04
    相关资源
    最近更新 更多