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

我正在 try catch 错误以记录它们并从错误中优雅地恢复,但我无法获得堆栈跟踪 (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...
})

运行上面的代码会产生以下输出:



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



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

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

  • 多谢!

    最佳答案

    抱歉回答太长,但我想在一个答案中总结所有知识和引用!

    需要解决的主要问题

  • tryCatch 将调用堆栈“展开”到 tryCatch 调用,以便 tracebacksys.calls 不再包含完整的堆栈跟踪,以识别导致错误或警告的源代码行。
  • tryCatch aborts the execution if you catch a warning 通过为 warning 条件传递处理函数。如果您只想记录警告,则无法正常继续执行。
  • dump.frames 写入堆栈跟踪的评估环境(帧)以允许事后调试(= 检查每个函数调用中可见的变量值),但 dump.frames "forgets" to save the workspace too if you set the parameter to.file = TRUE 。因此可能会丢失重要的对象。
  • 找到一个简单的日志框架,因为 R 不支持开箱即用的适当日志记录
  • 使用源代码行丰富堆栈跟踪。

  • 解决方案概念
  • 使用 withCallingHandlers 而不是 tryCatch 来获取指向引发错误或警告的源代码行的完整堆栈跟踪。
  • 仅在 withCallingHandlers 中(不在 tryCatch 中)捕获警告,因为它仅调用处理程序函数但不更改程序流程。
  • withCallingHandlers 环绕 tryCatch 以根据需要捕获和处理错误。
  • 使用 dump.frames 和参数 to.file = FALSE 将转储写入名为 last.dump 的全局变量,并通过调用 save.image 将其与全局环境一起保存到文件中。
  • 使用日志框架,例如。 G。 futile.logger 包。
  • R 会在您设置 options(keep.source = TRUE) 时跟踪源代码引用。您可以将此选项添加到您的 .Rprofile 文件中,或使用设置此选项的启动 R 脚本,然后 source 您的实际 R 脚本。
  • 要使用跟踪的源代码行丰富堆栈跟踪,您可以使用未记录(但广泛使用)的函数 limitedLabels
  • 要从堆栈跟踪中过滤掉 R 内部函数调用,您可以删除所有没有源代码行引用的调用。

  • 执行

    代码模板

    您应该使用以下代码片段,而不是使用 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 包提供了一个函数 futile.logger

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

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

    https://github.com/aryoda/tryCatchLog

    您还可以对 source function 进行 tryCatchLog,而不是使用完整的软件包。

    示例(演示)

    查看 demo file 提供了很多注释来解释它是如何工作的。

    引用

    其他 tryCatch 替换
  • 记录警告和错误,具有在 try catch 时执行多次尝试(重试)的功能,例如。 G。访问不可靠的网络驱动器:

    Handling errors before warnings in tryCatch
  • withJavaLogging 函数与其他包没有任何依赖关系,这也使用 limitedLabels 丰富了对调用堆栈的源代码引用:

    Printing stack trace and continuing after error occurs in R

  • 其他有用的链接

    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

    10-08 13:38