如何 console.log node.js 中的堆栈跟踪错误?
How to console.log an error with stack trace in node.js?
我一直在尝试调试我的节点应用程序以在我的日志中找到错误的来源,该错误仅显示为“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
的 48=](包括 err.stack
)到控制台日志?我的理解是,与例如相比,我不会通过上述方式丢失任何信息。至:
console.log(err.stack);
但是像 this one 这样的 post 似乎另有建议(尽管链接的 post 现在已经更新)。
其实我更进一步,添加了一些相关的文字来帮助定位错误:
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 ...
}
我基本上始终遵循相同的模式。
你的模式看起来很常见,但我会说通常我不喜欢它,稍后再详细说明。
至于你的主要问题,根据你提供的内容很难回答。如果您显示实际代码而不是 "I generally follow this pattern",它可能会有所帮助。但同样有可能错误是在您没有预料到的地方抛出的,因此您的 console.log
根本没有被调用。
您似乎在寻找最佳实践,所以我将提供我认为目前为止发现的最佳实践。
首先,不要使用 console.log
进行记录。这并不可怕,但你可以做得更好。我最喜欢的是使用 morgan as middleware for logging request information, and debug 进行应用程序日志记录。
使用 debug
,您可以设置自定义日志级别,并以您想要的任何粒度级别收听您想要的任何级别。这一切都是通过设置 DEBUG 环境变量来控制的,在生产中你可以重定向到文件或你想要的任何其他目的地。此外,许多节点模块(包括 Express 和 Connect)在后台使用 Debug 作为它们的记录器,因此通过调整 DEBUG 变量,您可以根据需要查看或多或少的内部日志记录。 非常 有助于找出哪里出了问题。
其次,正如我所说,在路由方面我根本不使用您拥有的模式。我发现如果我不小心,很容易不小心发送 headers 不止一次,所以我的中间件总是 return 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)
});
请注意,这是最终错误处理程序的一个简单示例。我经常有几个这样的错误代码,我可能会根据应用程序的需要以不同的方式处理不同的错误代码。
我现在已经安装了 n,我可以确认以下内容:
节点 4.0.0
使用 console.log(err)
仅打印错误消息。
节点 7.7.0(最新)
使用 console.log(err)
打印错误消息和完整堆栈。
我已经确认此行为在版本 6.0.0 上发生了变化。因此,如果您使用旧版本,我建议您更新 Node.js 或使用 console.log(err.stack)
来打印完整堆栈。
我刚刚搞清楚是怎么回事,我希望这能帮助其他人避免这个初学者的错误。
对于我的一些错误记录,我使用了类似下面的东西,使用字符串连接来构造错误消息:
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
原封不动地处理,并作为一个整体转储。
这解释了为什么有时我没有看到错误的全部内容,正如我所期望的,而其他时候我却看到了。
至于其他库放在那里的控制台日志消息,还有一件事要检查是你没有在日志查看器中过滤掉日志消息的 'stack' 部分......结果是我was(为了节省日志配额...正在使用 papertrail)...d'oh。我这样做是通过过滤掉以 ____at
开头的任何行(四个空格后跟 'at'),例如 ____at Request.self.callback
.
我一直在尝试调试我的节点应用程序以在我的日志中找到错误的来源,该错误仅显示为“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
的 48=](包括 err.stack
)到控制台日志?我的理解是,与例如相比,我不会通过上述方式丢失任何信息。至:
console.log(err.stack);
但是像 this one 这样的 post 似乎另有建议(尽管链接的 post 现在已经更新)。
其实我更进一步,添加了一些相关的文字来帮助定位错误:
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 ...
}
我基本上始终遵循相同的模式。
你的模式看起来很常见,但我会说通常我不喜欢它,稍后再详细说明。
至于你的主要问题,根据你提供的内容很难回答。如果您显示实际代码而不是 "I generally follow this pattern",它可能会有所帮助。但同样有可能错误是在您没有预料到的地方抛出的,因此您的 console.log
根本没有被调用。
您似乎在寻找最佳实践,所以我将提供我认为目前为止发现的最佳实践。
首先,不要使用 console.log
进行记录。这并不可怕,但你可以做得更好。我最喜欢的是使用 morgan as middleware for logging request information, and debug 进行应用程序日志记录。
使用 debug
,您可以设置自定义日志级别,并以您想要的任何粒度级别收听您想要的任何级别。这一切都是通过设置 DEBUG 环境变量来控制的,在生产中你可以重定向到文件或你想要的任何其他目的地。此外,许多节点模块(包括 Express 和 Connect)在后台使用 Debug 作为它们的记录器,因此通过调整 DEBUG 变量,您可以根据需要查看或多或少的内部日志记录。 非常 有助于找出哪里出了问题。
其次,正如我所说,在路由方面我根本不使用您拥有的模式。我发现如果我不小心,很容易不小心发送 headers 不止一次,所以我的中间件总是 return 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)
});
请注意,这是最终错误处理程序的一个简单示例。我经常有几个这样的错误代码,我可能会根据应用程序的需要以不同的方式处理不同的错误代码。
我现在已经安装了 n,我可以确认以下内容:
节点 4.0.0
使用 console.log(err)
仅打印错误消息。
节点 7.7.0(最新)
使用 console.log(err)
打印错误消息和完整堆栈。
我已经确认此行为在版本 6.0.0 上发生了变化。因此,如果您使用旧版本,我建议您更新 Node.js 或使用 console.log(err.stack)
来打印完整堆栈。
我刚刚搞清楚是怎么回事,我希望这能帮助其他人避免这个初学者的错误。
对于我的一些错误记录,我使用了类似下面的东西,使用字符串连接来构造错误消息:
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
原封不动地处理,并作为一个整体转储。
这解释了为什么有时我没有看到错误的全部内容,正如我所期望的,而其他时候我却看到了。
至于其他库放在那里的控制台日志消息,还有一件事要检查是你没有在日志查看器中过滤掉日志消息的 'stack' 部分......结果是我was(为了节省日志配额...正在使用 papertrail)...d'oh。我这样做是通过过滤掉以 ____at
开头的任何行(四个空格后跟 'at'),例如 ____at Request.self.callback
.