I'm using R's sink() function to capture errors, warnings, messages and console output into a single text file.
I'm wondering if simultaneously sinking both message and output types to a single open file is bad to do?
I capture all of the above into a single file, but I must open a file handle to allow both sink types to be captured. The following code illustrates using the file handle approach:
message_filename = 'script_messages.txt'
try(message_file <- file(message_filename, open="at")) # open file for appending in text mode
sink(message_file, type="message")
sink(message_file, type="output")
cat("\n\n")
message(format(Sys.time(), "%a %b %d %Y %X TZ(%z)"), appendLF = TRUE)
# next line produces messages since file doesn't exist
try(source("import_file.R"), silent = TRUE)
# Save and close writing errors, warnings, messages, and console output to a file
sink(type="output")
sink(type="message")
close(message_file)
If I don't open a file handle, then the sink 'output' type messages are the only ones captured in the text file.
The documentation on sink {base} has some key info in the first half of the Details section, but I'm not fluent enough to be sure I've implemented it properly.
I believe it's to do with the global option for warn. The default is warn=0 which means "warnings are stored until the top–level function returns". In other words, when you source("script.R"), R stores up the warnings and prints them once your script has finished, i.e. after you've run sink(type="output"); sink(type="message"); close(message_file).
To change this you can call options(warn=1) before you source your script, this will print warnings as they occur and will therefore be caught by your sink. This only needs to be run once per session.
Related
Is it possible to get R to write a plot in bitmap format (e.g. PNG) to standard output? If so, how?
Specifically I would like to run Rscript myscript.R | other_prog_that_reads_a_png_from_stdin. I realise it's possible to create a temporary file and use that, but it's inconvenient as there will potentially be many copies of this pipeline running at the same time, necessitating schemes for choosing unique filenames and removing them afterwards.
I have so far tried setting outf <- file("stdout") and then running either bitmap(file=outf, ...) or png(filename=outf, ...), but both complain ('file' must be a non-empty character string and invalid 'filename' argument, respectively), which is in line with the official documentation for these functions.
Since I was able to persuade R's read.table() function to read from standard input, I'm hoping there's a way. I wasn't able to find anything relevant here on SO by searching for [r] stdout plot, or any of the variations with stdout replaced by "standard output" (with or without double quotes), and/or plot replaced by png.
Thanks!
Unfortunately the {grDevices} (and, by implication, {ggplot2}) seems to fundamentally not support this.
The obvious approach to work around this is: let a graphics device write to a temporary file, and then read that temporary file back into the R session and write it to stdout.
But this fails because, on the one hand, the data cannot be read into a string: character strings in R do not support embedded null characters (if you try you’ll get an error such as “nul character not allowed”). On the other hand, readBin and writeBin fail because writeBin categorically refuses to write to any device that’s hooked up to stdout, which is in text mode (ignoring the fact that, on POSIX system, the two are identical).
This can only be circumvented in incredibly hacky ways, e.g. by opening a binary pipe to a command such as cat:
dev_stdout = function (underlying_device = png, ...) {
filename = tempfile()
underlying_device(filename, ...)
filename
}
dev_stdout_off = function (filename) {
dev.off()
on.exit(unlink(filename))
fake_stdout = pipe('cat', 'wb')
on.exit(close(fake_stdout), add = TRUE)
writeBin(readBin(filename, 'raw', file.info(filename)$size), fake_stdout)
}
To use it:
tmp_dev = dev_stdout()
contour(volcano)
dev_stdout_off(tmp_dev)
On systems where /dev/stdout exists (which are most but not all POSIX systems), the dev_stdout_off function can be simplified slightly by removing the command redirection:
dev_stdout_off = function (filename) {
dev.off()
on.exit(unlink(filename))
fake_stdout = file('/dev/stdout', 'wb')
on.exit(close(fake_stdout), add = TRUE)
writeBin(readBin(filename, 'raw', file.info(filename)$size), fake_stdout)
}
This might not be a complete answer, but it's the best I've got: can you open a connection using the stdout() command? I know that png() will change the output device to a file connection, but that's not what you want, so it might work to simply substitute png by stdout. I don't know enough about standard outputs to test this theory, however.
The help page suggests that this connection might be text-only. In that case, a solution might be to generate a random string to use as a filename, and pass the name of the file through stdout so that the next step in your pipeline knows where to find your file.
I am trying to take full control of output to both the console and the (split) sink file. For one, I want certain output to be colored on the [ansi] console but not clutter up my Rout file.
I know of two basic commands:
cat() outputs to both the console and the sink file.
message() outputs only to the console (via file stderr), but not to the sink file.
I start with
messageln <- function(...) {
boldblue <- "\033[34;1m"; ansioff <- "\033[0m"
if (interactive()) message(boldblue) # fails. stderr dest overrides
cat(..., sep="")
if (interactive()) message(ansioff) # fails. stderr dest overrides
}
Problem 1: the ansi terminal command is emitted and interpreted by my console only if I use cat instead of message. this is presumably because message emits its own ansi-off to turn off coloring.
Problem 2: is there an opposite to message() that outputs a string only to the sink file but not to the console?
I am running a script for some batch copying/renaming operations using R. The script is executed using a .bat file for executing using command prompt in MS Windows. Note that the destination computer where this will be run does not allow external connections (internet of otherwise) to install new packages so the solution should be in base R.
I can print comments to screen using cat and the output shows up in the .Rout file generated after the script is run. The .Rout file gets overwritten each time the script is executed and I would like to create a separate log file instead.
Here's the relevant of code:
if(copy_files){
# tried different modes for the file statement below
log_con <- file(paste(format(Sys.time(), '%d-%b-%Y %H:%M:%S'),
'move duplicates.txt'),
open = 'at')
cat('Parent directory:\t\t' , file = log_con, append= F)
cat(parent_folder , file = log_con, append= T)
cat('\nSubfolder to copy files to:\t ', file = log_con, append= T)
cat(subfolder_old, file = log_con, append= T)
cat('\n\n', file = log_con, append= T)
# copying operations here - omitted for brevity
}
Using only the cat statements without the file and append argument works fine but the above code returns the following error messages:
> log_con <- file(paste(format(Sys.time(), '%d-%b-%Y %H:%M:%S'), 'move duplicates.txt'), open = 'at')
Error in file(paste(format(Sys.time(), "%d-%b-%Y %H:%M:%S"), "move duplicates.txt"), :
cannot open the connection
In addition: Warning message:
In file(paste(format(Sys.time(), "%d-%b-%Y %H:%M:%S"), "move duplicates.txt"), :
cannot open file '07-Aug-2018 15:50:36 move duplicates.txt': Invalid argument
> cat('Parent directory:\t\t' , file = log_con, append= F)
Error in cat("Parent directory:\t\t", file = log_con, append = F) :
cannot open the connection
In addition: Warning message:
In cat("Parent directory:\t\t", file = log_con, append = F) :
cannot open file '07-Aug-2018 15:48:11 move duplicates.txt': Invalid argument
From what I understand the error stems from the fact that the log file does not exist at the beginning and the connection can not be opened.
Looking at the documentation and the answer for How to create periodically send text to a "log file" while printing normal output to console? seems to suggest that including append = F in the first cat statement ought to work. I have tried versions where different/no mode is specified for file command with the same result. Answers for Add lines to a file seem to suggest the same. Am I missing something?
I could create a file and have R append lines to it each time but I want a unique log for each time the script is run.
one way to solve this is to use sink, at the start of the code put sink("R_code.log") once the whole process is done put sink() at the last line of code .I hope this will solve your problem and if you want to name it dynamically use paste0 inside sink function.
I have written a function in R to print any message both to log file and console. But if there is any unexpected error while running the code, then error is displayed only to console. Is there any way to write error message to both console and log file? Here is the function..
log_con <- file("Text1.txt", open="a")
loggerfn<-function(Message,LogConnection=log_con){
cat(Message, file = LogConnection)
cat(Message)
}
Here is the sample code to...
for (i in 1:10)
{
loggerfn("loop begins\n",log_con)
a <- rnorm(n = 100, mean = i, sd = 5)
loggerfn(mean(a),log_con)
loggerfn("loop Completed\n",log_con)
if(i==8){
sdfs
}
}
In above code I have intentionally introduced error by providing undefined object sdfd.Below provided Error message is shown only in console, is there any way to write error message to both console and logfile?
Error: object 'sdfs' not found
use sink() to divert messages as well as warnings to a file. The trick is to set the argument type="message"
refer http://www.statmethods.net/interface/io.html
and Output error/warning log (txt file) when running R script under command line
https://stat.ethz.ch/R-manual/R-devel/library/base/html/sink.html
The sink( ) function defines the direction of the output.
Description
sink diverts R output to a connection (and stops such diversions).
sink.number()
reports how many diversions are in use.
sink.number(type = "message") reports the number of the connection currently being used for error messages.
Usage
sink(file = NULL, append = FALSE, type = c("output", "message"),
split = FALSE)
sink.number(type = c("output", "message"))
direct output to a file
sink("myfile", append=FALSE, split=FALSE)
return output to the terminal
sink()
The append option controls whether output overwrites or adds to a file. The split option determines if output is also sent to the screen as well as the output file.
Here are some examples of the sink() function.
output directed to output.txt in c:\projects directory.
output overwrites existing file. no output to terminal.
sink("c:/projects/output.txt")
output directed to myfile.txt in cwd. output is appended
to existing file. output also send to terminal.
sink("myfile.txt", append=TRUE, split=TRUE)
When redirecting output, use the cat( ) function to annotate the output.
I have a routine to write (writeLines with sprintf) to output some values to the console.
I was asked to do the same output to a txt file.
What I have now is to duplicate the first set of "writeLine"s and change them to a write(..., file)
I am unable to find if I can declare a file to the console. What I am thinking is to have a function to do that and pass a parameter (file) and then just one set of statements for the write and call
diskfile <- file("results.txt", "w")
printresults("console") # This is the part I don't know how
printresults(diskfile)
If I do the printresults to the file, I can read from it and present to the console. I am using:
cat(readlines, ...)
This works fine but I preferr to have the function solution.
Is there a way to do that?
Thanks for your time,
EDIT>>> More specific
Can I do write(sprintf(...), CONSOLE)?
Would sink() (a favourite of mine) do the trick?
sink("file.txt", append = FALSE, split = TRUE)
print("Hello world")
sink()
With split = TRUE the output is passed to the console and the text file simulataneously, and the second call to sink() reverts output back to the console.
Both the console and file.txt print
"Hello world"