Author: Ben Bond-Lamberty
Date: 2024-02-26

# Introduction to luzlogr

#### Ben Bond-Lamberty

#### 2024-02-26

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):

    ## Mon Feb 26 20:29:31 2024  Opening test.log
    ## Mon Feb 26 20:29:31 2024  message
    ## Mon Feb 26 20:29:31 2024  Closing test.log flags = 0
    ## -------
    ## R version 4.3.2 (2023-10-31)
    ## Platform: aarch64-apple-darwin20 (64-bit)
    ## Running under: macOS Sonoma 14.3.1
    ##
    ## Matrix products: default
    ## BLAS:   /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRblas.0.dylib
    ## LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0
    ##
    ## locale:
    ## [1] C/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
    ##
    ## time zone: America/New_York
    ## tzcode source: internal
    ##
    ## attached base packages:
    ## [1] stats     graphics  grDevices utils     datasets  methods   base
    ##
    ## other attached packages:
    ## [1] luzlogr_0.2.1
    ##
    ## loaded via a namespace (and not attached):
    ##  [1] assertthat_0.2.1  digest_0.6.34     R6_2.5.1          fastmap_1.1.1
    ##  [5] xfun_0.40         cachem_1.0.8      knitr_1.45        htmltools_0.5.6
    ##  [9] rmarkdown_2.25    cli_3.6.1         sass_0.4.8        jquerylib_0.1.4
    ## [13] compiler_4.3.2    rstudioapi_0.15.0 tools_4.3.2       evaluate_0.21
    ## [17] bslib_0.5.1       yaml_2.3.7        rlang_1.1.1       jsonlite_1.8.7

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.)

    ## Mon Feb 26 20:29:31 2024  Opening test.log
    ## Mon Feb 26 20:29:31 2024  message 1 2
    ## Mon Feb 26 20:29:31 2024
    ##   speed dist
    ## 1     4    2
    ## 2     4   10
    ## 3     7    4
    ## 4     7   22
    ## 5     8   16
    ## 6     9   10
    ##
    ## Mon Feb 26 20:29:31 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

    ## Mon Feb 26 20:29:31 2024  Opening test.log
    ## Mon Feb 26 20:29:31 2024  This message will appear
    ## Mon Feb 26 20:29:31 2024  So will this (level 0 by default)
    ## Mon Feb 26 20:29:31 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)

    ## Mon Feb 26 20:29:31 2024  Opening test.log
    ## Mon Feb 26 20:29:31 2024  A normal message
    ## ** Flagged message: **
    ## Mon Feb 26 20:29:31 2024  A flagged message!
    ## ** Flagged message: **
    ## Mon Feb 26 20:29:31 2024  Another
    ## Mon Feb 26 20:29:31 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.↩︎