send R diagnostic messages to stdout instead stder

2019-02-15 11:04发布

问题:

Looking for an options which let me to redirect R diagnostic messages (produces by message()) to stdout, not stderr as it is by default.

message manual states:

The default handler sends the message to the stderr() connection.

So the question is how can I change this default behavior? still leaving redirection of warning() and stop() intact.

Already tried sink type='message' but it redirects all (messages, warnings, errors).

If anyone is willing to test, this is sample script exec_test.R:

print("using print")
cat("using cat\n")
message("using message")
warning("using warning")
stop("using stop")
q("no")

which then will be executed by:

Rscript exec_test.R 1>> exec_test.Rout 2>> exec_test_error.Rout

I don't what to use 2>&1 because my script produce tons of messages and very rarely the real errors so I need to store those logs in separate files.

回答1:

While this is very likely not a best practice, you could override message with a version that writes to stdout() by default, right?

message <- function (..., domain = NULL, appendLF = TRUE) 
{
    args <- list(...)
    cond <- if (length(args) == 1L && inherits(args[[1L]], "condition")) {
        if (nargs() > 1L) 
            warning("additional arguments ignored in message()")
        args[[1L]]
    }
    else {
        msg <- .makeMessage(..., domain = domain, appendLF = appendLF)
        call <- sys.call()
        simpleMessage(msg, call)
    }
    defaultHandler <- function(c) {
        cat(conditionMessage(c), file = stdout(), sep = "")
    }
    withRestarts({
        signalCondition(cond)
        defaultHandler(cond)
    }, muffleMessage = function() NULL)
    invisible()
}


回答2:

The OP showed the execution happening via the Rscript command and using some I/O redirection. If you want to use redirection to log everything and only show to console on error, the best method I've found is to use || to check if the script had non-zero exit status before printing to screen:

Rscript myrscript.R > temp.log 2>&1 || cat temp.log

This method relies strictly on the exit code for printing, which only partly gets around message() going to stderr, but I thought this example helpful to mention since messages won't necessarily trigger a non-zero exit status and you can continue to log quietly with this method.

If you'd like to go one step further and keep appending to a single log file, then this will work:

Rscript myrscript.R > temp.log 2>&1 || cat temp.log && cat temp.log >> persistent.log && rm temp.log

The pseudocode for this command is:

  1. Redirect stderr and stdout into temp.log
  2. If command had non-zero exit status then write to screen
  3. Then redirect the contents of temp.log into your persistent.log
  4. Then remove temp.log


回答3:

Using sink. Here's a modification of your code:

sink(stdout(), type = "message") # sink messages to stdout
print("using print")
cat("using cat\n")
message("using message")
warning("using warning")
sink(NULL, type="message") # close the sink
warning("after ending sink") # this will be the only thing in your err file
q("no")