【问题标题】:R: Catch errors and continue execution after logging the stacktrace (no traceback available with tryCatch)R:在记录堆栈跟踪后捕获错误并继续执行(tryCatch 没有可用的回溯)
【发布时间】:2017-02-19 05:10:12
【问题描述】:

我在服务器上运行了许多无人参与的 R 批处理作业,我必须在它们运行后分析作业失败。

我正在尝试捕获错误以记录它们并从错误中正常恢复,但我无法获取堆栈跟踪 (traceback) 以记录导致错误的 R 命令的代码文件名和行号.一个(愚蠢的)可重现的例子:

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...
})

运行上面的代码会产生这个输出:

f() 中的simpleError:找不到对象'variable.not.found'

没有可用的回溯

回溯不可用,原因记录在 R 帮助 (?traceback) 中:

通过 try 或 tryCatch 捕获的错误不会生成 回溯,所以打印的是最后一个调用序列 未捕获的错误,不一定是最后一个错误。

换句话说:使用tryCatch 捕获错误确实会杀死堆栈跟踪!

我该怎么做

  • 处理错误
  • 记录堆栈跟踪(回溯)以供进一步检查
  • [可选] 不使用无法保证在未来正常工作的未记录或隐藏的 R 内部函数?

非常感谢!

【问题讨论】:

    标签: r logging error-handling


    【解决方案1】:

    对不起,答案很长,但我想在一个答案中总结所有知识和参考资料!

    需要解决的主要问题

    1. tryCatch 将调用堆栈“展开”到tryCatch 调用,以便tracebacksys.calls 不再包含完整堆栈跟踪,以识别导致错误或警告的源代码行.

    2. tryCatch aborts the execution if you catch a warning 通过为warning 条件传递处理函数。如果您只想记录警告,则无法正常继续执行。

    3. dump.frames 写入堆栈跟踪的评估环境(帧)以允许事后调试(= 检查每个函数调用中可见的变量值)但 dump.frames "forgets" to save the workspace too if you set the parameter to.file = TRUE。因此,重要的对象可能会丢失。

    4. 寻找一个简单的日志框架,因为 R 不支持开箱即用的体面日志

    5. 使用源代码行丰富堆栈跟踪。

    解决方案概念

    1. 使用withCallingHandlers 而不是tryCatch 来获取指向引发错误或警告的源代码行的完整堆栈跟踪。

    2. 只在withCallingHandlers 中捕获警告(不在tryCatch 中),因为它只是调用处理程序函数但不会更改程序流程。

    3. tryCatch 包围withCallingHandlers 以根据需要捕获和处理错误。

    4. 使用dump.frames和参数to.file = FALSE将转储写入名为last.dump的全局变量中,并通过调用save.image将其与全局环境一起保存到文件中。

      李>
    5. 使用日志框架,例如。 G。包裹futile.logger

    6. 当您设置 options(keep.source = TRUE) 时,R 会跟踪源代码引用。您可以将此选项添加到您的 .Rprofile 文件中,或者使用设置此选项的启动 R 脚本和 source 您的实际 R 脚本。

    7. 要使用跟踪的源代码行丰富堆栈跟踪,您可以使用未记录(但广泛使用)的函数 limitedLabels

    8. 要从堆栈跟踪中过滤掉 R 内部函数调用,您可以删除所有没有源代码行引用的调用。

    实施

    代码模板

    而不是使用tryCatch,你应该使用这个代码sn-p:

    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 = "\n"))
        }
        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>
    }
    

    通过包实现可重用 (tryCatchLog)

    我已经实现了一个包含上述所有概念的简单包。 它使用futile.logger 包提供了一个函数tryCatchLog

    用法:

    library(tryCatchLog)  # or source("R/tryCatchLog.R")
    
    tryCatchLog(<expression>,
                error = function(e) {
                  <your error handler>
                })
    

    你可以在 github 上找到免费的源代码:

    https://github.com/aryoda/tryCatchLog

    您也可以source tryCatchLog function 而不是使用完整的软件包。

    示例(演示)

    请参阅demo file,它提供了很多 cmets 来解释它是如何工作的。

    参考文献

    其他tryCatch 替换

    其他有用的链接

    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中却如此复杂。通常在调用异常处理程序时,调用堆栈应该是完整的;只有在从错误中恢复时,才需要一些魔法。
    【解决方案2】:

    traceback 函数可用于打印/保存当前堆栈跟踪,但您必须指定一个整数参数,即从顶部省略的堆栈帧数(可以是 0)。这可以在 tryCatch 块内或其他任何地方完成。说这是文件t.r的内容:

    f <- function() {
        x <- 1
        g()
    }
    g <- function() {
        traceback(0)
    }
    

    当您将此文件导入 R 并运行 f 时,您将获得堆栈跟踪:

    3:t.r#7 处的回溯(0)
    2:g() 在 t.r#3
    1: f()

    其中包含每个条目的文件名和行号信息。您将获得来自tryCatch 实现的几个堆栈帧,并且您不能通过为traceback 指定非零参数来跳过它们,但如果tryCatch 的实现发生变化,这确实会中断。

    文件名和行号信息(源引用)仅适用于已解析以保留源引用的代码(默认情况下为source'd 代码,但不包括包)。堆栈跟踪将始终具有调用表达式。

    堆栈跟踪由traceback 打印(无需在其上调用print)。

    对于记录一般错误,有时使用options(error=) 很有用,这样就不需要修改导致错误的代码。

    【讨论】:

    • 感谢详细的答案。我的问题仍然是tracebacktryCatch 处理程序中为空,我只想在出现错误的情况下记录/打印回溯。如果您通过在导致错误的 f() 函数的末尾添加一行来修改您的示例(例如 stop("my error"))并通过 tryCatch(f(), error=function(e) print(traceback())) 调用 f(),您仍然只得到一个 "No traceback available "。我正在为此寻找解决方法。
    • 有趣的行为,我认为我们在 R 的 tracebacktryCatch 实现中发现了一个“肮脏的黑客”:虽然 traceback() 提供“没有可用的回溯”,但 traceback(0) 提供了一个完整的堆栈通过从空堆栈中省略零个元素来跟踪:-)
    • 仍然:traceback(0)tryCatch 的上下文中提供了错误的堆栈跟踪(不是错误位置的调用层次结构,而是sys.calls() 的调用层次结构 指向源文件中的错误行,试试看:f &lt;- function() { g() stop("error") } g &lt;- function() { print("g") } tryCatch(f(), error=function(e) traceback(0))
    • 这是记录在案的行为,“traceback()”给出最后一个未捕获错误的调用堆栈,“traceback(number)”给出当前堆栈。是的,你可以通过 sys.calls() 得到同样的结果。
    • Full ACK,记录了这种奇怪的语义:“默认显示的是最后一个未捕获错误的堆栈...如果 x 是数字,则打印当前堆栈”。感谢您的反馈意见!我目前正在玩withCallingHandlers,这是有希望的......
    猜你喜欢
    • 1970-01-01
    • 2018-08-15
    • 2013-02-23
    • 1970-01-01
    • 2013-02-21
    • 1970-01-01
    • 2018-07-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多