R:使用 tryCatchLog 进行错误处理 - 为控制台创建可自定义的结果代码并将详细的回溯写入日志文件

R: Errorhandling with tryCatchLog - create a customizable result code for the console and write a detailled traceback to log file

我的代码包含对不同参数值的多个初始检查。该代码是一个更大项目的一部分,该项目涉及多个 R 脚本以及来自其他环境的调用。如果一个参数值没有通过其中一个检查,我想

  1. 生成可自定义的结果代码
  2. 跳过剩余的代码(如果参数错误,无论如何都不会工作)
  3. 使用抛出错误的行创建一个日志条目(它告诉我参数未通过哪个测试)
  4. 将我的自定义结果代码打印到控制台(没有更详细的解释/从错误中追溯)

否则,剩下的代码应该是运行。如果还有其他错误(不是我抛出的),我还需要一个错误处理来生成可自定义的一般结果代码(表示有错误,但不是我抛出的)和更详细的日志。

结果代码是与更大环境通信的一部分,只是区分错误的参数值(即我抛出的错误)和其他内部问题(脚本稍后可能发生)。

我想使用 tryCatchLog 因为它允许我记录详细的回溯,包括脚本名称(我正在采购我自己的代码)和行号。然而,我还没有想出如何生成我自己的错误代码(目前我正在通过基本函数 stop() 执行此操作)并使用 tryCatchLog 传递它(同时还写日志)。

例子

在以下示例中,我的 parameter_check() 通过 stop() 抛出错误,结果代码为“400”。使用 tryCatchLog 我可以捕获错误并获得包含回溯的详细错误消息。但是,我想分开我自己的错误代码(只是“400”),它应该打印到控制台,以及更详细的错误消息,它应该进入日志文件。

library(tryCatchLog)

parameter_check <- function(error) {
  if (error){
    stop("400")
    print("This line should not appear")
  }
}

print("Beginning")
tryCatchLog(parameter_check(error = TRUE),
            error = function(e) {print(e)}
            )

print("End")

目前,结果是:

[1] "Beginn" ERROR [2021-12-08 11:43:38] 400 Compact call stack: 1 tryCatchLog(parameter_check(0), error = function(e) { 2 #3: stop("400") Full call stack: 1 tryCatchLog(parameter_check(0), error = function(e) { print(e) 2 tryCatch(withCallingHandlers(expr, condition =
cond.handler), ..., finall 3 tryCatchList(expr, classes, parentenv, handlers) 4 tryCatchOne(expr, names, parentenv, handlers[[1]]) 5 doTryCatch(return(expr), name, parentenv, handler) 6 withCallingHandlers(expr, condition = cond.handler) 7 parameter_check(0) 8 #3: stop("400") 9 .handleSimpleError(function (c) { if (inherits(c, "condition") <simpleError in parameter_check(0): 400>

我想获得自己的结果代码(“400”),以便在将完整的错误消息记录到文件中时将其打印到控制台。有没有不用写代码解析错误信息等的方法呢?

使用 tryCatch 的解决方案

根据 R Yoda and this answers 的提示,这是一个使用 tryCatch 和调用处理程序的解决方案。

### Parameters
log_file_location <- "./logs/log.txt"


### Defining functions
parameter_check_1 <- function(error) {
  if (error){
    stop("400")
  }
}

parameter_check_2 <- function(error) {
  if (error){
    stop("400")
  }
}

write_to_log <- function(file_location, message) {
  if (file.exists(file_location)) 
      {write(message, file_location, append = TRUE)}
      else 
      {write(message, file_location, append = FALSE)}
  
  
}
parameter_check <- function(){
  print("Beginning of parameter check")
  print("First check")
  parameter_check_1(error = TRUE)
  print("Second check")
  parameter_check_2(error = FALSE)
  print("End of parameter check")
  
}

main<- function() {
  print("Beginning of main function")
  log(-1) # throws warning
  log("error") # throws error
  print("End of main function")
}


### Setting parameters
result_code_no_error <- "200"
result_code_bad_request <- "400"
result_code_internal_error <- "500"

# initial value for result_code
result_code <- result_code_no_error


print("Beginning of program")

### Execute parameter check with tryCatch and calling handlers
# Error in parameter checking functions should result in result_code_bad_request
tryCatch(withCallingHandlers(parameter_check(), 
                             error = function(condition){},
                             warning = function(condition){
                                         write_to_log(log_file_location, condition$message)
                                         invokeRestart("muffleWarning")
                                         }
                             ),
         error = function(condition) { 
                    write_to_log(log_file_location, condition$message) 
                    result_code <<- result_code_bad_request
                   }
         )

### Execute main section with tryCatch and calling handlers
# Error in main section  should result in result_code_internal_error
# main section should only be excecuted if there is no error (internal or bad request) in the previous section

if (result_code == result_code_no_error) {
  tryCatch(withCallingHandlers(main(), 
                               error = function(condition){},
                               warning = function(condition){
                                           write_to_log(log_file_location, condition$message)
                                           invokeRestart("muffleWarning")
                                           }
                               ),
          error = function(condition) { 
                      write_to_log(log_file_location, condition$message) 
                      result_code <<- result_code_internal_error
                     }
          )
}


print("End of program")
print(result_code)

vignette for tryCatchLog 中所述,这样做的缺点是无法记录错误的准确位置。我没有传递来自 stop("400") 的错误消息,因为现在所有参数检查函数都在一个函数调用中,但这可以使用 condition$message.

解决方案是(完全独立于使用 tryCatchLog 或标准 R tryCatch):

...
error = function(e) {print(e$message)}
..

背景(R 错误如何工作):它们创建类型(错误)condition:

的对象
e <- simpleError("400") # same "condition" object as created by stop("400")
str(e)
# List of 2
# $ message: chr "400"
# $ call   : NULL
# - attr(*, "class")= chr [1:3] "simpleError" "error" "condition"
print(e$message)
[1] "400"