在R Markdown中使用logger包log_errors()函数时遇报错求助
我编写了一个使用logger包初始化日志的函数,将日志保存到R的tempdir()临时目录中:
init_logger <- function() { library(logger) temporary_directory <- tempdir() date_time_current <- format(Sys.time(), "log_%Y_%m_%d_%H_%M_%S_") tmp <- tempfile(pattern = date_time_current, tmpdir = temporary_directory, fileext = '.txt') configuration_logger(tmp) log_info('Logging starting:') return(tmp) } configuration_logger <- function(tmp){ log_appender(appender_file(tmp)) log_threshold(TRACE) log_formatter(formatter_paste) log_layout(layout_simple) log_errors() }
该函数在mypackage::get_data()中被调用,用于记录运行信息及错误。在常规R脚本中运行正常,能生成日志并记录错误;但在R Markdown中触发错误时,控制台报错:
Error in globalCallingHandlers(error = function(m) { : should not be called with handlers on the stack
且无法生成日志。若移除log_errors(),两类环境均能生成日志,但错误仅输出到控制台而不记录。请问为何仅在R Markdown中调用log_errors()会出问题,如何实现错误日志记录?
问题原因
R Markdown渲染时,knitr会自动注册全局错误处理函数来管控代码块的错误流程,保证渲染正常进行。而logger::log_errors()底层是通过globalCallingHandlers()注册新的全局错误处理器,但R的globalCallingHandlers()不允许在已有活跃错误处理栈的场景下注册新的全局处理器,因此在R Markdown环境中调用log_errors()会触发报错。
解决办法
方案1:局部捕获错误(推荐,避免全局冲突)
放弃使用全局错误处理器log_errors(),改为在get_data()函数内部用tryCatch捕获错误并记录,同时保留原错误的抛出逻辑,不影响R Markdown的错误显示:
修改mypackage::get_data()函数:
get_data <- function() { tryCatch({ # 原函数逻辑代码 # 示例:触发错误测试 stop("模拟数据获取失败") }, error = function(e) { # 记录错误到日志 log_error("get_data执行出错: {conditionMessage(e)}") # 重新抛出错误,保证R Markdown能正常捕获并显示 stop(e) }) }
方案2:条件注册全局错误处理器
如果必须使用全局错误处理,可以先检查当前是否已有活跃的全局错误处理器,仅在无冲突时注册:
修改configuration_logger()函数:
configuration_logger <- function(tmp){ log_appender(appender_file(tmp)) log_threshold(TRACE) log_formatter(formatter_paste) log_layout(layout_simple) # 检查是否已有全局错误处理器,无则注册 current_handlers <- globalCallingHandlers() has_error_handler <- any(sapply(current_handlers, function(h) "error" %in% names(h))) if (!has_error_handler) { globalCallingHandlers(error = function(e) { log_error("全局错误: {conditionMessage(e)}") stop(e) }) } else { log_info("检测到已有全局错误处理器,将通过局部捕获记录错误") } }
方案3:在R Markdown中局部包裹调用
在R Markdown的代码块中,用withCallingHandlers包裹get_data()的调用,局部捕获错误并记录:
# R Markdown代码块中调用 log_file <- init_logger() withCallingHandlers( mypackage::get_data(), error = function(e) { log_error("运行get_data时出错: {conditionMessage(e)}") } )
内容的提问来源于stack exchange,提问作者miyene

