Structured Logging

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
library(log4r)

# Dummy Plumber request & response.
req <- list(
  REQUEST_METHOD = "POST", PATH_INFO = "/upload",
  QUERY_STRING = "", HTTP_USER_AGENT = "curl/7.58.0",
  REMOTE_ADDR = "124.133.52.161"
)
res <- list(status = 401)

# Dummy CSV-parsing stuff:
filename <- "catpics_01.csv"
entries <- data.frame(x = 1:4124)
start <- Sys.time()

Traditionally, messages emitted from R packages or scripts are unstructured messages, like this one from the shiny package:

Listening on http://localhost:8080

A richer, more structured representation of this log message might be:

level=INFO ts=2021-10-21T20:21:01Z message="starting Shiny" host=localhost port=8080 shiny_version=1.6.0 appdir=projects/mycoolapp

This second message uses structured logging, attaching relevant metadata to a log message as standalone fields.

Structured logs have two advantages:

log4r includes support for two of the most popular structured logging formats:

To use these formats, you can pass additional arguments to the existing logging functions info(), warn() and error().

JSON Logs

The most popular format for structured logging is probably JSON, which you can configure as follows:

logger <- logger(appenders = console_appender(json_log_layout()))

As an example, suppose you are logging unauthorised requests to a Plumber API. You might have a log message with fields like the following:

# Here "req" and "res" are slightly fake request & response objects.
info(
  logger, message = "authentication failed",
  method = req$REQUEST_METHOD,
  path = req$PATH_INFO,
  params = sub("^\\?", "", req$QUERY_STRING),
  user_agent = req$HTTP_USER_AGENT,
  remote_addr = req$REMOTE_ADDR,
  status = res$status
)

logfmt Logs

An alternative to JSON is the popular, more human-friendly logfmt style, which you can configure as follows:

logger <- logger(appenders = console_appender(logfmt_log_layout()))

As an example, you might have the following in a script that processes many CSV files:

info(
  logger, message = "processed entries", file = filename,
  entries = nrow(entries),
  elapsed = unclass(difftime(Sys.time(), start, units = "secs"))
)


Try the log4r package in your browser

Any scripts or data that you put into this service are public.

log4r documentation built on Nov. 4, 2021, 9:09 a.m.