Add convenience functions to activate other major logging packages
#10 introduced the possibility to inject your own logging functions.
There are many logging packages at CRAN now.
It would be very convenient if tryCatchLog provides functions to activate one of these logging packages with a simple function call.
List of known logging frameworks for R (updated in-place):
- plogr allows logging within C++ code into R' standard error stream (via the
REprintf()function. This logging framework seems not to be meant for R code itself!. - futile.logger: logging to files, console
- logging: Powerful R port of the popular Python logging package. Logging to console, file, sentry server etc.
- logger (by daroczik): A lightweight, modern and flexibly logging utility for R -- heavily inspired by the futile.logger R package and logging Python module. Under active development (put on watch list ;-). Lists many other logging frameworks.
- debugme ("zero"-overhead logging and easy debug strings with embedded R code!)
- logR: transactional logging to databases and email notifications
- loggr: logging to files, console
- logr: logging to files
- dtq: detailed auditing of data.table queries
- loggit
- rlogging
- log4r
- rsyslog for POSIX-compatible OSes
- lgr: a logging package for R built on the back of R6 classes. Supports logging of data in objects.
See also:
https://www.r-bloggers.com/effortless-but-powerful-exception-logging-in-r-loggit-1-0-0-released-on-cran/
CRAN download statistics:
library(cranlogs)
library(data.table)
res <- cran_downloads(when = "last-month", packages = c("futile.logger", "logging", "logR", "logr", "loggr", "dtq", "loggit", "rlogging", "log4r", "rsyslog", "luzlogr", "debugme", "plogr", "logger", "lgr"))
setDT(res)
res[, .(.N, downloads = sum(count)), by = .(package)][downloads > 0,][order(-downloads),]
shows two major packages and a newcomer (debugme) - last updated July 22, 2022 (with lgr, logR added):
package N downloads
1: futile.logger 30 178068
2: plogr 30 88917
3: logger 30 25768
4: logging 30 25201
5: lgr 30 14701
6: debugme 30 14253
7: log4r 30 13096
8: logr 30 1470
9: loggit 30 729
10: rsyslog 30 647
11: luzlogr 30 378
To support the logging package a simple call should work:
set.logging.functions(logging::logerror, logging::logwarn, logging::loginfo)
Required code changes:
- Modify the zzz.R for
loggingsupport as default (withfutile.loggerhaving higher prio) - Add convenience functions
use.log.pkg.futile.logger+use.log.pkg.logging.pkg+use.log.pkg.tryCatchLog(or "activate", "use", "enable", "set"... as prefix)
To experiment with the logging levels try
# default logging level is INFO
tryCatchLog(log(-1), silent.warnings = T)
logging::setLevel("WARN")
tryCatchLog(log(-1), silent.warnings = T)
logging::setLevel("ERROR")
tryCatchLog(log(-1), silent.warnings = T)
The debugme package is a good newcomer and supporting it as logging framework out-of-the-box looks straight-forward.
Implementation hints:
There is one central public debug function which supports printing log messages according to different severity levels by prepending a certain number of exclamation marks:
get_log_levels <- function() {
c(FATAL = 1,
ERROR = 2,
WARNING = 3,
INFO = 4,
DEBUG = 5,
VERBOSE = 6
)
}
get_msg_debug_levels <- function(x) {
m <- re_match(x, "^!DEBUG-(?<level>[^\\s]+)\\s+")
if (! is.na(m$.match)) {
wh <- match(m$level, names(get_log_levels()))
if (is.na(wh)) {
warning("Unknown debug level: `", m$level, "`")
0
} else {
wh
}
} else {
m <- re_match(x, "^(!+)DEBUG\\s+")
if (is.na(m[1,1])) 0 else nchar(m[1,1])
}
}
Source: https://github.com/r-lib/debugme/blob/master/R/debug.R
Log levels are documented in the vignette:
https://cran.r-project.org/web/packages/debugme/debugme.pdf
To organize the log messages into log levels, you can start the !DEBUG token with multiple ! char-acters. You can then select the desired level of logging via ! characters before the package name in the DEBUGME environment variable. E.g. DEBUGME=!!mypackagemeans that only debug messages with two or less ! marks will be printed.
Currently it is also not possible to switch between different logging frameworks in tryCatchLog except calling the "low-level" tryCatchLog::set.logging.functions().
If would be good to provide convenience functions to have an explicit public API, eg.:
set.logging.framework.internal() # tryCatchLog = same as calling tryCatchLog::set.logging.functions() with the default arguments
set.logging.framework.futile.logger()
set.logging.framework.xyz()
I have added the logger package (https://github.com/daroczig/logger) to the watch list and updated the CRAN download statstics:
library(cranlogs)
library(data.table)
res <- cran_downloads(when = "last-month", packages = c("futile.logger", "logging", "logR", "logr", "loggr", "dtq", "loggit", "rlogging", "log4r", "rsyslog", "luzlogr", "debugme", "plogr", "logger"))
setDT(res)
res[, .(.N, downloads = sum(count)), by = .(package)][downloads > 0,][order(-downloads),]
# Dec 14, 2021:
# package N downloads
# 1: futile.logger 30 121455
# 2: plogr 30 94182
# 3: logger 30 23239
# 4: logging 30 20735
# 5: log4r 30 18342
# 6: debugme 30 11725
# 7: loggit 30 5176
# 8: logr 30 1079
# 9: rsyslog 30 790
# 10: luzlogr 30 467
The major preparation for supporting more logging frameworks was done with #74 for lgr by extending the API (and improving the internal code for that)...