---
title: "Migration Guide"
vignette: >
%\VignetteIndexEntry{Migration Guide}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{css, echo=FALSE}
div.comparison {
width: 49%;
display: inline-block;
vertical-align: top;
}
div.comparison p package {
font-family: monospace;
color: brown;
}
div#tocnav ul.nav li ul {
padding-left: 10px;
}
```
```{r pkgchecks, echo = FALSE}
## check if other logger packages are available and exit if not
for (pkg in c("futile.logger", "logging", "log4r")) {
if (!requireNamespace(pkg, quietly = TRUE)) {
warning(paste(pkg, "package not available, so cannot build this vignette"))
knitr::knit_exit()
}
}
```
```{r setup, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
## load the main package first
library(logger)
log_appender(appender_stdout)
```
In this vignette I suppose that you are already familiar with at least one of the [similar logging R packages](https://daroczig.github.io/logger/index.html#why-another-logging-r-package) and you are looking for suggestions on how to switch to `logger`. Before moving forward, please make sure that you have read the [Introduction to logger](https://daroczig.github.io/logger/articles/Intro.html), [The Anatomy of a Log Request](https://daroczig.github.io/logger/articles/anatomy.html) and [Customizing the Format and the Destination of a Log Record](https://daroczig.github.io/logger/articles/customize_logger.html) vignettes for a decent background on `logger`, and use this vignette as a quick-reference sheet to help you migrate from another package.
## futile.logger
The `logger` package has been very heavily inspired by [`futile.logger`](https://cran.r-project.org/package=futile.logger) and have been using it for many years, also opened multiple pull requests to extend `futile.logger` before I decided to revamp my ideas into a new R package -- but there are still many common things between `futile.logger` and `logger`.
### Initialize
Both packages comes with a default log engine / config, so it's enough to load the packages and those are ready to be used right away:
futile.logger
```{r}
library(futile.logger)
```
logger
```{r}
library(logger)
```
### Logging functions
The most important change is that function names are by snake_case in `logger`, while `futile.logger` uses dot.separated expressions, and `futile.logger` prefixes function names by `flog` while `logger` uses `log` for that:
futile.logger
```{r}
flog.info("hi there")
flog.warn("watch out")
```
logger
```{r}
log_info("hi there")
log_warn("watch out")
```
As you can see above, the default layout of the messages is exactly the same.
### Log levels
Regarding log levels, `futile.logger` bundles the default `log4j` levels (`TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR` and `FATAL`) that is extended by `SUCCESS` in `logger` as sometimes it's worth logging with a higher than `INFO` level that something succeeded.
### Log record layout
Changing layouts is easy in both package, as you simply pass a layout function:
futile.logger
```{r}
flog.layout(layout.json)
flog.info("hi again")
```
logger
```{r}
log_layout(layout_json())
log_info("hi again")
```
As you can see, `logger` provided a bit more information about the log request compared to `futile.logger`, but it's easy to change the list of fields to be used in the JSON -- see `?get_logger_meta_variables` for a complete list of variable names to be passed to `?layout_json`. `logger` also ships a lot more layouts, eg `?layout_glue_colors` or roll out your own via the `?layout_glue_generator` factory function.
```{r echo=FALSE, results='hide'}
flog.layout(layout.simple)
log_layout(layout_simple)
```
### Log message formatting
By default, `futile.logger` uses an `sprintf` formatter, while `logger` passes the objects to be logged to `glue`:
futile.logger
```{r}
flog.info("hi")
flog.info("hi %s", 84 / 2)
flog.info(paste("hi", 84 / 2))
flog.info(glue::glue("hi {84/2}"))
```
logger
```{r}
log_info("hi")
log_info("hi {84/2}")
log_formatter(formatter_sprintf)
log_info("hi %s", 84 / 2)
log_formatter(formatter_paste)
log_info("hi", 84 / 2)
```
```{r echo=FALSE, results='hide'}
log_formatter(formatter_glue)
```
It's easy to change this default formatter in both packages: use `flog.layout` handles this as well in `futile.logger`, while the formatter is separated from the layout function in `logger`, so check `?log_formatter` instead. `logger` ships with a bit more formatter functions, eg the default `?formatter_glue` and `?formatter_glue_or_sprintf` that tries to combine the best from both words.
### Log record destination
Setting the destination of the log records works similarly in both packages, although he `logger` packages bundles a lot more options:
logging
```{r}
t <- tempfile()
flog.appender(appender.file(t))
flog.appender(appender.tee(t))
```
logger
```{r}
t <- tempfile()
log_appender(appender_file(t))
log_appender(appender_tee(t))
```
```{r echo=FALSE, results='hide'}
flog.appender(appender.console)
log_appender(appender_stdout)
```
### Hierarchical logging and performance
Both packages support using different logging namespaces and stacking loggers within the same namespace. Performance-wise, there's `logger` seems to be faster than `futile.logger`, but for more details, check the [Simple Benchmarks on Performance](https://daroczig.github.io/logger/articles/performance.html) vignette.
### Using `logger` as a drop-in-replacement of `futile.logger`
`logger` has no hard requirements, so it's a very lightweight alternative of `futile.logger`. Although the function names are a bit different, and the message formatter also differs, but with some simple tweaks, `logger` can become an almost perfect drop-in-replacement of `futile.logger`:
```{r}
library(logger)
log_formatter(formatter_sprintf)
flog.trace <- log_trace
flog.debug <- log_debug
flog.info <- log_info
flog.warn <- log_warn
flog.error <- log_error
flog.info("Hello from logger in a futile.logger theme ...")
flog.warn("... where the default log message formatter is %s", "sprintf")
```
## logging
The [`logging`](https://cran.r-project.org/package=logging) package behaves very similarly to the Python logging module and so thus being pretty Pythonic, while `logger` tries to accommodate native R users' expectations -- so there are some minor nuances between the usage of the two packages.
### Initialize
In `logging`, you have to initialize a logger first via `addHandler` or simply by calling `basicConfig`, which is not required in `logger` as it already comes with a default log config:
logging
```{r}
library(logging)
basicConfig()
```
logger
```{r}
library(logger)
```
### Logging functions
After initializing the logging engine, actual logging works similarly in the two packages -- with a bit different function names:
* although `logging` uses mostly camelCase function names (eg `basicConfig`), but the logging functions are all lowercase without any separator, such as `loginfo` or `logwarn`
* `logger` uses snake_case for the function names, such as `log_info` and `log_warn`
logging
```{r}
loginfo("hi there")
logwarn("watch out")
```
logger
```{r}
log_info("hi there")
log_warn("watch out")
```
As you can see above, the default layout of the log messages is somewhat different:
* `logging` starts with the timestamp that is followed by the log level, optional namespace and the message separated by colons
* `logger` starts with the log level, followed by the timestamp between brackets and then the message
### Log levels
For the available log levels in `logging`, check `?loglevels`, and `?log_levels` for the same in `logger`:
logging
```{r}
str(as.list(loglevels))
```
logger
```{r}
levels <- mget(rev(logger:::log_levels_supported), envir = asNamespace("logger"))
str(levels, give.attr = FALSE)
```
### Performance
Performance-wise, there's no big difference between the two packages, but for more details, check the [Simple Benchmarks on Performance](https://daroczig.github.io/logger/articles/performance.html) vignette.
### Log record layout
Getting and setting the layout of the log record should happen up-front in both packages:
logging
```{r}
getLogger()[["handlers"]]$basic.stdout$formatter
```
logger
```{r}
log_layout()
```
`logger` provides multiple configurable layouts to fit the user's need, eg easily show the calling function of the lof request, the `pid` of the R process, name of the machine etc. or colorized outputs. See [Customizing the Format and the Destination of a Log Record](https://daroczig.github.io/logger/articles/customize_logger.html) vignette for more details.
### Log message formatting
If you want to pass dynamic log messages to the log engines, you can do that via the hard-coded `sprintf` in the `logging` package, while you can set that on a namespaces basis in `logger`, which is by default using `glue`:
logging
```{r}
loginfo("hi")
loginfo("hi %s", 84 / 2)
loginfo(paste("hi", 84 / 2))
loginfo(glue::glue("hi {84/2}"))
```
logger
```{r}
log_info("hi")
log_info("hi {84/2}")
log_formatter(formatter_sprintf)
log_info("hi %s", 84 / 2)
log_formatter(formatter_paste)
log_info("hi", 84 / 2)
```
For even better compatibility, there's also `?formatter_logging` that not only relies on `sprintf` when the first argument is a string, but will log the call and the result as well when the log object is an R expression:
```{r}
log_formatter(formatter_logging)
log_info("42")
log_info(42)
log_info(4 + 2)
log_info("foo %s", "bar")
log_info(12, 1 + 1, 2 * 2)
```
```{r echo=FALSE, results='hide'}
log_formatter(formatter_glue)
```
### Log record destination
Setting the destination of the log records works similarly in both packages, although he `logger` packages bundles a lot more options:
logging
```r
?addHandler
?writeToConsole
?writeToFile
```
logger
```r
?log_appender
?appender_console
?appender_file
?appender_tee
?appender_slack
?appender_pushbullet
```
### Hierarchical logging
Both packages support using different logging namespaces and stacking loggers within the same namespace.
### Using `logger` as a drop-in-replacement of `logging`
`logger` has no hard requirements, so it's an adequate alternative of `logging`. Although the function names are a bit different, and the message formatter also differs, but with some simple tweaks, `logger` can become an almost perfect drop-in-replacement of `logging` -- although not all log levels (eg \code{FINE} and \code{CRITICAL}) are supported:
```{r}
library(logger)
log_formatter(formatter_logging)
log_layout(layout_logging)
logdebug <- log_debug
loginfo <- log_info
logwarn <- log_warn
logerror <- log_error
loginfo("Hello from logger in a logging theme ...")
logwarn("... where the default log message formatter is %s", "sprintf", namespace = "foobar")
```
## log4r
The [`log4r`](https://cran.r-project.org/package=log4r) package provides an object-oriented approach for logging in R, so the logger object is to be passed to the log calls -- unlike in the `logger` package.
### Initialize
So thus it's important to create a logging object in `log4r` before being able to log messages, while that's automatically done in `logger:
log4r
```{r}
library(log4r)
logger <- create.logger(logfile = stdout(), level = "INFO")
```
logger
```{r}
library(logger)
```
Please note that in the background, `logger` does have a concept of logger objects, but that's behind the scene and the user does not have to specify / reference it. On the other hand, if you wish, you can do that via the `namespace` concept of `logger` -- more on that later.
### Logging functions
While `logger` has a `log_` prefix for all logging functions, `log4r` has lowercase functions names referring to the log level, which takes a logging object and the log message:
log4r
```{r}
info(logger, "hi there")
warn(logger, "watch out")
```
logger
```{r}
log_info("hi there")
log_warn("watch out")
```
As you can see the default layout of the messages is a bit different in the two packages.
### Log levels
Both packages are based on `log4j`, and `log4r` provides `DEBUG`, `INFO`, `WARN`, `ERROR` and `FATAL`, while `logger` also adds `TRACE` and `SUCCESS` on the top of these.
To change the log level threshold, use the `level` function on the logging object in `log4r`, while it's `log_level` in `logger`.
### Log record layout and formatter functions
The `log4r` provides a `logformat` argument in `create.logger` that can be used to override the default formatting, while `logger` provides formatter and layout functions for a flexible log record design.
### Log record destination
By default, `log4r` logs to a file that can be set to `stoud` to write to the console, while `logger` writes to the console by default, but logging to files via the `appender_file` functions is also possible -- besides a number of other log record destinations as well.
### Hierarchical logging and performance
Creating objects is the `log4r` way of handling multiple log environments, while `logger` handles that via `namespace`s.
## loggit
Sorry, no direct replacement for [`loggit`](https://cran.r-project.org/package=loggit) -- capturing `message`, `warning` and `stop` function messages, but it's on the [roadmap](https://github.com/daroczig/logger/issues/6) to provide helper functions to be used as message hooks feed `logger`.
```{r cleanup, include = FALSE}
logger:::namespaces_reset()
detach("package:logger", unload = TRUE)
detach("package:futile.logger", unload = TRUE)
detach("package:logging", unload = TRUE)
```