2015-05-14 95 views
15

假設我在腳本中有一堆R代碼,並且想要將所有從.GlobalEnv運行的R代碼記錄到平面文件或數據庫以及錯誤和警告消息。 如下,我可以寫一個簡單的logme功能或使之更加複雜一點也獲取錯誤改變options(error = mylogginfunction)獲取運行時運行的所有R代碼

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年5月14日17點24分31秒:啓動了一些yadayada,2015-05-14 17:24:31:用yadayada完成

但我想要的是,日誌文件寫下執行的表達式,而不必我寫每個包裝器聲明。 我希望日誌看起來像。 2015年5月14日17時24分31秒:開始有些yadayada,X < - 10,X * 72015年5月14日17時24分31秒:以yadayada做

所以我問題是,我如何獲取R正在執行的內容,以便將執行的表達式存儲在日誌/數據庫中。而不需要在每個表達式之前寫一個函數調用(如myhandler(x < - 10); myhandler(x * 10))。 對此有何幫助?

+0

'R CMD BATCH filename.R'命令行產生一個似乎你問的'.Rout'文件。 – nicola

+0

我對.Rout或Rscript myscript> mylog.log管道是什麼感興趣。我對錶達感興趣。 – jwijffels

+0

'R CMD BATCH'也寫入表達式。你試過了嗎? – nicola

回答

0

這可能是簡單的在任何情況下工作,但你可以用這個嘗試:

定義將myHandler爲:

myhandler({ 

    a <- 1 
    a <- a + 1 
    print(a) 

}) 

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()) 
    } 
} 

與您的代碼使用它的括號內

結果:

# 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 
+0

非常感謝您的回答。這與我的想法非常接近,但是我試圖在我的問題中明確排除這個答案,它表示'不必在每個表達式之前寫入函數調用'。這個解決方案只是把所有的表達式放在1個塊中。我正在尋找一種解決方案,在所有代碼之前我不需要myhandler。 – jwijffels

0

我承認我並沒有真正明白「在運行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()的每個輸出和錯誤消息都指向日誌文件。你顯然不需要以任何方式修改你的源文件。

+0

Nicola,謝謝你的回答。這基本上和bergant給出的一樣。我正在尋找一種解決方案,我不需要傳遞一個源文件。這可能嗎? – jwijffels

+0

如果你不想在源文件中存儲你想要評估和存儲的表達式,你認爲如何?對不起,我不認爲我真的得到了你的約束,你的工作流程是什麼。 – nicola

+0

工作流程 'logme(sprintf(「%s:started some yadayada,」,Sys.time())) x < - 10 x * 7 logme(sprintf(「%s:done with yadayada」, Sys.time())) ' 這應該導致日誌文件陳述'2015-05-14 17:24:31:啓動了一些yadayada,x < - 10,x * 7 2015-05-14 17:24: 31:完成與yadayada' – jwijffels

5

爲了捕捉輸入命令,你可以使用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 

但是你得到的是解析表達,這意味着該行

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

將被記錄爲

x + 1 
b <- 7 
b == 2 

另外:

  • 輸出將不被記錄,特別是在控制檯error記錄的情況下
  • 顯示messagewarning不會被觸發,所以你需要單獨的函數來處理它
+0

太棒了!正是我在找什麼。非常感謝。 – jwijffels