Writing Custom Logger Extensions
Source:vignettes/write_custom_extensions.Rmd
write_custom_extensions.Rmd
In this vignette I suppose that you are already familiar with Customizing the format and destination of log records vignette.
Custom log message formatter functions
The log message formatter function should be able to take any number of R objects and convert those into a character vector that is safe to pass to the layout function.
This transformer function can be as simple as calling
paste
, glue
or sprintf
or
something complex as well, eg looking up user attributes of a user id
mentioned in a log record etc.
When writing a custom formatter function, it should also accept the
original logging function call as .logcall
, the parent call
as .topcall
and the log request’s environment as
.topenv
, which can be used to find the relevant variables
in your formatter.
If you are writing such a function or a generator function returning
a log message formatter function, please keep the actual call resulting
in the formatter function (eg match.call()
in the generator
function or the quoted function call) recorded in the
generator
attribute of the function so that
?log_formatter
can pretty-print that instead of the unnamed
function body. See the formatters.R
file for more examples.
Custom log layout rendering functions
The layout functions return the log record and take at least two arguments:
- the log level and
- a message already formatted as a string by a log message formatter function
- the namespace (as
namespace
), calling function (as.topcall
) and its environment (as.topenv
) of the log request, and the actual log call (as.logcall
) automatically collected by?log_level
Such layout rendering function can be as simple as the default
?layout_simple
:
layout_simple <- function(level, msg, ...) {
paste0(attr(level, 'level'), ' [', format(Sys.time(), "%Y-%m-%d %H:%M:%S"), '] ', msg)
}
Or much more complex, eg looking up the hostname of the machine, public IP address etc and logging all these automatically with the message of the log request.
Your easiest option to set up a custom layout is calling
?layout_glue_generator
that comes with a nice API being
able to access a bunch of meta-information on the log request via
?get_logger_meta_variables
. For such example, see the Customizing
the format and destination of log records vignette.
If you are writing such a function or a generator function returning
a log message formatter function, please keep the actual call resulting
in the formatter function (eg match.call()
in the generator
function or the quoted function call) recorded in the
generator
attribute of the function so that
?log_layout
can pretty-print that instead of the unnamed
function body. See the layouts.R
file for more examples.
Custom log record appenders
The appender functions take log records and delivers those to the
desired destination. This can be as simple as writing to the console
(?appender_console
) or to a local file
(?appender_file
), or delivering the log record via an API
request to a remote service, streaming somewhere or sending a Slack
message (?appender_slack
).
If you are writing such a function or a generator function returning
a log message formatter function, please keep the actual call resulting
in the formatter function (eg match.call()
in the generator
function or the quoted function call) recorded in the
generator
attribute of the function so that
?log_appender
can pretty-print that instead of the unnamed
function body. See the appenders.R
file for more examples.
An example for a custom appender delivering log messages to a database table:
## the dbr package provides and easy and secure way of connecting to databased from R
## although if you want to minimize the dependencies, feel free to stick with DBI etc.
library(dbr)
## init a persistent connection to the database using a yaml config in the background thanks to dbr
## NOTE that this is optional and a temporarily connection could be also used
## for higher reliability but lower throughput
con <- db_connect('mydb')
## define custom function writing the log message to a table
log_appender(function(lines) {
db_append(
df = data.frame(timestamp = Sys.time(), message = lines),
table = 'logs', db = con)
})