R:捕获错误并在记录堆栈跟踪后继续执行(tryCatch没有可用的跟踪)

时间:2016-10-10 17:54:33

标签: r logging error-handling

我在服务器上运行的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:object&#39; variable.not.found&#39;找不到

     

无可追溯

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

  

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

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

我怎么能

  • 处理错误
  • 记录堆栈跟踪(追溯)以供进一步检查
  • [可选]不使用未保证在将来有效的未记录或隐藏的R内部函数?

THX很多!

2 个答案:

答案 0 :(得分:3)

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

要解决的主要问题

  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. 使用源代码行丰富堆栈跟踪。

  6. 解决方案概念

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

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

    3. 使用withCallingHandlers环绕tryCatch以捕获并处理所需的错误。

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

    5. 使用日志框架,例如: G。包last.dump

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

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

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

    9. 实施

      代码模板

      您应该使用此代码段:

      ,而不是使用source
      limitedLabels

      通过包(tryCatch

      重用实现

      我已经实现了一个包含上述所有概念的简单包。 它使用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

      用法:

      tryCatchLog

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

      https://github.com/aryoda/tryCatchLog

      你也可以futile.logger tryCatchLog function而不是使用完整的套餐。

      示例(演示)

      请参阅提供大量评论的demo file来解释其工作原理。

      参考

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

      其他有用的链接

      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

答案 1 :(得分:2)

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

t.r

当您将此文件导入R并运行f <- function() { x <- 1 g() } g <- function() { traceback(0) } 时,您将获得堆栈跟踪:

  

3:t.r#7的追溯(0)   2:g()at t.r#3
  1:f()

,其中包含每个条目的文件名和行号信息。您将获得源自f实现的多个堆栈帧,并且您不能通过为tryCatch指定非零参数来跳过它们,但实际上这将在{ {1}}更改。

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

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

对于记录常规错误,使用traceback有时很有用,然后不需要修改导致错误的代码。