loggit2
is an easy-to-use, yet powerful, ndjson
logger. It is
very fast, has zero external dependencies, and can be as straightforward
or as integral as you want to make it.
No preparations are needed to use loggit2
. However, it
is recommended to explicitly set a log file using
loggit2::set_logfile("path/to/your/file")
, as
loggit2
defaults to creating a file in your temporary
directory.1
In order to use the full potential of loggit2
, it is
advisable to take a look at the further configurations after
reading this vignette.
There are three ways to populate the log in loggit2
.
First, through wrapper functions of the base R
condition
handler, second, via the loggit()
log function, and third,
by logging (external) expressions using with_loggit()
.
Each function of these three methods has the parameter
echo
, which determines whether the log entries should also
be echoed to stdout
.
loggit2
provides a set of wrappings for base R’s
message()
, warning()
, stop()
and
stopifnot()
functions that maintain identical
functionality2, except the additional logging. Thus, it is
sufficient to import the loggit2
namespace, for example by
using library("loggit2")
, or by prefixing
loggit2::
at the desired locations.
base::message("This is another message")
#> This is another message
loggit2::message("This is a message")
#> {"timestamp": "2024-07-25T18:16:39+0200", "log_lvl": "INFO", "log_msg": "This is a message\n"}
#> This is a message
base::warning("This is another warning")
#> Warning: This is another warning
loggit2::warning("This is a warning")
#> {"timestamp": "2024-07-25T18:16:39+0200", "log_lvl": "WARN", "log_msg": "This is a warning"}
#> Warning: This is a warning
base::stop("This is another error")
#> Error in eval(expr, envir, enclos): This is another error
loggit2::stop("This is an error")
#> {"timestamp": "2024-07-25T18:16:39+0200", "log_lvl": "ERROR", "log_msg": "This is an error"}
#> Error in eval(expr, envir, enclos): This is an error
base::stopifnot("This is another condition" = FALSE)
#> Error: This is another condition
loggit2::stopifnot("This is another condition" = FALSE)
#> {"timestamp": "2024-07-25T18:16:39+0200", "log_lvl": "ERROR", "log_msg": "This is another condition"}
#> Error: This is another condition
Besides the echo
parameter, the functions have an
additional parameter .loggit
, which allows to deactivate
the logging such that the function behaves exactly like the base
R
equivalents.
loggit2::warning("This is a alternative warning", echo = FALSE)
#> Warning: This is a alternative warning
loggit2::warning("This is not part of the log", .loggit = FALSE)
#> Warning: This is not part of the log
#> timestamp log_lvl log_msg
#> 1 2024-07-25T18:16:38+0200 INFO This message will be logged, since the log level is INFO.\n
#> 2 2024-07-25T18:16:38+0200 DEBUG This message will be logged because the log level is ignored.
#> 3 2024-07-25T18:16:38+0200 INFO This message will be logged, since .loggit = TRUE.\n
#> 4 2024-07-25T18:16:38+0200 ERROR This error message will be logged because the log level is set to ERROR.
#> 5 2024-07-25T18:16:38+0200 INFO This message will be logged because the log level is set to DEBUG.\n
#> 6 2024-07-25T18:16:38+0200 INFO This message will not be logged, but it will be output to the console.\n
#> 7 2024-07-25T18:16:38+0200 INFO This message will be logged and output to the console.\n
#> 8 2024-07-25T18:16:38+0200 INFO This message will be logged and output to the console.\n
#> 9 2024-07-25T18:16:38+0200 INFO This message will be logged, but it will not be echoed.\n
#> 10 2024-07-25T18:16:38+0200 INFO This message will be logged, but it will not be output to the console.\n
#> 11 18:16:38 INFO This message will be logged with a timestamp in the format HH:MM:SS.\n
#> 12 2024-07-25T18:16:39+0200 INFO This is a message\n
#> 13 2024-07-25T18:16:39+0200 WARN This is a warning
#> 14 2024-07-25T18:16:39+0200 ERROR This is an error
#> 15 2024-07-25T18:16:39+0200 ERROR This is another condition
#> 16 2024-07-25T18:16:39+0200 WARN This is a alternative warning
loggit2
allows direct access to the core logging
function loggit()
. This enables setting the log level
directly during the call and creating arbitrary fields in the log.
This function does not trigger any conditions; it only populates the log.
loggit2::loggit("INFO", "This is a message", ID = 1L, boole = TRUE)
#> {"timestamp": "2024-07-25T18:16:39+0200", "log_lvl": "INFO", "log_msg": "This is a message", "ID": "1", "boole": "TRUE"}
loggit2::loggit("WARN", "This is a alternative warning", echo = FALSE)
loggit2::loggit("DEBUG", "This is a message", Why = "Because", echo = FALSE)
To allow log levels other than “DEBUG”, “INFO”, “WARN” or “ERROR” the
custom_log_lvl
parameter must be set.
loggit2::loggit("CRITICAL", "Critical error")
#> Error in loggit2::loggit("CRITICAL", "Critical error"): Nonstandard log_lvl ('CRITICAL').
#> Should be one of DEBUG, INFO, WARN, or ERROR. Please check if you made a typo.
#> If you insist on passing a custom level, please set 'custom_log_lvl = TRUE' in the call to 'loggit()'.
loggit2::loggit("CRITICAL", "Critical error 2", custom_log_lvl = TRUE)
#> {"timestamp": "2024-07-25T18:16:39+0200", "log_lvl": "CRITICAL", "log_msg": "Critical error 2"}
#> timestamp log_lvl log_msg ID boole Why
#> 1 2024-07-25T18:16:39+0200 INFO This is a message 1 TRUE <NA>
#> 2 2024-07-25T18:16:39+0200 WARN This is a alternative warning <NA> <NA> <NA>
#> 3 2024-07-25T18:16:39+0200 DEBUG This is a message <NA> <NA> Because
#> 4 2024-07-25T18:16:39+0200 CRITICAL Critical error 2 <NA> <NA> <NA>
One will repeatedly encounter situations where conditions need to be
logged from code without wanting to or being able to modify it (e.g.,
when dealing with functions from external packages). In this case,
with_loggit()
comes into play. This function allows logging
conditions from arbitrary expressions without restricting
functionality3 or needing to modify the code.
fun_a <- function(x) {
base::warning("This is a warning")
base::message("This is a message")
base::stopifnot("This is true" = 3L == 1L + 2L, "This is not TRUE" = FALSE)
}
fun_b <- function(x) {
base::warning("This is a second warning")
5L + 5L
}
x <- loggit2::with_loggit(fun_b())
#> {"timestamp": "2024-07-25T18:16:39+0200", "log_lvl": "WARN", "log_msg": "This is a second warning"}
#> Warning in fun_b(): This is a second warning
print(x)
#> [1] 10
loggit2::with_loggit({
x <- fun_b()
fun_a()
}, echo = FALSE)
#> Warning in fun_b(): This is a second warning
#> Warning in fun_a(): This is a warning
#> This is a message
#> Error in fun_a(): This is not TRUE
Additionally, with_loggit()
allows alternative settings
(logfile, echo, etc.) to be used for a specific section of code.
#> timestamp log_lvl log_msg
#> 1 2024-07-25T18:16:39+0200 WARN This is a second warning
#> 2 2024-07-25T18:16:39+0200 WARN This is a second warning
#> 3 2024-07-25T18:16:39+0200 WARN This is a warning
#> 4 2024-07-25T18:16:39+0200 INFO This is a message\n
#> 5 2024-07-25T18:16:39+0200 ERROR This is not TRUE
A log is of little use without the ability to access and modify it. Here are a few possibilities.
As seen above, the log can be queried as a data.frame
using read_logs()
.
loggit2::read_logs()
#> timestamp log_lvl log_msg
#> 1 2024-07-25T18:16:39+0200 WARN This is a second warning
#> 2 2024-07-25T18:16:39+0200 WARN This is a second warning
#> 3 2024-07-25T18:16:39+0200 WARN This is a warning
#> 4 2024-07-25T18:16:39+0200 INFO This is a message\n
#> 5 2024-07-25T18:16:39+0200 ERROR This is not TRUE
Alternatively, the log can also be saved as a CSV file using
convert_to_csv()
.
To maintain a clear log even in long-running sessions (e.g., in a
Shiny app hosted on a server), the log can be restricted to the last
n
entries using rotate_logs(n)
.
#> timestamp log_lvl log_msg
#> 1 2024-07-25T18:16:39+0200 INFO This is a message\n
#> 2 2024-07-25T18:16:39+0200 ERROR This is not TRUE
#> [1] timestamp log_lvl log_msg
#> <0 Zeilen> (oder row.names mit Länge 0)
This is done to CRAN Repository Policy:
↩︎Packages should not write in the user’s home filespace (including clipboards), nor anywhere else on the file system apart from the R session’s temporary directory
[...]
.
This means in particular that tryCatch
and
similar functions can be used as usual.↩︎
Just like with the direct use of the wrappers for
condition handlers, tryCatch
and similar mechanisms can be
used as usual.↩︎