【问题标题】:How to show error location in tryCatch?如何在 tryCatch 中显示错误位置?
【发布时间】:2017-03-30 12:12:34
【问题描述】:

在使用tryCatch 处理异常时,使用options(show.error.locations = TRUE) 显示错误位置似乎不起作用。我正在尝试显示错误的位置,但我不知道如何:

options(show.error.locations = TRUE)

tryCatch({
    some_function(...)
}, error = function (e, f, g) {
    e <<- e
    cat("ERROR: ", e$message, "\nin ")
    print(e$call) 
})

如果我再查看变量e,位置似乎不存在:

> str(e)
List of 2
 $ message: chr "missing value where TRUE/FALSE needed"
 $ call   : language if (index_smooth == "INDEX") {     rescale <- 100/meanMSI[plotbaseyear] ...
 - attr(*, "class")= chr [1:3] "simpleError" "error" "condition"

如果我没有捕获错误,它会与源文件和行号一起打印在控制台上。 tryCatch 怎么做?

【问题讨论】:

    标签: r exception-handling try-catch


    【解决方案1】:

    上下文

    正如 Willem van Doesburg 所指出的,无法使用 traceback() function 来显示 tryCatch() 发生错误的位置,据我所知,目前没有实用的方法来存储使用 tryCatch 时,R 中的基本函数出错。

    单独的错误处理程序的想法

    我找到的可能解决方案包括两部分,主要是编写一个类似于Chrispy from "printing stack trace and continuing after error occurs in R" 的错误处理程序,它会生成一个包含错误位置的日志。 第二部分是将这个输出捕获到一个变量中,类似于Ben Bolker in "is it possible to redirect console output to a variable" 的建议。

    R 中的调用堆栈似乎在引发错误然后处理时被清除(我可能错了,所以欢迎提供任何信息),因此我们需要在错误发生时捕获错误。

    脚本出错

    我使用了您之前关于 where 和 R error occured 的问题之一的示例,并将以下函数存储在名为“TestError.R”的文件中,我在下面的示例中调用了该文件:

    # TestError.R
    f2 <- function(x)
    {
        if (is.null(x)) "x is Null"
        if (x==1) "foo"
    }
    
    f <- function(x)
    {
      f2(x)
    }
    
    
    # The following line will raise an error if executed
    f(NULL)
    

    错误追踪功能

    这是我根据上面提到的 Chrispy 的代码改编的函数。 执行时,如果出现错误,下面的代码将打印发生错误的位置,在上述函数的情况下,它将打印: "Error occuring: Test.R#9: f2(x)""Error occuring: Test.R#14: f(NULL)" 表示错误是由于第 14 行的 f(NULL) 函数出现问题而导致的,该函数引用了第 9 行的 f2() 函数

    # Error tracing function
    withErrorTracing = function(expr, silentSuccess=FALSE) {
        hasFailed = FALSE
        messages = list()
        warnings = list()
    
        errorTracer = function(obj) {
    
            # Storing the call stack 
            calls = sys.calls()
            calls = calls[1:length(calls)-1]
            # Keeping the calls only
            trace = limitedLabels(c(calls, attr(obj, "calls")))
    
            # Printing the 2nd and 3rd traces that contain the line where the error occured
            # This is the part you might want to edit to suit your needs
            print(paste0("Error occuring: ", trace[length(trace):1][2:3]))
    
            # Muffle any redundant output of the same message
            optionalRestart = function(r) { res = findRestart(r); if (!is.null(res)) invokeRestart(res) }
            optionalRestart("muffleMessage")
            optionalRestart("muffleWarning")
        }
    
        vexpr = withCallingHandlers(withVisible(expr),  error=errorTracer)
        if (silentSuccess && !hasFailed) {
            cat(paste(warnings, collapse=""))
        }
        if (vexpr$visible) vexpr$value else invisible(vexpr$value)
    }
    

    存储错误位置和消息

    我们调用上面的脚本TestError.R 并将打印输出捕获到一个变量中,这里称为errorStorage,我们可以稍后处理或简单地显示。

    errorStorage <- capture.output(tryCatch({
        withErrorTracing({source("TestError.R")})
        }, error = function(e){
            e <<- e
            cat("ERROR: ", e$message, "\nin ")
            print(e$call)
    }))
    

    因此,我们将 e 的值与调用和消息以及错误位置的位置一起保留。 errorStorage 输出应如下所示:

    [1] "[1] \"Error occuring: Test.R#9: f2(x)\"    \"Error occuring: Test.R#14: f(NULL)\""
    [2] "ERROR:  argument is of length zero "                                        
    [3] "in if (x == 1) \"foo\""
    

    希望这可能会有所帮助。

    【讨论】:

    • 优秀的代码 sn-p :-) 我只想补充一点,只有通过两个选项启用此功能,您才能在调用堆栈中看到文件名和行号(对于包,您甚至必须在安装它们之前执行此操作):options(keep.source = TRUE); options(keep.source.pkgs = TRUE)。详情FAQ of the package tryCatchLog
    【解决方案2】:

    您可以在错误处理程序中使用 traceback() 来显示调用堆栈。 tryCatch 中的错误不会产生行号。另请参阅回溯中的help。如果您防御性地使用您的 tryCatch 语句,这将帮助您缩小错误的位置。

    这是一个工作示例:

    ## Example of Showing line-number in Try Catch
    
    # set this variable to "error", "warning" or empty ('') to see the different scenarios
    case <- "error"
    
    result <- "init value"
    
    tryCatch({
    
      if( case == "error") {
        stop( simpleError("Whoops:  error") )
      }
    
      if( case == "warning") {
        stop( simpleWarning("Whoops:  warning") )
      }
    
      result <- "My result"
    },
    warning = function (e) {
      print(sprintf("caught Warning: %s", e))
      traceback(1, max.lines = 1)
    },
    error = function(e) {
      print(sprintf("caught Error: %s", e))
      traceback(1, max.lines = 1)
    },
    finally = {
      print(sprintf("And the result is: %s", result))
    })
    

    【讨论】:

    • 我不太明白,“默认情况下traceback() 打印最后一个未捕获错误的调用堆栈”,如documentation of R 中所述,所以如果我们用@987654325 捕获错误@,使用traceback 不会将我们引导到发生错误的行(尝试在第 14 行故意调用一个错误的脚本,但它没有显示它)。
    • 确实,正如我提到的 tryCatch() 中的错误不会产生回溯。通过在 Catch 部分中调用 traceback(),您至少可以获得 catch 子句的行号。不幸的是,根据我的理解,这是最好的结果。
    • 不幸的是,这不是问题的答案。谢谢@PierreChevallier
    猜你喜欢
    • 2012-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多