I am having problems using capture.output()
and I can't figure out why, since it is for the most part just a wrapper for sink()
.
Consider this trivial example using sink()
:
foo = function() warning("foo")
f = file()
sink(f, type = "message")
foo()
readLines(f)
## [1] "Warning message:" "In foo() : foo"
close(f)
This works as expected. however, capture.output()
does not :
f = file()
capture.output(foo(), file = f, type = "message")
## Warning message:
## In foo() : foo
readLines(f)
## character(0)
close(f)
capture.output()
does work for messages though:
bar = function() message("bar")
f = file()
capture.output(bar(), file = f, type = "message")
readLines(f)
## [1] "bar"
close(f)
But according to the documentation both messages and warnings should be captured:
Messages sent to
stderr()
(including those frommessage
,warning
andstop
) are captured bytype = "message"
.
What am I missing here?
@MrFlick's comment points to a potential solution, provide you have control over the arguments that are passed to warning()
. If you use the argument immediate. = TRUE
then capture.output()
can retrieve the warning message.
baz = function() warning("baz", immediate. = TRUE)
res = capture.output(baz(), type = "message")
print(res)
## [1] "Warning in baz() : baz"
EDIT
Alternatively, @user2554330 points out you can use options(warn = 1)
to globally make warnings print immediately.
oldopt = getOption("warn")
options(warn = 1)
res = capture.output(foo(), type = "message")
print(res)
## [1] "Warning in foo() : foo"
options(warn = oldopt)
EDIT 2
For completeness, I think it's helpful to point out this alternative approach using withCallingHandlers
, which does not require any changes to options and may be a cleaner solution depending on the application. Consider this example of nested warnings:
foo = function() {
warning("foo")
bar()
}
bar = function() {
warning("bar")
baz()
}
baz = function() {
warning("baz")
TRUE
}
# create a container to hold warning messages
logs = vector("character")
# function to capture warning messages
log_fun = function(w) logs <<- append(logs, w$message)
# function call with message capturing
withCallingHandlers(foo(), warning = log_fun)
## [1] TRUE
## Warning messages:
## 1: In foo() : foo
## 2: In bar() : bar
## 3: In baz() : baz
print(logs)
## [1] "foo" "bar" "baz"
Note that withCallingHandlers
allows you to specify different behavior for different signal conditions, e.g. warnings
and messages
could be stored in separate variables.