In 可以提供两个选项来记录 R 条件,例如带有 futile.logger 的警告并捕获所有警告,无论函数调用堆栈有多深:
- 使用
withCallingHandlers 包装您的代码以获得基本解决方案
- 使用我的包 tryCatchLog 获取高级解决方案(出于合规性原因:我是作者)
为了解释解决方案,我创建了一个简单的 R 脚本,它会产生警告和错误:
# Store this using the file name "your_code_file.R"
# This could be your code...
f1 <- function(value) {
print("f1() called")
f2(value) # call another function to show what happens
print("f1() returns")
}
f2 <- function(value) {
print("f2() called")
a <- log(-1) # This throws a warning: "NaNs produced"
print(paste("log(-1) =", a))
b <- log(value) # This throws an error if you pass a string as value
print("f2() returns")
}
f1(1) # produces a warning
f1("not a number") # produces a warning and an error
“按原样”执行(不记录)此代码会产生以下输出:
[1] "f1() called"
[1] "f2() called"
[1] "log(-1) = NaN"
[1] "f2() returns"
[1] "f1() returns"
[1] "f1() called"
[1] "f2() called"
[1] "log(-1) = NaN"
Error in log(value) : non-numeric argument to mathematical function
Calls: source -> withVisible -> eval -> eval -> f1 -> f2
In addition: Warning messages:
1: In log(-1) : NaNs produced
2: In log(-1) : NaNs produced
解决方案 1 (withCallingHandlers)
创建一个由 R 调用的新 R 文件并获取您的 未更改 (!) 原始 R 脚本:
# Store this using the file name "logging_injector_withCallingHandlers.R"
# Main function to inject logging of warnings without changing your original source code
library(futile.logger)
flog.threshold(INFO)
# Injecting the logging of errors and warnings:
tryCatch(withCallingHandlers({
source("your_code_file.R") # call your unchanged code by sourcing it!
}, error = function(e) {
call.stack <- sys.calls() # "sys.calls" within "withCallingHandlers" is like a traceback!
log.message <- e$message
flog.error(log.message) # let's ignore the call.stack for now since it blows-up the output
}, warning = function(w) {
call.stack <- sys.calls() # "sys.calls" within "withCallingHandlers" is like a traceback!
log.message <- w$message
flog.warn(log.message) # let's ignore the call.stack for now since it blows-up the output
invokeRestart("muffleWarning") # avoid warnings to "bubble up" to being printed at the end by the R runtime
})
, error = function(e) {
flog.info("Logging injector: The called user code had errors...")
})
如果你执行这个包装代码,R 输出是:
$ Rscript logging_injector_withCallingHandlers.R
NULL
[1] "f1() called"
[1] "f2() called"
WARN [2017-06-08 22:35:53] NaNs produced
[1] "log(-1) = NaN"
[1] "f2() returns"
[1] "f1() returns"
[1] "f1() called"
[1] "f2() called"
WARN [2017-06-08 22:35:53] NaNs produced
[1] "log(-1) = NaN"
ERROR [2017-06-08 22:35:53] non-numeric argument to mathematical function
INFO [2017-06-08 22:35:53] Logging injector: The called user code had errors...
如你所见
- 警告已记录
- 也可以输出调用堆栈(我已禁用此功能以避免淹没此答案)
参考:https://stackoverflow.com/a/19446931/4468078
解决方案2 - 包tryCatchLog(我是作者)
方案一有一些缺点,主要是:
- 堆栈跟踪(“traceback”)不包含文件名和行号
- 堆栈跟踪中充斥着您不想看到的内部函数调用(相信我,或者使用您的非平凡的 R 脚本尝试它 ;-)
我开发了一个包,将上面的withCallingHandlers逻辑封装在一个函数中,并添加了额外的功能,而不是一次又一次地复制和粘贴上面的代码sn-p
- 记录错误、警告和消息
- 通过使用对源文件名和行号的引用记录堆栈跟踪来识别错误和警告的来源
- 支持post-mortem分析错误后通过创建一个包含全局环境(工作空间)的所有变量和调用的每个函数(通过dump.frames)的转储文件 - 对于您无法在服务器上直接调试的批处理作业非常有用重现错误!
要使用tryCatchLog 包装上述 R 脚本文件,请创建一个包装文件
# Store this using the file name "logging_injector_tryCatchLog.R"
# Main function to inject logging of warnings without changing your original source code
# install.packages("devtools")
# library(devtools)
# install_github("aryoda/tryCatchLog")
library(tryCatchLog)
library(futile.logger)
flog.threshold(INFO)
tryCatchLog({
source("your_code_file.R") # call your unchanged code by sourcing it!
#, dump.errors.to.file = TRUE # Saves a dump of the workspace and the call stack named dump_<YYYYMMDD_HHMMSS>.rda
})
并通过Rscript 执行它以获得这个(缩短的!)结果:
# $ Rscript -e "options(keep.source = TRUE); source('logging_injector_tryCatchLog.R')" > log.txt
[1] "f1() called"
[1] "f2() called"
WARN [2017-06-08 23:13:31] NaNs produced
Compact call stack:
1 source("logging_injector_tryCatchLog.R")
2 logging_injector_tryCatchLog.R#12: tryCatchLog({
3 logging_injector_tryCatchLog.R#13: source("your_code_file.R")
4 your_code_file.R#18: f1(1)
5 your_code_file.R#6: f2(value)
6 your_code_file.R#12: .signalSimpleWarning("NaNs produced", quote(log(-1)))
Full call stack:
1 source("logging_injector_tryCatchLog.R")
2 withVisible(eval(ei, envir))
...
<a lot of logging output omitted here...>
正如您在调用堆栈级别 6 中清楚地看到的那样,源代码文件名和行号 (#12) 与引发警告的源代码 sn-p 一起记录为警告源:
6 your_code_file.R#12 .signalSimpleWarning("NaNs generated", quote(log(-1)))