-
Notifications
You must be signed in to change notification settings - Fork 46
Description
I work with others, and we don't always have the same formatter set. We found recently that when my code (I tend to use formatter_json
) is logging with their non-json formatter, the message is lost.
For instance,
log_formatter(formatter_glue)
log_info("hello: {nrow(mtcars)}") # fashioned for formatter_glue
# INFO [2022-12-09 14:46:11] hello: 32
log_info(msg = "hello", n = nrow(mtcars)) # fashioned for formatter_json
# INFO [2022-12-09 14:46:11]
Where the last message is empty. If all of our code bases were using the same assumptions, then either all or none would have (say) msg=
as the main message. The issue is that it is a named argument, not that it's msg=
.
(One of my colleagues likes formatter_glue
, ergo my inclusion of {..}
above.)
It's not a problem in the reverse, a non-json-assuming log is sent with formatter_json
:
log_formatter(formatter_json)
log_info("hello: {nrow(mtcars)}") # fashioned for formatter_glue
# INFO [2022-12-09 14:46:21] ["hello: {nrow(mtcars)}"]
log_info(msg = "hello", n = nrow(mtcars)) # fashioned for formatter_json
# INFO [2022-12-09 14:46:21] {"msg":"hello","n":32}
I suggest either updating the formatters or supplying alternative functions, such as
formatter_glue2 <- function (..., .logcall = sys.call(), .topcall = sys.call(-1),
.topenv = parent.frame(), collapse = "; ") {
fail_on_missing_package("glue")
paste(
sapply(list(...), function(dot) {
tryCatch(as.character(glue::glue(dot, .envir = .topenv)),
error = function(e) {
stop(paste("`glue` failed in `formatter_glue` on:\n\n",
capture.output(str(dot)), "\n\nRaw error message:\n\n",
e$message, "\n\nPlease consider using another `log_formatter` or",
"`skip_formatter` on strings with curly braces."))
})
}),
collapse = collapse)
}
log_formatter(formatter_glue2)
log_info("hello: {nrow(mtcars)}")
# INFO [2022-12-09 14:48:50] hello: 32
log_info(msg = "hello", n = nrow(mtcars))
# INFO [2022-12-09 14:48:50] hello; 32
(No missing messages.) Since one of my colleagues prefers formatter_glue
, I focused on that formatter, but this could apply to others as well.
As one more step, it would be nice if my colleague were able to see all of the components send in my messages, where I often have one or more named components (in lieu of encoding within a string, where json makes it much easier to programmatically consume later). What about:
formatter_json_glue <- function(..., .logcall = sys.call(), .topcall = sys.call(-1),
.topenv = parent.frame()) {
logger::fail_on_missing_package("jsonlite")
dots <- list(...)
if (is.null(nms <- names(dots))) nms <- rep("", length(dots))
if (any(!nzchar(nms))) {
nms[!nzchar(nms)] <- paste0("arg", seq_along(dots))[!nzchar(nms)]
}
names(dots) <- nms
eval(as.character(jsonlite::toJSON(
lapply(dots, function(dot) tryCatch(glue::glue(dot, .envir = .topenv), error = function(ign) dot)),
auto_unbox = TRUE)),
envir = .topenv)
}
logger::log_formatter(formatter_json_glue)
logger::log_info("hello") # fashioned for any formatter
# INFO [2022-12-09 14:52:55] {"arg1":"hello"}
logger::log_info("hello: {nrow(mtcars)}") # fashioned for formatter_glue
# INFO [2022-12-09 14:52:55] {"arg1":"hello: 32"}
logger::log_info(msg = "hello: {nrow(mtcars)}", n = nrow(mtcars))
# INFO [2022-12-09 14:52:55] {"msg":"hello: 32","n":"32"}