R:在记录堆栈跟踪后捕获错误并继续执行(tryCatch 没有可用的回溯) [英] R: Catch errors and continue execution after logging the stacktrace (no traceback available with tryCatch)
问题描述
我在服务器上运行的 R 中有许多无人值守的批处理作业,我必须在它们运行后分析作业失败.
我正在尝试捕获错误以记录它们并从错误中优雅地恢复,但我无法获得堆栈跟踪 (traceback
) 来记录 R 的代码文件名和行号导致错误的命令.一个(愚蠢的)可重现的例子:
f <- function() {1 + variable.not.found # 愚蠢的错误}tryCatch( f(), error=function(e) {# 在这里我会记录错误消息和堆栈跟踪(回溯)print(e) # 报错信息没问题traceback() # 堆栈跟踪不起作用# 在这里,我将处理错误并恢复...})
运行上面的代码产生这个输出:
<块引用>f() 中的简单错误:未找到对象variable.not.found"
没有可用的回溯
回溯不可用,原因记录在 R 帮助 (?traceback
) 中:
通过 try 或 tryCatch 捕获的错误不会生成回溯,所以打印的是最后一个的调用序列未捕获的错误,不一定是最后一个错误.
换句话说:使用 tryCatch
捕获错误确实会终止堆栈跟踪!
我该怎么办
- 处理错误和
- 记录堆栈跟踪(回溯)以供进一步检查
- [可选] 不使用无法保证在未来工作的未记录或隐藏的 R 内部函数?
非常感谢!
抱歉回答太长,但我想在一个答案中总结所有知识和参考!
需要解决的主要问题
tryCatch
将调用堆栈展开"到tryCatch
调用,以便traceback
和sys.calls
不再包含完整的堆栈跟踪以识别导致错误或警告的源代码行.tryCatch
会在捕获时中止执行通过为warning
条件传递处理函数来发出警告.如果您只想记录警告,则无法正常继续执行.dump.frames
编写堆栈跟踪的评估环境(帧)以允许事后调试(= 检查每个函数调用中可见的变量值)但dump.frames
如果您设置参数to.file = TRUE
,忘记"也保存工作区.因此可能会丢失重要的对象.找到一个简单的日志框架,因为 R 不支持开箱即用的日志
使用源代码行丰富堆栈跟踪.
解决方案概念
使用
莉>withCallingHandlers
而不是tryCatch
来获取指向引发错误或警告的源代码行的完整堆栈跟踪.用
tryCatch
环绕withCallingHandlers
以根据需要捕获和处理错误.使用带有参数
to.file = FALSE
的dump.frames
将转储写入名为last.dump
的全局变量中并通过调用save.image
将其与全局环境一起保存到一个文件中.使用日志框架,例如.G.包
futile.logger
.R 会在您设置
options(keep.source = TRUE)
时跟踪源代码引用.您可以将此选项添加到您的.Rprofile
文件中,或使用设置此选项的启动 R 脚本,然后source
您的实际 R 脚本.要使用跟踪的源代码行丰富堆栈跟踪,您可以使用未记录(但广泛使用)的函数
limitedLabels
.要从堆栈跟踪中过滤掉 R 内部函数调用,您可以删除所有没有源代码行引用的调用.
实施
代码模板
您应该使用以下代码片段,而不是使用 tryCatch
:
library(futile.logger)试着抓(withCallingHandlers(<表达式>,错误 = 函数(e){call.stack <- sys.calls() # 就像withCallingHandlers"中的回溯转储.frames()save.image(file = "last.dump.rda")flog.error(paste(e$message, limitedLabels(call.stack), sep = "
"))}警告 = <类似于上面的错误>}error = <捕捉错误并像往常一样恢复># 警告 = <...># 永远不要在这里这样做,因为它会像错误一样停止正常执行!finally = <您的清理代码放在这里>}
通过包(tryCatchLog
)可重用的实现
我已经用上面提到的所有概念实现了一个简单的包.它使用 futile.logger
包提供了一个函数 tryCatchLog
.
用法:
library(tryCatchLog) # 或 source("R/tryCatchLog.R")tryCatchLog(<表达式>,错误 = 函数(e){<你的错误处理程序>})
你可以在github上找到免费的源代码:
https://github.com/aryoda/tryCatchLog
你也可以source
tryCatchLog
函数,而不是使用完整的包.
示例(演示)
查看提供大量评论的演示文件解释它是如何工作的.
参考文献
其他 tryCatch
替代品
使用在 try catch 时执行多次尝试(重试)的功能记录警告和错误,例如.G.用于访问不可靠的网络驱动器:
withJavaLogging
函数与其他包没有任何依赖关系,这也使用limitedLabels
丰富了对调用堆栈的源代码引用:
其他有用的链接
http://adv-r.had.co.nz/Exceptions-调试.html
在 R 中,为什么 withCallingHandlers 仍然停止执行?
如何继续运行R 中的 withCallingHandlers 抛出错误
options(error=dump.frames) vs. options(error=utils::恢复)
关于srcrefs"属性的背景信息 (Duncan Murdoch)
I have many unattended batch jobs in R running on a server and I have to analyse job failures after they have run.
I am trying to catch errors to log them and recover from the error gracefully but I am not able to get a stack trace (traceback
) to log the code file name and line number of the R command that caused the error. A (stupid) reproducible example:
f <- function() {
1 + variable.not.found # stupid error
}
tryCatch( f(), error=function(e) {
# Here I would log the error message and stack trace (traceback)
print(e) # error message is no problem
traceback() # stack trace does NOT work
# Here I would handle the error and recover...
})
Running the code above produces this output:
simpleError in f(): object 'variable.not.found' not found
No traceback available
The traceback is not available and the reason is documented in the R help (?traceback
):
Errors which are caught via try or tryCatch do not generate a traceback, so what is printed is the call sequence for the last uncaught error, and not necessarily for the last error.
In other words: Catching an error with tryCatch
does kill the stack trace!
How can I
- handle errors and
- log the stack trace (traceback) for further examination
- [optionally] without using undocumented or hidden R internal functions that are not guaranteed to work in the future?
THX a lot!
Sorry for the long answer but I wanted to summarize all knowledge and references in one answer!
Main issues to be solved
tryCatch
"unrolls" the call stack to thetryCatch
call so thattraceback
andsys.calls
do no longer contain the full stack trace to identify the source code line that causes an error or warning.tryCatch
aborts the execution if you catch a warning by passing a handler function for thewarning
condition. If you just want to log a warning you cannot continue the execution as normal.dump.frames
writes the evaluation environments (frames) of the stack trace to allow post-mortem debugging (= examining the variable values visible within each function call) butdump.frames
"forgets" to save the workspace too if you set the parameterto.file = TRUE
. Therefore important objects may be missing.Find a simple logging framework since R does not support decent logging out of the box
Enrich the stack trace with the source code lines.
Solution concept
Use
withCallingHandlers
instead oftryCatch
to get a full stack trace pointing to the source code line that throwed an error or warning.Catch warnings only within
withCallingHandlers
(not intryCatch
) since it just calls the handler functions but does not change the program flow.Surround
withCallingHandlers
withtryCatch
to catch and handle errors as wanted.Use
dump.frames
with the parameterto.file = FALSE
to write the dump into global variable namedlast.dump
and save it into a file together with the global environment by callingsave.image
.Use a logging framework, e. g. the package
futile.logger
.R does track source code references when you set
options(keep.source = TRUE)
. You can add this option to your.Rprofile
file or use a startup R script that sets this option andsource
your actual R script then.To enrich the stack trace with the tracked source code lines you can use the undocumented (but widely used) function
limitedLabels
.To filter out R internal function calls from stack trace you can remove all calls that have no source code line reference.
Implementation
Code template
Instead of using tryCatch
you should use this code snippet:
library(futile.logger)
tryCatch(
withCallingHandlers(<expression>,
error = function(e) {
call.stack <- sys.calls() # is like a traceback within "withCallingHandlers"
dump.frames()
save.image(file = "last.dump.rda")
flog.error(paste(e$message, limitedLabels(call.stack), sep = "
"))
}
warning = <similar to error above>
}
error = <catch errors and recover as you would do it normally>
# warning = <...> # never do this here since it stops the normal execution like an error!
finally = <your clean-up code goes here>
}
Reusable implementation via a package (tryCatchLog
)
I have implemented a simple package with all the concepts mentioned above.
It provides a function tryCatchLog
using the futile.logger
package.
Usage:
library(tryCatchLog) # or source("R/tryCatchLog.R")
tryCatchLog(<expression>,
error = function(e) {
<your error handler>
})
You can find the free source code at github:
https://github.com/aryoda/tryCatchLog
You could also source
the tryCatchLog
function instead of using a full blown package.
Example (demo)
See the demo file that provides a lot of comments to explain how it works.
References
Other tryCatch
replacements
Logging of warnings and errors with with a feature to perform multiple attempts (retries) at try catch, e. g. for accessing an unreliable network drive:
withJavaLogging
function without any dependencies to other packages which also enriches the source code references to the call stack usinglimitedLabels
:
Other helpful links
http://adv-r.had.co.nz/Exceptions-Debugging.html
A Warning About warning() - avoid R's warning feature
In R, why does withCallingHandlers still stops execution?
How to continue function when error is thrown in withCallingHandlers in R
Can you make R print more detailed error messages?
How can I access the name of the function generating an error or warning?
How do I save warnings and errors as output from a function?
options(error=dump.frames) vs. options(error=utils::recover)
General suggestions for debugging in R
Suppress warnings using tryCatch in R
R Logging display name of the script
Background information about the "srcrefs" attribute (Duncan Murdoch)
get stack trace on tryCatch'ed error in R
这篇关于R:在记录堆栈跟踪后捕获错误并继续执行(tryCatch 没有可用的回溯)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!