debugr
is a package designed to support debugging in R. It mainly provides the dwatch()
function which prints a debug output to the console or to a file. A debug output can consist of a static text message, the values of one or more objects (potentially transformed by applying some functions) or the value of one or multiple (more complex) R expressions.
Whether or not a debug message is displayed can be made dependent on the evaluation of a criterion phrased as an R expression. Generally, debug messages are only shown if the debug mode is activated. The debug mode is activated and deactivated with debugr_switchOn()
and debugr_switchOff()
, respectively, which change the logical debugr.active
value in the global options. Since debug messages are only displayed in debug mode, the dwatch()
function calls can even remain in the original code as they remain silent and won’t have any effect until the debug mode is switched on again.
Let’s have a closer look at how to work with debugr
.
Assume you have developed the following function:
myfunction <- function(x) {
justastring <- "Not much information here"
z <- 1
for(i in 1:x) {
z <- z * i
}
}
With debugr
it is now possible to add a debug output to this function. Say, you want to see how the variable z
develops over time, but only if z > 40000
. To achieve that, we include a simple call to dwatch()
into our funtion myfunction()
(and attach the debugr
package first by calling library(debugr)
):
library(debugr)
myfunction <- function(x) {
justastring <- "Not much information here"
z <- 1
for(i in 1:x) {
dwatch(crit = "z > 40000", objs = c("z"))
z <- z * i
}
invisible(z)
}
Please notice that the name of the object we want to print out is provided in the argument objs
as a string, as a string. So, objs
is a vector of all the objects we want to have printed.
Now, we can call our function myfunction()
:
myfunction(10)
What happens?
Nothing.
Didn’t we want to see a debug output?
The reason why we don’t see anything is that the debug mode is currently switched off. Let’s turn it on and try again:
debugr_switchOn()
myfunction(10)
#>
#> ----------------------------- DEBUGR MESSAGE ------------------------------
#>
#> ** z:
#> [1] 40320
#>
#> ---------------------------------------------------------------------------
#>
#> ----------------------------- DEBUGR MESSAGE ------------------------------
#>
#> ** z:
#> [1] 362880
#>
#> ---------------------------------------------------------------------------
This time, we get two debug outputs. Every time the variabe z
exceeds the limit of 40,000 (we use the expression z > 40000
as dwatch()
’s criterion argument crit
) its value is printed by dwatch()
.
Turning on the debug mode brings dwatch()
to life. As dwatch()
remains silent as long as the debug mode is turned off (which is the ‘normal state of the world’), you could even leave the dwatch()
call in your code, it wouldn’t do any harm. In fact, nobody would ever notice. If you want to check if the debug mode is enabled, just call debugr_isActive()
debugr_isActive()
#> [1] TRUE
To turn the debug mode off again after you have finished your work, call debugr_switchOn()
’s counterpart, debugr_switchOff()
:
debugr_switchOff()
If you wanted to print the debug output into a file, you could use dwatch()
’s filename
argument to provide a file. In this case, no debug output would be displayed in the R console.
In the above example, we have simply printed the value of z
. But, of course, we could also do some more sophisticated things. For example, if we wanted to have a prettier output, we could modify our call of dwatch()
like this:
dwatch(crit = "z > 40000", objs = c("z"), funs=c("format"), args = as.list(c(big.mark = "\",\"")))
Putting this call into our function myfunction()
from above yields:
#>
#> ----------------------------- DEBUGR MESSAGE ------------------------------
#>
#> ** z:
#> [1] "40,320"
#>
#> ---------------------------------------------------------------------------
#>
#> ----------------------------- DEBUGR MESSAGE ------------------------------
#>
#> ** z:
#> [1] "362,880"
#>
#> ---------------------------------------------------------------------------
Here, we apply the function() format
to our object z
to include a comma as a seperator. Two things are noteworthy here:
The name of the function that is to be applied to our object z
is provided in the argument funs
as a string. In our example, we have only one object. However, if we had more objects, we could apply a different function to each of them, leading to funs
look like funs = c("format", NULL, "mean")
, for example. In this case, we would have format()
applied to the first object, no function applied to the second, and mean()
to the third one.
While the function format()
is assumed to take our object z
as its first argument, we can supply additional arguments using dwatch()
‘s args
argument. This is a list of vectors, one for each function in funs
. The elements of the vector are named and the elements’ names are the names of the (additional) arguments of the respective function in funs
. As these vectors are iternally interpreted as character vectors, make sure you escape any quotation mark properly, as we did in the above example. Don’t worry too much about these vectors being interpreted as character vectors. If your funs
argument is funs = c("format", NULL, "mean")
then args = as.list(c(big.mark = "\",\""), NULL, c(na.rm = TRUE, trail = 0.2))
will work perfectly fine (even though you don’t out TRUE
an 0.2
in quotation marks).
By the way: If you use dwatch()
to print a dataframe, dwatch()
uses View()
as the default way of displaying it. If you want to have it printed to the R console, just apply print()
with funs = c("print")
.
In the above example, we format the debug output by using dwatch()
’s funs
argument. We would accomplish the same effect by phrasing our command as an R expression and let dwatch()
evaluate that expression:
myfunction <- function(x) {
justastring <- "Not much information here"
z <- 1
for(i in 1:x) {
dwatch(crit = "z > 40000", expr=c("format(z, big.mark = \",\")"))
z <- z * i
}
invisible(z)
}
myfunction(10)
#>
#> ----------------------------- DEBUGR MESSAGE ------------------------------
#>
#> ** Expression: format(z, big.mark = ",")
#> [1] "40,320"
#> ---------------------------------------------------------------------------
#>
#> ----------------------------- DEBUGR MESSAGE ------------------------------
#>
#> ** Expression: format(z, big.mark = ",")
#> [1] "362,880"
#> ---------------------------------------------------------------------------
The expr
argument allows you to print more complex expressions; however, in our case here, this expression is just a simple function call. Of course, you can print as many expressions as you like, as expr
is a vector of strings.
Sometimes you probably don’t want to list all the objects that you want to include in your debug output. You just want to print all objects. This is easy to accomplish with dwatch()
’s show.all
argument. Look at the following example:
myfunction <- function(x) {
justastring <- "Not much information here"
z <- 1
for(i in 1:x) {
dwatch(crit = "z > 40000", show.all = TRUE)
z <- z * i
}
invisible(z)
}
myfunction(10)
#>
#> ----------------------------- DEBUGR MESSAGE ------------------------------
#>
#> ** i:
#> [1] 9
#>
#>
#> ** justastring:
#> [1] "Not much information here"
#>
#>
#> ** x:
#> [1] 10
#>
#>
#> ** z:
#> [1] 40320
#>
#> ---------------------------------------------------------------------------
#>
#> ----------------------------- DEBUGR MESSAGE ------------------------------
#>
#> ** i:
#> [1] 10
#>
#>
#> ** justastring:
#> [1] "Not much information here"
#>
#>
#> ** x:
#> [1] 10
#>
#>
#> ** z:
#> [1] 362880
#>
#> ---------------------------------------------------------------------------
This time, dwatch()
prints all objects. More precisely, it prints all objects in the environment from which dwatch()
was called.
Needless to say, that you can easily combine the use of the arguments objs
, expr
and show.all
in one dwatch()
call.
Here are some more options to customize your use of dwatch()
:
Add a (static) text message with the msg
argument.
Remove the upper and lower border of the dwatch()
outputs by setting show.frame = FALSE
.
Include the source code section surrounding the call of dwatch()
in the output. To do this, you need to add an arbitrary unique ID to the call of dwatch()
with the unique.id
argument (which is just a string). dwatch()
will try to figure out your source file and print the code. This works only when you run your code from a saved script (not from the console), and it works best when you are using the R Studio IDE.
If you want to work with a uniqe.id
(which will also be displayed in the caption of the dwatch()
message) but don’t want to have the source code printed that surrounds the dwatch()
call, set suppress.source = TRUE
.
If you want stop the execution of your as soon as the crit
criterion is fulfilled, use halt = TRUE
. The debug outputs are shown in any case.