Получите все R-коды, которые запускаются при запуске

Предположим, что у меня есть куча R-кода в script, и я хочу записать весь R-код, который запускается из .GlobalEnv, в плоский файл или базу данных вместе с ошибками и предупреждениями. Я мог бы написать простую функцию 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 < - 10, x * 7 2015-05-14 17:24:31: сделано с yadayada

Итак, мой вопрос: как мне получить то, что выполняется R, чтобы я мог хранить выполненные выражения в журнале/базе данных. И без необходимости писать вызов функции перед каждым выражением (как в myhandler (x < - 10); myhandler (x * 10)). Любая помощь по этому поводу?

Ответы

Ответ 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

Кроме того:

  • вывод не будет зарегистрирован, в частности message или warning, показанный на консоли
  • в случае 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

Ответ 3

Я признаюсь, что я действительно не понимаю, что "иметь текущие выражения в том же процессе, что и в случае, когда выполняются команды 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])
  }
}

Эта функция принимает в качестве аргументов исходный файл и имена файлов журнала. Этот script будет принимать R файл и будет регистрировать время выполнения каждой команды. Затем он записывает выражение в один и тот же файл журнала. Каждый вывод, направленный на stdout() и сообщения об ошибках, направляется в файл журнала. Вам, очевидно, не нужно каким-либо образом изменять исходный файл.