【问题标题】:Get all R code which is run when running获取运行时运行的所有 R 代码
【发布时间】:2015-05-14 15:37:46
【问题描述】:

假设我在脚本中有一堆 R 代码,我想将所有从 .GlobalEnv 运行的 R 代码连同错误和警告消息一起记录到平面文件或数据库中。 我可以编写一个简单的 logme 函数,如下所示,或者通过更改 options(error = mylogginfunction) 使其更复杂以获取错误。

mylogfile <- tempfile()
logme <- function(x){
  mode <- "at"
  if(!file.exists(mylogfile)){
    mode <- "wt"
  }
  myconn <- file(mylogfile, mode)
  writeLines(x, myconn)
  close(myconn)
  invisible()
}

logme(sprintf("%s: started some yadayada, ", Sys.time()))
x <- 10
x * 7
logme(sprintf("%s: done with yadayada", Sys.time()))

## Get the log
cat(readLines(mylogfile))

日志打印出来: 2015-05-14 17:24:31:开始了一些 yadayada,2015-05-14 17:24:31:完成了 yadayada

但我想要的是日志文件写下执行的表达式,而我不必为每个语句编写一个包装器。 我希望日志看起来像。 2015-05-14 17:24:31: 开始了一些 yadayada, x 2015-05-14 17:24:31 : 用 yadayada 完成

所以我的问题是,我如何获取 R 正在执行的内容,以便我可以将执行的表达式存储在日志/数据库中。并且不必在每个表达式之前编写函数调用(如 myhandler(x

【问题讨论】:

  • R CMD BATCH filename.R 命令行会生成一个 .Rout 文件,其中包含您所要求的内容。
  • 我对 .Rout 或 Rscript myscript > mylog.log 传输的内容不感兴趣。我对表达式很感兴趣。
  • R CMD BATCH 也写入表达式。你试过了吗?
  • 是的,我试过 R CMD BATCH。但我需要运行的表达式,以便我可以将它们一一存储在日志数据库中,因为它们由 R 解释器运行。
  • 那么您是在寻找 Sweave 还是 knitr?

标签: r try-catch eval


【解决方案1】:

要捕获输入命令,您可以使用 addTaskCallback

mylogfile <- tempfile()
addTaskCallback(
    function(...) { 
        expr <- deparse(as.expression(...)[[1]]) # it could handled better...
        cat(expr, file=mylogfile, append=TRUE, sep="\n")
        # or cat(sprintf("[%s] %s", Sys.time(), expr),...) if you want timestamps
        TRUE
    }
    ,name="logger"
)

x <- 10
x * 7

removeTaskCallback("logger")

那么结果是:

cat(readLines(mylogfile), sep="\n")
... addTaskCallback definition ...
x <- 10
x * 7

但是你得到的是 parsed 表达式,这意味着那一行

x+1;b<-7;b==2

将被记录为

x + 1
b <- 7
b == 2

另外:

  • 不会记录输出,尤其是控制台中显示的messagewarning
  • 如果error日志不会被触发,所以你需要单独的函数来处理它

【讨论】:

    【解决方案2】:

    这可能在每种情况下都很简单,但您可以尝试这样做:

    将 myhandler 定义为:

    myhandler <- function(x, file = stdout()) {
      expr <- substitute(x)
      for(e_line in as.list(expr)) {
        cat( file = file, as.character(Sys.time()), capture.output(e_line), "\n")
        eval(e_line, envir = parent.frame())
      }
    }
    

    将它与括号内的代码一起使用:

    myhandler({
    
      a <- 1
      a <- a + 1
      print(a)
    
    })
    

    结果:

    # 2015-05-14 18:46:34 `{` 
    # 2015-05-14 18:46:34 a <- 1 
    # 2015-05-14 18:46:34 a <- a + 1 
    # 2015-05-14 18:46:34 print(a) 
    # [1] 2
    

    【讨论】:

    • 非常感谢您的回答。这与我的想法很接近,但我试图在我的问题中明确排除这个答案,它指出“不必在每个表达式之前编写函数调用”。而这个解决方案只是将所有表达式放在 1 个块中。我正在寻找一个在所有代码之前不需要 myhandler 的解决方案。
    【解决方案3】:

    我承认,当我们在 cmets 中聊了几句时,我并没有真正理解“在运行 R 命令的同一进程中运行表达式”是什么意思。但是,我扩展了我的想法。您可以使用以下行创建一个logGenerator.R 文件:

    logGenerator<-function(sourcefile,log) {
      ..zz <- file(log, open = "at")
      sink(..zz)
      sink(..zz, type = "message")
      on.exit({
        sink(type="message")
        sink()
        close(..zz)
      })
      ..x<-parse(sourcefile)
      for (..i in 1:length(..x)) {
        cat(as.character(Sys.time()),"\n")
        cat(as.character(..x[..i]),"\n")
        ..y<-eval(..x[..i])
      }
    }
    

    此函数将源文件和日志文件名作为参数。该脚本将获取一个 R 文件,并将记录每条指令的执行时间。然后它将表达式记录在同一个日志文件中。每个输出定向到stdout(),错误消息定向到日志文件。您显然不必以任何方式修改您的源文件。

    【讨论】:

    • 尼古拉,谢谢你的回答。基本上和bergant给出的一样。我正在寻找不需要传递源文件的解决方案。这可能吗?
    • 如果不在源文件中,您认为如何传递要评估和存储的表达式?很抱歉回答,但我认为我并没有真正了解您的限制以及您的工作流程。
    • 工作流程是 logme(sprintf("%s: started some yadayada, ", Sys.time())) x &lt;- 10 x * 7 logme(sprintf("%s: done with yadayada", Sys.time())) 这应该会产生一个日志文件说明 '2015-05-14 17:24:31: started some yadayada, x
    • 我不清楚。 “我不需要传递源文件”是什么意思?我猜我的函数会产生你所要求的输出。
    • 我正在寻找一种解决方案,我不必编写 logGenerator('myRscript.R', 'mylog.log') 但只需 logme("mylog.log") and next logme(sprintf("%s: started some yadayada, ", Sys.time())) x &lt;- 10 x * 7 logme(sprintf("%s: done with yadayada", Sys.time())) 即可生成所需的问题输出.所以我的代码没有包装器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-27
    • 1970-01-01
    • 1970-01-01
    • 2017-12-16
    • 2011-11-11
    相关资源
    最近更新 更多