lgr

CRAN status Lifecycle: maturing Travis build status Codecov test coverage

lgr is a logging package for R built on the back of R6 classes. It is designed to be flexible, performant and extensible. The package vignette contains a comprehensive description of the features of lgr (some of them unique among R logging packages) along with many code examples.

Users that have not worked with R6 classes before, will find configuring Loggers a bit strange and verbose, but care was taken to keep the syntax for common logging tasks and interactive usage simple and concise. User that have experience with shiny, plumber, python logging or Apache Log4j will feel at home. User that are proficient with R6 classes will also find it easy to extend and customize lgr, for example with their own appenders Loggers or Appenders.

Features

Usage

To log an event with with lgr we call lgr$<logging function>(). Unnamed arguments to the logging function are interpreted by sprintf(). For a way to create loggers that glue instead please refer to the vignette.

lgr$fatal("A critical error")
#> FATAL [09:46:45.461] A critical error
lgr$error("A less severe error")
#> ERROR [09:46:45.701] A less severe error
lgr$warn("A potentially bad situation")
#> WARN  [09:46:45.852] A potentially bad situation
lgr$info("iris has %s rows", nrow(iris))
#> INFO  [09:46:45.880] iris has 150 rows
# the following log levels are hidden by default
lgr$debug("A debug message")
lgr$trace("A finer grained debug message")

A Logger can have several Appenders. For example, we can add a JSON appender to log to a file with little effort.

tf <- tempfile()
lgr$add_appender(AppenderFile$new(tf, layout = LayoutJson$new()))
lgr$info("cars has %s rows", nrow(cars))
#> INFO  [09:46:45.959] cars has 50 rows
cat(readLines(tf))
#> {"level":400,"timestamp":"2021-09-16 09:46:45","logger":"root","caller":"eval","msg":"cars has 50 rows"}

By passing a named argument to info(), warn(), and co you can log not only text but arbitrary R objects. Not all appenders handle such custom fields perfectly, but JSON does. This way you can create logfiles that are machine as well as (somewhat) human readable.

lgr$info("loading cars", "cars", rows = nrow(cars), cols = ncol(cars))
#> INFO  [09:46:46.095] loading cars {rows: 50, cols: 2}
cat(readLines(tf), sep = "\n")
#> {"level":400,"timestamp":"2021-09-16 09:46:45","logger":"root","caller":"eval","msg":"cars has 50 rows"}
#> {"level":400,"timestamp":"2021-09-16 09:46:46","logger":"root","caller":"eval","msg":"loading cars","rows":50,"cols":2}

For more examples please see the package vignette and documentation

See lgr in action

lgr is used to govern console output in my shiny based csv editor shed

# install.packages("remotes")
remotes::install_github("s-fleck/shed")
library(shed)

# log only output from the "shed" logger to a file
logfile <- tempfile()
lgr::get_logger("shed")$add_appender(AppenderFile$new(logfile))
lgr::threshold("all")

# edit away and watch the rstudio console!
lgr$info("starting shed")
shed(iris)  
lgr$info("this will not end up in the log file")

readLines(logfile)

# cleanup
file.remove(logfile)

Development status

lgr in general is stable and safe for use, but the following features are still experimental:

Dependencies

R6: The R6 class system provides the framework on which lgr is built and the only Package lgr will ever depend on. If you are a package developer and want to add logging to your package, this is the only transitive dependency you have to worry about, as configuring of the loggers should be left to the user of your package.

Optional dependencies

lgr comes with a long list of optional dependencies that make a wide range of appenders possible. You only need the dependencies for the Appenders you actually want to use. Care was taken to choose packages that are slim, stable, have minimal dependencies, and are well maintained :

Extra appenders (in the main package):

Extra appenders via lgrExtra:

Other extra features:

Other Suggests (future, future.apply) do not provide extra functionality but had to be included for some of the automated unit tests run by lgr.

Installation

You can install lgr from CRAN

install.packages("lgr")

Or you can install the current development version directly from github

#install.packages("remotes")
remotes::install_github("s-fleck/lgr")

Outlook

The long term goal is to support (nearly) all features of the python logging module. If you have experience with python logging or Log4j and are missing features/appenders that you’d like to see, please feel free to post a feature request on the issue tracker.

Acknowledgement