Introduction to luzlogr

This vignette describes how to use the luzlogr package, which provides flexible but lightweight logging facilities for R scripts.

Installing

To install this package, use R’s install.packages() function or the “Packages” pane in RStudio. To install the latest, developmental version of this package from GitHub:

  1. Make sure you have the devtools package installed from CRAN and loaded.
  2. install_github("bpbond/luzlogr")

Basics

Three functions - openlog(), printlog(), closelog() - provide logging of script output. They’re simple to use:

library(luzlogr)
openlog("test.log")
printlog("message")
closelog()

The resulting log file test.log looks like this (not including the initial ## characters):

## Thu Oct 24 02:42:22 2024  Opening test.log  
## Thu Oct 24 02:42:22 2024  message  
## Thu Oct 24 02:42:22 2024  Closing test.log flags = 0  
## ------- 
## R version 4.4.1 (2024-06-14) 
## Platform: x86_64-pc-linux-gnu 
## Running under: Ubuntu 24.04.1 LTS 
##  
## Matrix products: default 
## BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3  
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so;  LAPACK version 3.12.0 
##  
## locale: 
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=C               
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8    
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                  
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C             
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C        
##  
## time zone: Etc/UTC 
## tzcode source: system (glibc) 
##  
## attached base packages: 
## [1] stats     graphics  grDevices utils     datasets  methods   base      
##  
## other attached packages: 
## [1] luzlogr_0.2.1  rmarkdown_2.28 
##  
## loaded via a namespace (and not attached): 
##  [1] assertthat_0.2.1  digest_0.6.37     R6_2.5.1          fastmap_1.2.0     
##  [5] xfun_0.48         maketools_1.3.1   cachem_1.1.0      knitr_1.48        
##  [9] htmltools_0.5.8.1 buildtools_1.0.0  lifecycle_1.0.4   cli_3.6.3         
## [13] sass_0.4.9        jquerylib_0.1.4   compiler_4.4.1    sys_3.4.3         
## [17] tools_4.4.1       evaluate_1.0.1    bslib_0.8.0       yaml_2.3.10       
## [21] jsonlite_1.8.9    rlang_1.1.4

By default individual lines are prefixed with a timestamp, end with a carriage return, and the entire log starts with an open message and ends with a close one.

Any printable object can be written to a log. For example:

openlog("test.log")
printlog("message", 1, 2)
printlog(head(cars))
closelog(sessionInfo = FALSE)

(Notice in this case we’ve told closelog() not to append sessionInfo() output, as it does by default.)

## Thu Oct 24 02:42:22 2024  Opening test.log  
## Thu Oct 24 02:42:22 2024  message 1 2  
## Thu Oct 24 02:42:22 2024   
##   speed dist 
## 1     4    2 
## 2     4   10 
## 3     7    4 
## 4     7   22 
## 5     8   16 
## 6     9   10 
##  
## Thu Oct 24 02:42:22 2024  Closing test.log flags = 0

More involved examples

By design, luzlogr is intended to be simple and easy to use. Nonetheless, it does provide additional features, including:

  • priority levels for logs and messages
  • flagging messages
  • capturing all script output
  • logging to a text file or arbitrary connection
  • switching logs

Priority levels

In many circumstances, we want messages to have different priorities or levels associated with them. Each logfiles has a minimum priority level, set when it’s opened, making it easy to produce logs with varying levels of detail–for example, under development versus release conditions.

By default, luzlogr logs have a minimum levels of -Inf: in other words, they’ll accept every single message sent to them via printlog(), which produces messages with a default level of zero. We can change the log’s level, however, and this will then affect which messages appear in the log.

openlog("test.log", loglevel = 0)
printlog("This message will appear", level = 0)
printlog("So will this (level 0 by default)")
printlog("This will not", level = -1)
closelog(sessionInfo = FALSE)

produces

## Thu Oct 24 02:42:22 2024  Opening test.log  
## Thu Oct 24 02:42:22 2024  This message will appear  
## Thu Oct 24 02:42:22 2024  So will this (level 0 by default)  
## Thu Oct 24 02:42:22 2024  Closing test.log flags = 0

Notice that the third message didn’t get logged. If we change the loglevel parameter in openlog() to -1 or lower, however, all these messages will appear.

Flagging messages

Another way to differentiate messages is by flagging them. Note that in all the above examples, when the log is closed, a flags = 0 message is printed. But we can change that:

openlog("test.log")
printlog("A normal message")
printlog("A flagged message!", flag = TRUE)
flaglog("Another")
closelog(sessionInfo = FALSE)
## Thu Oct 24 02:42:22 2024  Opening test.log  
## Thu Oct 24 02:42:22 2024  A normal message  
## ** Flagged message: ** 
## Thu Oct 24 02:42:22 2024  A flagged message!  
## ** Flagged message: ** 
## Thu Oct 24 02:42:22 2024  Another  
## Thu Oct 24 02:42:22 2024  Closing test.log flags = 2

Capturing all output

So far, only messages sent via printlog() appear in the log. We might, however, want to capture everything1 produced by a script. To do this, use the sink = TRUE option of openlog().

Errors and logs

If an error occurs in your script, any log files will by default remain open. If using a single log file, it’s easy to put a statement such as suppressWarnings(closelog()) at the start of your script. Alternatively, you can tell luzlogr to close all open logs if an error occurs:

options(luzlogr.close_on_error = TRUE)

Logging to a connection

Logs can also be sent to any R connection: a pipe, compressed file, URL, etc.

con <- gzfile("test.log.gz")
openlog(con)
printlog("Sending to a compressed logfile")
closelog(sessionInfo = FALSE)

Note that luzlogr won’t close a connection that was already open at the time of the openlog() call.

Switching logs

If you’re logging to log A and open log B (without closing A), subsequent printlog() messages will go to B. When you close B, logging switches back to A (i.e., there’s a stack of logs that gets pushed/popped when necessary).

If you need to append to an existing log, use append = TRUE when calling openlog(). By default, existing logfiles are erased upon opening.

This concludes the Introduction to luzlogr vignette.


  1. Almost. Messages, warnings, and errors will not appear; but see ?sink and its type = "message" parameter.↩︎