这与其他一些问题有关,但我似乎不知道如何应用答案,所以我问一个新问题。
我试图从一段如下所示的代码中找出一个无信息的错误:
tryCatch(MainLoop(),
error=function(e) { fatal(lgr, paste('caught fatal error:', as.character(e)));
exit.status <<- 1 })
问题在于该错误似乎与隐藏在库函数中的某些内容有关:
Error in nrow(x): (subscript) logical subscript too long
那个
nrow
不在我的代码中,因为上面的 C 级错误仅适用于在我的任何 nrow
调用中从未发生过的索引类型。
所以我真的很想从其中获得堆栈跟踪
tryCatch
。这是一个类似的问题:
x <- function() { y(); }
y <- function() { z(); }
z <- function() { stop("asdf") }
> x()
Error in z() : asdf
> tryCatch(x(), error=function(e) { print(conditionCall(e)) } )
z()
> tryCatch(x(), error=function(e) { dump.frames() } )
> last.dump
$`tryCatch(x(), error = function(e) {
dump.frames()
})`
<environment: 0x1038e43b8>
$`tryCatchList(expr, classes, parentenv, handlers)`
<environment: 0x1038e4c60>
$`tryCatchOne(expr, names, parentenv, handlers[[1]])`
<environment: 0x1038e4918>
$`value[[3]](cond)`
<environment: 0x1038ea578>
attr(,"error.message")
[1] "asdf"
attr(,"class")
[1] "dump.frames"
如何获取包含对
y()
的调用的堆栈跟踪?我必须停止使用tryCatch
吗?有什么更好的办法吗?
对于交互式使用,可以在调用
trace(stop, quote(print(sys.calls())))
时打印调用堆栈。来自
stop()
,
?tryCatch
而
The function 'tryCatch' evaluates its expression argument in a
context where the handlers provided in the '...' argument are
available.
所以
Calling handlers are established by 'withCallingHandlers'...
the handler is called... in the context where the condition
was signaled...
如果存在内部 tryCatch,这会受到阻碍
> withCallingHandlers(x(), error=function(e) print(sys.calls()))
[[1]]
withCallingHandlers(x(), error = function(e) print(sys.calls()))
[[2]]
x()
[[3]]
y()
[[4]]
z()
[[5]]
stop("asdf")
[[6]]
.handleSimpleError(function (e)
print(sys.calls()), "asdf", quote(z()))
[[7]]
h(simpleError(msg, call))
Error in z() : asdf
因为我们只能在 tryCatch“处理”错误后才能访问调用堆栈。
我把它放在我的杂项包中,如果你想要文档,可以从那里使用它。
https://github.com/brry/berryFunctions/blob/master/R/tryStack.R
下一个 CRAN 版本计划很快发布,在那之前:
withCallingHandlers({
tryCatch(x(), error=function(e) stop("oops"))
}, error=function(e) print(sys.calls()))
这里供快速参考:
devtools::install_github("brry/berryFunctions")
# or use:
source("http://raw.githubusercontent.com/brry/berryFunctions/master/R/instGit.R")
instGit("brry/berryFunctions")
library(berryFunctions)
?tryStack
d
这是错误堆栈:<- tryStack(upper("4"))
tryStack(上部(“4”))
上(“4”)
下(b)
+ 10 中的错误:二元运算符的非数字参数
tryStack <- function(
expr,
silent=FALSE
)
{
tryenv <- new.env()
out <- try(withCallingHandlers(expr, error=function(e)
{
stack <- sys.calls()
stack <- stack[-(2:7)]
stack <- head(stack, -2)
stack <- sapply(stack, deparse)
if(!silent && isTRUE(getOption("show.error.messages")))
cat("This is the error stack: ", stack, sep="\n")
assign("stackmsg", value=paste(stack,collapse="\n"), envir=tryenv)
}), silent=silent)
if(inherits(out, "try-error")) out[2] <- tryenv$stackmsg
out
}
lower <- function(a) a+10
upper <- function(b) {plot(b, main=b) ; lower(b) }
d <- tryStack(upper(4))
d <- tryStack(upper("4"))
cat(d[2])
的粉丝。
evaluate::try_capture_stack()
x <- function() {
y()
}
y <- function() {
z()
}
z <- function() {
stop("asdf")
}
env <- environment()
e <- evaluate::try_capture_stack(quote(x()), env)
names(e)
#> [1] "message" "call" "calls"
e$calls
#> [[1]]
#> x()
#>
#> [[2]]
#> y()
#>
#> [[3]]
#> z()
#>
#> [[4]]
#> stop("asdf")
main <- function()
{
on.exit({
msg <- capture.output(traceback())
if (msg != "No traceback available ")
{
print(msg)
}
}
)
# rest of code
}
withCallingHandlers(
expr =
{
main()
},
error = function(e)
{
print(e)
}
)
),该函数应该包装任何其他函数 (
tryCapture(what, args, quote)
),将 what
传递给 args
,并捕获 what
的结果或任何错误。 无论哪种情况,都应该捕获任何警告。 不过,最重要的是确保任何错误和警告都报告完整的堆栈跟踪。@Martin Morgan 的答案被证明是我解决问题所需要的。
那些熟悉
what
(它包装函数并传递参数列表)的人会注意到我借用了参数语义——即
do.call()
是要包装的函数,what
是命名的参数列表,并且 args
确定参数是否被引用。动机是将 quote
放入脚本(例如,
tryCapture()
)中,可以使用 foo.R
从命令行调用。 这样,任何传递给 Rscript
的函数都可以在非交互式生产环境中执行,因为知道所有错误和警告都将以错误和/或警告及其堆栈跟踪可以写入日志的方式捕获文件或报告给网络钩子或数据库。在what
内,我的方法是将
tryCapture(what, args, quote)
包裹在do.call(what, args, quote)
内。 这样,关联的 withCallingHandlers()
处理程序可以将堆栈跟踪添加到任何警告的 warning
成员,保存修改后的警告,然后恢复处理。 关联的 $message
处理程序可以将堆栈跟踪添加到任何错误的 error
成员,然后抛出修改后的错误。通过将 $message
包装在
withCallingHandlers()
中,可以捕获并返回来自 tryCatch()
的任何错误(现在包括错误的 what
成员中的堆栈跟踪)。 因此,$message
将返回 tryCatch()
的结果(如果没有错误)或由 what
生成的错误(已修改以在关联的错误消息中包含堆栈跟踪)。最后,what
的结果可以与存储的警告组合在一个列表中,并且该列表从
tryCatch()
返回这是代码:
tryCapture()
为了测试该方法,我们可以想象一组依赖函数,它们将创建类似于 OP 的堆栈跟踪,可能如下所示:
tryCapture <- function(what, args = list, quote = FALSE) {
warning_list <- list()
store_warning_with_trace <- function(w) {
# the `head()` call removes four calls that represent error handling, so that
# the call list ends with the call that created the warning
calls <- head(sys.calls(), -4)
w$message <- makeConditionMessage(w$message, calls)
warning_list <<- c(warning_list, list(w))
invokeRestart("muffleWarning")
}
throw_error_with_trace <- function(e) {
# the `head()` call removes a call that represent error handling, so that
# the call list ends with the call that created the warning
calls <- head(sys.calls(), -1)
e$message <- makeConditionMessage(e$message, calls)
# raise the modified error to call the `error =` function in tryCatch()
stop(e)
}
echo_error <- function(e) e
result <-
tryCatch (
withCallingHandlers(
{
do.call(what, args, quote)
},
error = throw_error_with_trace,
warning = store_warning_with_trace
),
error = echo_error
)
list(result = result, warnings = warning_list)
}
如果我们调用
x <- function(characters, numeric) {
y(characters, numeric)
}
y <- function(chars, nums) {
z(chars, nums)
}
z <- function(cs, n) {
as.numeric(cs) + n
}
,
x(c("0", "1"), 2)
应该返回 z()
,没有警告或错误。如果我们调用 c(2,3)
,
x(c("a", "1"), 2)
应该返回 z()
,但带有警告,因为 c(NA, 3)
将返回 as.numeric(v)
,并带有关于 NA 因强制为数字而产生的警告。如果我们调用 c(NA, 1)
,
x(c("a", "1", "text")
应首先返回有关 NA 因强制转换为数字而产生的警告,然后返回错误,因为 z()
无法添加到 "text"
这是 c(NA, 1)
的实际应用,包含上述三个测试用例:
tryCapture()