Search code examples
rloggingsink

R - Automatically create daily file to log everything from RStudio console


I typically work in one script.R file for each project (which each last a few weeks). I'd like to set up a process such that every day when I open RStudio I can start a file that stores everything I see in the console (input, output, warnings) to a file. I'd also like it to create a new file every single day. I've read around here and there is some talk about how to do parts of this but I'm not smart enough to bring it all together by myself. Here's what I have so far:

sink(paste("filename.txt", strtrim(Sys.time(),10)), append=T, split=T)
x <- 1:5
y <- 2:6
z <- c(5, 8, 3, 5, 9)
reg <- lm(y ~ x)  #intentional "mistake" used to produce a warning
reg <- lm(z ~ x)
summary(reg)
sink()

What I think this accomplishes: I get a log file which captures all output in a new file if it's the first time I've done it that, but will add to the existing file if I do some work in the AM, close it all out, and start anew later in the day.

What this doesn't accomplish: it doesn't capture input or warning messages, and I think it's a bit clunky.

Am I right that this accomplishes what I think it does? Is there a way to modify this (or do something similar) which will allow me to also capture input and warnings (basically the rest of what is seen in the console)?

Thanks!


Solution

  • Up front: this answer does not provide both inputs and messages. A more robust mechanism for you would be to use a R-markdown document: it will capture warnings and such, it will continue on errors, it does include inputs, etc. Just use RStudio's "knit document" (or even notebooks), and you'll get the same effect. If that doesn't satisfy you and you want to stick with sink, read on.

    There is an argument to sink that allows the saving of warnings and errors:

    sink(file = NULL, append = FALSE, type = c("output", "message"),
         split = FALSE)
    ...
    type: character string.  Either the output stream or the messages
          stream.  The name will be partially matched so can be
          abbreviated.
    

    You cannot sink both "output" and "messages" at the same time, so you need to call sink twice, one for each type. (They are handled completely separately, so you'll need close them individually as well.)

    CAVEAT EMPTOR: the help page warns against sinking messages:

     Sink-ing the messages stream should be done only with great care.
     For that stream 'file' must be an already open connection, and
     there is no stack of connections.
    

    Furthermore, it does not support split, so you will not see any warnings or errors. (That's a significant-enough issue to discourage me from using it for this ... I'd just highlight the console in its entirety and save in a text editor. But I understand you are trying to automate things, so we'll continue.)

    There are two ways to do it: safely (losing synchronization between output and messages), and not-well-tested-likely-unsafely.

    Safely

    msgcon <- file("out1-msg.txt", open = "a")
    sink("out1.txt", type = "output", append = TRUE, split = TRUE)
    sink(msgcon, type = "message") # does not support split
    # do your work here
    a <- 1
    a
    stop("huh?")
    sink(NULL, type = "message")
    sink(NULL, type = "output")
    

    The good is that you'll get both types of messages, separately and safely. The bad is that you won't be able to tie a specific error/warning to anywhere in the code or output. If that doesn't bother you, stick with this.

    Not-well-tested-likely-Unsafely

    The problem with this approach is that two "processes" are writing to the same file, potentially simultaneously. This could cause data loss or (more likely) jumbled/interleaved outputs. I have not mucked around in the code enough to see if this will happen, nor have I tested it exhaustively. You've been warned.

    con <- file("out1.txt", open = "a") # use for both sinks
    sink(con, type = "output", append = TRUE, split = TRUE)
    sink(con, type = "message")
    a <- 2
    a
    stop("uh-wha?")
    sink(NULL, type = "message")
    sink(NULL, type = "output")
    

    You still need to open and close both types independently.