library(dyn.log)
configs <- dyn.log::get_configurations()
# set logging config with tweaks for knitr output
init_logger(configs$knitr)
## [90m[1mTRACE[22m[39m [90m[3m[11/09/2024 03:37:53 +0000][23m[39m [90mdyn.log loaded 'knitr' configuration successfully.[39m
Format objects are the driver of customization in log rendering. Log layouts were developed with the composition design pattern in mind; a log layout is simply a series of formats that get evaluated with their associated context to form a log message.
All formats derive from the fmt_layout base type and have a couple of generics associated with them, specifically: style and value. The fmt_layout is meant to be an abstract base type; by driving from it the logging framework can make some assumptions about how to treat a format object.
There are five main categories of log format objects:
The values available for a fmt_metric type can be accessed via sys_context:
sys_context()
$sysname
[1] "Linux"
$release
[1] "6.5.0-1025-azure"
$version
[1] "#26~22.04.1-Ubuntu SMP Thu Jul 11 22:33:04 UTC 2024"
$nodename
[1] "4a1c574c3f41"
$machine
[1] "x86_64"
$login
[1] "unknown"
$user
[1] "root"
$effective_user
[1] "root"
$r_ver
[1] "4.4.2"
attr(,"class")
[1] "sys_context" "context"
new_log_layout(
format = list(
new_fmt_log_level(),
new_fmt_metric(crayon::green$bold, "sysname"),
new_fmt_metric(crayon::yellow$bold, "release"),
new_fmt_timestamp(crayon::silver$italic, "[%x %H:%M:%S]"),
new_fmt_log_msg()
),
seperator = '-',
association = "ex-sys-layout"
)
var1 <- "abc"; var2 <- 123; var3 <- round(runif(1), digits = 6)
Logger$debug("my log message - var1: {var1}, var2: {var2}, var3: {var3}",
layout = "ex-sys-layout")
DEBUG-Linux-6.5.0-1025-azure-[11/09/2024 03:37:53]-my log message - var1: abc, var2: 123, var3: 0.67086
As you can see, the log message has a great deal of detail, but difficult to interpret due to the amount of information jammed into one line. This is where literals and new lines come into play.
Literals and new lines are simple formatting objects that help you tweak the layout of a log message to something that is both informative and easy to consume. Taking the previous example, and tweaking the format slightly incorporating literals & new lines, we can produce a log message like this:
new_log_layout(
format = list(
new_fmt_metric(crayon::green$bold, "sysname"),
new_fmt_literal(crayon::magenta, "["),
new_fmt_metric(crayon::blue$bold, "release"),
new_fmt_literal(crayon::magenta, "]"),
new_fmt_line_break(),
new_fmt_log_level(),
new_fmt_timestamp(crayon::silver$italic, "[%x %H:%M:%S]"),
new_fmt_log_msg()
),
seperator = ' ',
association = "ex-syslit-layout"
)
var1 <- "abc"; var2 <- 123; var3 <- round(runif(1), digits = 6)
Logger$debug("my log message - var1: {var1}, var2: {var2}, var3: {var3}",
layout = "ex-syslit-layout")
Linux [ 6.5.0-1025-azure ]
DEBUG [11/09/2024 03:37:53] my log message - var1: abc, var2: 123, var3: 0.250829
Which has the same information as the previous example, but much easier to consume.
Execution scope formats give you the ability to log the context around the invocation of the logger, and is a context object, much like sys_context, called exec_context:
test <- function(a, b, c) {
wrapper <- function(x, y, z) {
outer <- function(d, e, f) {
inner <- function(g, h, i) {
# call_subset is used here to skip past knitr execution calls
exec_context(max_calls = 30, call_subset = c(callstack_settings$start,
callstack_settings$stop))
}
inner(d, e, f)
}
outer(x, y, z)
}
wrapper(a, b, c)
}
exec_context <- test(1,2,3)
exec_context
$call_stack
call_1 call_2 call_3 call_4
NA NA NA NA
call_5 call_6 call_7 call_8
NA NA NA NA
call_9 call_10 call_11 call_12
NA NA NA NA
call_13 call_14 call_15
NA NA "get_call_stack"
attr(,"class")
[1] "call_stack" "stack"
$calling_fn
[1] "get_call_stack"
$ncalls
[1] 15
attr(,"class")
[1] "exec_context" "context"
The evaluated exec_context gives you a structure with these 3 fields:
The execution scope can be accessed via the new_fmt_exec_scope format object, e.g.:
new_log_layout(
format = list(
new_fmt_metric(crayon::green$bold, 'sysname'),
new_fmt_metric(crayon::blue$yellow, 'release'),
new_fmt_line_break(),
new_fmt_log_level(),
new_fmt_timestamp(crayon::silver$italic, '[%x %H:%M:%S]'),
new_fmt_literal(crayon::magenta$bold, 'fn('),
new_fmt_exec_scope(crayon::magenta$bold, 'calling_fn'),
new_fmt_literal(crayon::magenta$bold, ')'),
new_fmt_log_msg(),
new_fmt_line_break(),
new_fmt_exec_scope(crayon::bgYellow$blue$bold, 'call_stack')
),
seperator = '-',
association = 'ex-sysexec-cs-layout'
)
local_fn <- function() {
outer <- function() {
inner <- function() {
var1 <- "abc"; var2 <- 123; var3 <- round(runif(1), digits = 6)
Logger$debug("my log message - var1: '{var1}', var2: '{var2}', var3: '{var3}'",
layout = 'ex-sysexec-cs-layout')
}
inner()
}
outer()
}
local_fn()
Linux-6.5.0-1025-azure
DEBUG-[11/09/2024 03:37:53]-fn(-get_call_stack-)-my log message - var1: 'abc', var2: '123', var3: '0.109827'
NA-NA-NA-NA-NA-NA-NA-NA-NA-NA-NA-NA-NA-NA-get_call_stack