# Logging Performance

#### Updated as of 2020-01-17

The following vignette presents benchmarks for log4r against all general-purpose logging packages available on CRAN:

- futile.logger and logging, which are well-established packages; and
- logger and lgr, both of which are relative newcomers.

Each logging package features slightly different capabilities, but these benchmarks are focused on the two situations common to using all of them:

1. Logging simple messages to the console; and
2. Deciding not to log a message because it is below the threshold.

The first of these is likely the most common kind of logging done by end users, although some may chose to log to files, over HTTP, or to the system log (among others). Yet a benchmark of these other scenarios would largely show the relative expense of these operations, instead of the overhead of the logic performed by the logging packages themselves.

The second measures the performance impact of leaving logging messages in running code, even if they are below the current threshold of visibility. This is another measure of overhead for each logging package.

## Using `cat()`

As a reference point, we can measure how long it takes R itself to write a simple message to the console:

    cat_debug <- function() {
      cat() # Print nothing.
    }

    cat_info <- function() cat(
      "INFO  [", format(Sys.time(), "%Y-%m-%d %H:%M:%S", usetz = FALSE),
      "] Info message.", sep = ""
    )

## The log4r Package

The following is a typical log4r setup:

    log4r_logger <- log4r::logger(threshold = "INFO")

    log4r_info <- function() {
      log4r::info(log4r_logger, "Info message.")
    }

    log4r_debug <- function() {
      log4r::debug(log4r_logger, "Debug message.")
    }

## The futile.logger Package

The following is a typical futile.logger setup:

    requireNamespace("futile.logger")
    #> Loading required namespace: futile.logger

    futile.logger::flog.logger()

    fl_info <- function() {
      futile.logger::flog.info("Info message.")
    }

    fl_debug <- function() {
      futile.logger::flog.debug("Debug message.")
    }

## The logging Package

The following is what I believe to be a typical logging setup:

    requireNamespace("logging")
    #> Loading required namespace: logging

    logging::basicConfig()

    logging_info <- function() {
      logging::loginfo("Info message.")
    }

    logging_debug <- function() {
      logging::logdebug("Debug message.")
    }

## The logger Package

The following is what I believe to be a typical logger setup:

    requireNamespace("logger")
    #> Loading required namespace: logger

    logger_info <- function() {
      logger::log_info("Info message.")
    }

    logger_debug <- function() {
      logger::log_debug("Debug message.")
    }

## The lgr Package

The following is what I believe to be a typical lgr setup:

    requireNamespace("lgr")
    #> Loading required namespace: lgr

    lgr_logger <- lgr::get_logger("perf-test")
    lgr_logger$set_appenders(list(cons = lgr::AppenderConsole$new()))
    lgr_logger$set_propagate(FALSE)

    lgr_info <- function() {
      lgr_logger$info("Info message.")
    }

    lgr_debug <- function() {
      lgr_logger$debug("Debug message.")
    }

## Test All Loggers

Debug messages should print nothing.

    log4r_debug()
    cat_debug()
    logging_debug()
    fl_debug()
    logger_debug()
    lgr_debug()

Info messages should print to the console. Small differences in output format are to be expected.

    log4r_info()
    #> INFO  [2020-01-17 19:47:52] Info message.
    cat_info()
    #> INFO  [2020-01-17 19:47:52] Info message.
    logging_info()
    #> 2020-01-17 19:47:52 INFO::Info message.
    fl_info()
    #> INFO [2020-01-17 19:47:52] Info message.
    logger_info()
    #> INFO [2020-01-17 19:47:52] Info message.
    lgr_info()
    #> INFO  [19:47:52.123] Info message.

## Benchmarks

The following benchmarks all loggers defined above:

    info_bench <- microbenchmark::microbenchmark(
      cat = cat_info(),
      log4r = log4r_info(),
      futile.logger = fl_info(),
      logging = logging_info(),
      logger = logger_info(),
      lgr = lgr_info(),
      times = 500,
      control = list(warmups = 50)
    )

    debug_bench <- microbenchmark::microbenchmark(
      cat = cat_debug(),
      log4r = log4r_debug(),
      futile.logger = fl_debug(),
      logging = logging_debug(),
      logger = logger_debug(),
      lgr = lgr_debug(),
      times = 500,
      control = list(warmups = 50)
    )

### How long does it take to print messages?

    print(info_bench, order = "median")
    #> Unit: microseconds
    #>           expr    min      lq      mean  median      uq     max neval
    #>            cat   41.6   54.15   70.6914   60.50   71.80  2265.6   500
    #>          log4r   37.4   59.75   76.1058   67.45   80.00  1351.3   500
    #>         logger  254.8  307.30  371.5422  346.15  380.95  3285.4   500
    #>        logging  369.3  459.95  542.7246  500.80  541.60 11031.2   500
    #>            lgr  613.9  743.85  898.7276  783.90  828.30 38575.5   500
    #>  futile.logger 2629.7 3000.00 3278.6204 3099.20 3260.80  8882.3   500

### How long does it take to decide to do nothing?

    print(debug_bench, order = "median")
    #> Unit: microseconds
    #>           expr   min     lq      mean median      uq    max neval
    #>            cat   1.8   4.70    8.7438   6.10    7.00 1098.1   500
    #>          log4r   9.8  15.45   24.0120  19.45   23.65 1182.6   500
    #>            lgr  13.9  23.30   34.0922  29.65   35.05 1267.5   500
    #>        logging  28.6  41.25   56.1088  50.65   59.45 1205.0   500
    #>         logger  41.6  58.30   74.0618  67.15   81.55 1073.7   500
    #>  futile.logger 800.2 934.00 1038.9932 984.75 1034.80 5479.9   500