diff --git a/R/logger.R b/R/logger.R index b28fa624..d8d47b1a 100644 --- a/R/logger.R +++ b/R/logger.R @@ -30,9 +30,7 @@ #' function), and a list of `handlers` with the `formatter`, #' `layout` and `appender` functions. #' @export -#' @references For more details, see the Anatomy of a Log Request -#' vignette at -#' . +#' @references For more details, see vignette("customize_logger") #' @note It's quite unlikely that you need to call this function #' directly, but instead set the logger parameters and functions at #' [log_threshold()], [log_formatter()], [log_layout()] and diff --git a/README.Rmd b/README.Rmd index 17a303ae..1a372e8d 100644 --- a/README.Rmd +++ b/README.Rmd @@ -106,13 +106,12 @@ Welcome to the [Bazaar](https://en.wikipedia.org/wiki/The_Cathedral_and_the_Baza Check out the main documentation site at or the vignettes on the below topics: -* [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) -* [Customizing the Format and the Destination of a Log Record](https://daroczig.github.io/logger/articles/customize_logger.html) -* [Writing Custom Logger Extensions](https://daroczig.github.io/logger/articles/write_custom_extensions.html) -* [Migration Guide from other logging packages](https://daroczig.github.io/logger/articles/migration.html) -* [Logging from R Packages](https://daroczig.github.io/logger/articles/r_packages.html) -* [Simple Benchmarks on Performance](https://daroczig.github.io/logger/articles/performance.html) +* Introduction to logger: `vignette("logger")`. +* Customizing the Format and the Destination of a Log Record: `vignette("customize_logger")` +* Writing Custom Logger Extensions: `vignette("write_custom_extensions")` +* Migration Guide from other logging packages: `vignette("migration")` +* Logging from R Packages: `vignette("r_packages")` +* Simple Benchmarks on Performance: `vignette("performance")` ::: diff --git a/README.md b/README.md index 5d288245..26987faf 100644 --- a/README.md +++ b/README.md @@ -46,11 +46,11 @@ messages in ad-hoc and programmatic ways: library(logger) log_threshold(DEBUG) log_info("Script starting up...") -#> INFO [2024-08-15 11:59:27] Script starting up... +#> INFO [2024-08-23 12:48:18] Script starting up... pkgs <- available.packages() log_info("There are {nrow(pkgs)} R packages hosted on CRAN!") -#> INFO [2024-08-15 11:59:28] There are 21131 R packages hosted on CRAN! +#> INFO [2024-08-23 12:48:19] There are 21137 R packages hosted on CRAN! for (letter in letters) { lpkgs <- sum(grepl(letter, pkgs[, "Package"], ignore.case = TRUE)) @@ -59,23 +59,23 @@ for (letter in letters) { "{lpkgs} R packages including the {shQuote(letter)} letter" ) } -#> DEBUG [2024-08-15 11:59:28] 10193 R packages including the 'a' letter -#> DEBUG [2024-08-15 11:59:28] 7016 R packages including the 'c' letter -#> DEBUG [2024-08-15 11:59:28] 5751 R packages including the 'd' letter -#> DEBUG [2024-08-15 11:59:28] 10907 R packages including the 'e' letter -#> DEBUG [2024-08-15 11:59:28] 8825 R packages including the 'i' letter -#> DEBUG [2024-08-15 11:59:28] 7059 R packages including the 'l' letter -#> DEBUG [2024-08-15 11:59:28] 7045 R packages including the 'm' letter -#> DEBUG [2024-08-15 11:59:28] 6665 R packages including the 'n' letter -#> DEBUG [2024-08-15 11:59:28] 7863 R packages including the 'o' letter -#> DEBUG [2024-08-15 11:59:28] 6581 R packages including the 'p' letter -#> DEBUG [2024-08-15 11:59:28] 11229 R packages including the 'r' letter -#> DEBUG [2024-08-15 11:59:28] 10296 R packages including the 's' letter -#> DEBUG [2024-08-15 11:59:28] 9531 R packages including the 't' letter +#> DEBUG [2024-08-23 12:48:19] 10185 R packages including the 'a' letter +#> DEBUG [2024-08-23 12:48:19] 7016 R packages including the 'c' letter +#> DEBUG [2024-08-23 12:48:19] 5757 R packages including the 'd' letter +#> DEBUG [2024-08-23 12:48:19] 10907 R packages including the 'e' letter +#> DEBUG [2024-08-23 12:48:19] 8832 R packages including the 'i' letter +#> DEBUG [2024-08-23 12:48:19] 7065 R packages including the 'l' letter +#> DEBUG [2024-08-23 12:48:19] 7061 R packages including the 'm' letter +#> DEBUG [2024-08-23 12:48:19] 6673 R packages including the 'n' letter +#> DEBUG [2024-08-23 12:48:19] 7867 R packages including the 'o' letter +#> DEBUG [2024-08-23 12:48:19] 6582 R packages including the 'p' letter +#> DEBUG [2024-08-23 12:48:19] 11229 R packages including the 'r' letter +#> DEBUG [2024-08-23 12:48:19] 10296 R packages including the 's' letter +#> DEBUG [2024-08-23 12:48:19] 9525 R packages including the 't' letter log_warn("There might be many, like {1:2} or more warnings!!!") -#> WARN [2024-08-15 11:59:28] There might be many, like 1 or more warnings!!! -#> WARN [2024-08-15 11:59:28] There might be many, like 2 or more warnings!!! +#> WARN [2024-08-23 12:48:19] There might be many, like 1 or more warnings!!! +#> WARN [2024-08-23 12:48:19] There might be many, like 2 or more warnings!!! ``` You can even use a custom log layout to render the log records with @@ -144,20 +144,14 @@ Check out the main documentation site at or the vignettes on the below topics: -- [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) -- [Customizing the Format and the Destination of a Log - Record](https://daroczig.github.io/logger/articles/customize_logger.html) -- [Writing Custom Logger - Extensions](https://daroczig.github.io/logger/articles/write_custom_extensions.html) -- [Migration Guide from other logging - packages](https://daroczig.github.io/logger/articles/migration.html) -- [Logging from R - Packages](https://daroczig.github.io/logger/articles/r_packages.html) -- [Simple Benchmarks on - Performance](https://daroczig.github.io/logger/articles/performance.html) +- Introduction to logger: `vignette("logger")`. +- Customizing the Format and the Destination of a Log Record: + `vignette("customize_logger")` +- Writing Custom Logger Extensions: + `vignette("write_custom_extensions")` +- Migration Guide from other logging packages: `vignette("migration")` +- Logging from R Packages: `vignette("r_packages")` +- Simple Benchmarks on Performance: `vignette("performance")` diff --git a/_pkgdown.yml b/_pkgdown.yml index 05e5044e..084001bc 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -6,7 +6,7 @@ template: authors: System1: href: https://system1.com - html: + html: System1 toc: depth: 3 @@ -73,3 +73,16 @@ reference: - get_logger_meta_variables - log_namespaces - log_indices + +articles: +- title: For users + navbar: ~ + contents: + - customize_logger + - migration + - performance +- title: For developers + navbar: For developers + contents: + - r_packages + - write_custom_extensions diff --git a/man/logger.Rd b/man/logger.Rd index 1f87ce1b..607a8b8f 100644 --- a/man/logger.Rd +++ b/man/logger.Rd @@ -61,7 +61,5 @@ do.call(logger, logger:::namespaces$global[[1]])(INFO, "{x}^2 = {x^2}") } } \references{ -For more details, see the Anatomy of a Log Request -vignette at -\url{https://daroczig.github.io/logger/articles/anatomy.html}. +For more details, see vignette("customize_logger") } diff --git a/vignettes/anatomy.Rmd b/vignettes/anatomy.Rmd deleted file mode 100644 index 92e0ad76..00000000 --- a/vignettes/anatomy.Rmd +++ /dev/null @@ -1,94 +0,0 @@ ---- -title: "The Anatomy of a Log Request" -output: rmarkdown::html_vignette -vignette: > - %\VignetteIndexEntry{The Anatomy of a Log Request} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} -resource_files: - - logger_structure.svg ---- - -```{r setup, include = FALSE} -knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>" -) -library(logger) -log_appender(appender_stdout) -``` - -```{r loggerStructureImage, echo=FALSE, out.extra='style="width: 100%;" title="The structure of a logger and the flow of a log record request" alt="The structure of a logger and the flow of a log record request"'} -knitr::include_graphics("logger_structure.svg") -``` - -To make a successful log record, `logger` requires the below components: - -- a **log request**, eg - - ```r - log_error('Oops') - ``` - - - including the log level (importance) of the record, which will be later used to decide if the log record is to be delivered or not: `ERROR` in this case - - R objects to be logged: a simple string in this case, although it could be a character vector or any R object(s) that can be converted into a character vector by the `formatter` function - -- the **environment** and meta-information of the log request, eg actual timestamp, hostname of the computer, the name of the user running the R script, the pid of the R process, calling function and the actual call etc. - - - ```{r} -f <- function() get_logger_meta_variables(log_level = INFO) -f() - ``` - -- a **logger definition** to process the log request, including - - - log level `threshold`, eg `INFO`, which defines the minimum log level required for actual logging -- all log requests with lower log level will be thrown away - - ```{r} -log_threshold() -ERROR <= INFO -log_error("Oops") - ``` - - - `formatter` function, which takes R objects and converts those into actual log message(s) to be then passed to the `layout` function for the log record rendering -- such as `paste`, `sprintf`, `glue` or eg the below custom example: - - - ```{r} -formatter <- function(...) paste(..., collapse = " ", sep = " ") -formatter(1:3, c("foo", "bar")) - ``` - - - `layout` function, which takes log message(s) and further information on the log request (such as timestamp, hostname, username, calling function etc) to render the actual log records eg human-readable text, JSON etc - - - ```r - library(jsonlite) - layout <- function(level, msg) toJSON(level = level, timestamp = time, hostname = node, message = msg) - layout(INFO, 'Happy Thursday!') - #> {'level': 'INFO', 'timestamp': '1970-01-01 00:00:00', 'hostname': 'foobar', 'message': 'Happy Thursday!'} - ``` - - - `appender` function, which takes fully-rendered log record(s) and delivers to somewhere, eg `stdout`, a file or a streaming service, eg - - ```{r} -appender <- function(line) cat(line, "\n") -appender("INFO [now] I am a log message") - ``` - -Putting all these together (by explicitly setting the default config in the `global` namespace): - -```{r} -log_threshold(INFO) -log_formatter(formatter_glue) -log_layout(layout_simple) -log_appender(appender_stdout) -log_debug("I am a low level log message that will not be printed with a high log level threshold") -log_warn("I am a higher level log message that is very likely to be printed") -``` - -Note, that all `logger` definitions and requests are tied to a logging namespace, and one log request might trigger multiple `logger` definitions as well (stacking). Find more information on these in the [Customizing the format and destination of log records](https://daroczig.github.io/logger/articles/customize_logger.html) vignette. - -```{r cleanup, include = FALSE} -logger:::namespaces_reset() -``` diff --git a/vignettes/customize_logger.Rmd b/vignettes/customize_logger.Rmd index 9eb7cd98..f798c86c 100644 --- a/vignettes/customize_logger.Rmd +++ b/vignettes/customize_logger.Rmd @@ -27,7 +27,77 @@ library(logger) log_appender(appender_stdout) ``` -In this vignette I suppose that you are already familiar with [The Anatomy of a Log Request](https://daroczig.github.io/logger/articles/anatomy.html) vignette. +## Anatomy of a request + +```{r loggerStructureImage, echo=FALSE, out.extra='style="width: 100%;" title="The structure of a logger and the flow of a log record request" alt="The structure of a logger and the flow of a log record request"'} +knitr::include_graphics("logger_structure.svg") +``` + +To make a successful log record, `logger` requires the below components: + +- a **log request**, eg + + ```r + log_error('Oops') + ``` + + - including the log level (importance) of the record, which will be later used to decide if the log record is to be delivered or not: `ERROR` in this case + - R objects to be logged: a simple string in this case, although it could be a character vector or any R object(s) that can be converted into a character vector by the `formatter` function + +- the **environment** and meta-information of the log request, eg actual timestamp, hostname of the computer, the name of the user running the R script, the pid of the R process, calling function and the actual call etc. + + + ```{r} +f <- function() get_logger_meta_variables(log_level = INFO) +f() + ``` + +- a **logger definition** to process the log request, including + + - log level `threshold`, eg `INFO`, which defines the minimum log level required for actual logging -- all log requests with lower log level will be thrown away + + ```{r} + log_threshold() + ERROR <= INFO + log_error("Oops") + ``` + + - `formatter` function, which takes R objects and converts those into actual log message(s) to be then passed to the `layout` function for the log record rendering -- such as `paste`, `sprintf`, `glue` or eg the below custom example: + + + ```{r} + formatter <- function(...) paste(..., collapse = " ", sep = " ") + formatter(1:3, c("foo", "bar")) + ``` + + - `layout` function, which takes log message(s) and further information on the log request (such as timestamp, hostname, username, calling function etc) to render the actual log records eg human-readable text, JSON etc + + ```r + library(jsonlite) + layout <- function(level, msg) toJSON(level = level, timestamp = time, hostname = node, message = msg) + layout(INFO, 'Happy Thursday!') + #> {'level': 'INFO', 'timestamp': '1970-01-01 00:00:00', 'hostname': 'foobar', 'message': 'Happy Thursday!'} + ``` + + - `appender` function, which takes fully-rendered log record(s) and delivers to somewhere, eg `stdout`, a file or a streaming service, eg + + ```{r} + appender <- function(line) cat(line, "\n") + appender("INFO [now] I am a log message") + ``` + +Putting all these together (by explicitly setting the default config in the `global` namespace): + +```{r} +log_threshold(INFO) +log_formatter(formatter_glue) +log_layout(layout_simple) +log_appender(appender_stdout) +log_debug("I am a low level log message that will not be printed with a high log level threshold") +log_warn("I am a higher level log message that is very likely to be printed") +``` + +Note, that all `logger` definitions and requests are tied to a logging namespace, and one log request might trigger multiple `logger` definitions as well (stacking). Find more information on these in the `vignette("customize_logger")`. ## What gets logged? @@ -114,7 +184,7 @@ log_info("There are {nrow(mtcars)} cars in the mtcars dataset") log_info("2 + 2 = {2+2}") ``` -If you don't like this syntax, or want to save a dependency, you can use other formatter functions as well, such as `?formatter_sprintf` (being the default in eg the [`logging` and `futile.logger` packages](https://daroczig.github.io/logger/articles/migration.html)) or `?formatter_paste`, or [write your own formatter function](https://daroczig.github.io/logger/articles/write_custom_extensions.html) converting R objects into string. +If you don't like this syntax, or want to save a dependency, you can use other formatter functions as well, such as `formatter_sprintf()` (the default in eg the `logging` and `futile.logger` packages, which you can learn more about in `vignette("migration.html")`) or `formatter_paste()`, or you can write your own, as described in `vignette("write_custom_extensions.html")`. ## Log message layouts @@ -222,7 +292,7 @@ mclapply(split(runif(100), 1:10), f, mc.cores = 5) log_layout() ``` -For more details on this, see the [Writing custom logger extensions](https://daroczig.github.io/logger/articles/write_custom_extensions.html) vignette. +For more details on this, see `vignette("write_custom_extensions.html")`. ```{r} ## reset layout @@ -271,7 +341,7 @@ You may find `?appender_tee` also useful, that writes the log messages to both ` log_appender(appender_stdout) ``` -And the are many other appender functions bundled with `logger` as well, eg some writing to Syslog, Telegram, Pushbullet, a database table or an Amazon Kinesis stream -- even doing that asynchronously via `appender_async` -- see [Simple Benchmarks on Performance](https://daroczig.github.io/logger/articles/performance.html) for more details. +And the are many other appender functions bundled with `logger` as well, eg some writing to Syslog, Telegram, Pushbullet, a database table or an Amazon Kinesis stream -- even doing that asynchronously via `appender_async` -- see `vignette("performance")` for more details. ## Stacking loggers diff --git a/vignettes/Intro.Rmd b/vignettes/logger.Rmd similarity index 90% rename from vignettes/Intro.Rmd rename to vignettes/logger.Rmd index d75e05cb..4f988f7d 100644 --- a/vignettes/Intro.Rmd +++ b/vignettes/logger.Rmd @@ -86,7 +86,7 @@ pander::panderOptions("knitr.auto.asis", ppo1) pander::panderOptions("table.style", ppo2) ``` -For more details, check the [function reference in the manual](https://daroczig.github.io/logger/reference/index.html), or start with the [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 more details, check the [function reference in the manual](https://daroczig.github.io/logger/reference/index.html) or `vignette("customize_logger")`. ```{r cleanup, include = FALSE} logger:::namespaces_reset() diff --git a/vignettes/migration.Rmd b/vignettes/migration.Rmd index 325c04c9..e5b9ab36 100644 --- a/vignettes/migration.Rmd +++ b/vignettes/migration.Rmd @@ -42,7 +42,7 @@ 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. +This vignette provides a quick reference guide to switching to logger from the futile.logger, logging, log4r, and loggit packages. I assume that you're already familiar with the basics of logger by reading `vignette("logger")` and `vignette("customize_logger")`. ## futile.logger @@ -168,7 +168,7 @@ 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. +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 `vignette("performance")`. ### Using `logger` as a drop-in-replacement of `futile.logger` @@ -255,7 +255,7 @@ str(levels[order(-as.numeric(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. +Performance-wise, there's no big difference between the two packages, but for more details, check `vignette("performance")`. ### Log record layout @@ -272,7 +272,7 @@ 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. +`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 `vignette("customize_logger")`for more details. ### Log message formatting diff --git a/vignettes/migration.html b/vignettes/migration.html deleted file mode 100644 index de52b543..00000000 --- a/vignettes/migration.html +++ /dev/null @@ -1,914 +0,0 @@ - - - - - - - - - - - - - -Migration Guide - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - -

In this vignette I suppose that you are already familiar with at -least one of the similar -logging R packages 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, The -Anatomy of a Log Request and Customizing -the Format and the Destination of a Log Record 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 -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

-
library(futile.logger)
-#> 
-#> Attaching package: 'futile.logger'
-#> The following objects are masked from 'package:logger':
-#> 
-#>     DEBUG, ERROR, FATAL, INFO, TRACE, WARN
-
-
-

logger

-
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

-
flog.info('hi there')
-#> INFO [2024-08-06 14:28:37] hi there
-flog.warn('watch out')
-#> WARN [2024-08-06 14:28:37] watch out
-
-
-

logger

-
log_info('hi there')
-#> INFO [2024-08-06 14:28:37] hi there
-log_warn('watch out')
-#> WARN [2024-08-06 14:28:37] 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

-
flog.layout(layout.json)
-#> NULL
-flog.info('hi again')
-#> {"level":"INFO","timestamp":"2024-08-06 14:28:37 -0500","message":"hi again","func":"rmarkdown::render"}
-
-
-

logger

-
log_layout(layout_json())
-
-log_info('hi again')
-#> {"time":"2024-08-06 14:28:37","level":"INFO","ns":"global","ans":"global","topenv":"R_GlobalEnv","fn":"eval","node":"Hadleys-MBP.attlocal.net","arch":"arm64","os_name":"Darwin","os_release":"23.5.0","os_version":"Darwin Kernel Version 23.5.0: Wed May  1 20:12:58 PDT 2024; root:xnu-10063.121.3~5/RELEASE_ARM64_T6000","pid":86631,"user":"hadleywickham","msg":"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.

-
-
-

Log message formatting

-

By default, futile.logger uses an sprintf -formatter, while logger passes the objects to be logged to -glue:

-
-

futile.logger

-
flog.info('hi')
-#> INFO [2024-08-06 14:28:37] hi
-flog.info('hi %s', 84/2)
-#> INFO [2024-08-06 14:28:37] hi 42
-flog.info(paste('hi', 84/2))
-#> INFO [2024-08-06 14:28:37] hi 42
-flog.info(glue::glue('hi {84/2}'))
-#> INFO [2024-08-06 14:28:37] hi 42
-
-
-

logger

-
log_info('hi')
-#> INFO [2024-08-06 14:28:37] hi
-log_info('hi {84/2}')
-#> INFO [2024-08-06 14:28:37] hi 42
-log_formatter(formatter_sprintf)
-log_info('hi %s', 84/2)
-#> INFO [2024-08-06 14:28:37] hi 42
-log_formatter(formatter_paste)
-log_info('hi', 84/2)
-#> INFO [2024-08-06 14:28:37] hi 42
-
-

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

-
t <- tempfile()
-flog.appender(appender.file(t))
-#> NULL
-flog.appender(appender.tee(t))
-#> NULL
-
-
-

logger

-
t <- tempfile()
-log_appender(appender_file(t))
-log_appender(appender_tee(t))
-
-
-
-

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 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:

-
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 -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

-
library(logging)
-basicConfig()
-
-
-

logger

-
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

-
loginfo('hi there')
-#> 2024-08-06 14:28:37.693914 INFO::hi there
-logwarn('watch out')
-#> 2024-08-06 14:28:37.698499 WARNING::watch out
-
-
-

logger

-
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

-
str(as.list(loglevels))
-#> List of 11
-#>  $ NOTSET  : num 0
-#>  $ FINEST  : num 1
-#>  $ FINER   : num 4
-#>  $ FINE    : num 7
-#>  $ DEBUG   : num 10
-#>  $ INFO    : num 20
-#>  $ WARNING : num 30
-#>  $ WARN    : num 30
-#>  $ ERROR   : num 40
-#>  $ CRITICAL: num 50
-#>  $ FATAL   : num 50
-
-
-

logger

-
levels <- mget(ls(
-    envir = environment(logger), pattern = '^[A-Z]'),
-    envir = environment(logger))
-str(levels[order(-as.numeric(levels))], give.attr = FALSE)
-#> List of 8
-#>  $ TRACE  : 'loglevel' int 600
-#>  $ DEBUG  : 'loglevel' int 500
-#>  $ INFO   : 'loglevel' int 400
-#>  $ SUCCESS: 'loglevel' int 350
-#>  $ WARN   : 'loglevel' int 300
-#>  $ ERROR  : 'loglevel' int 200
-#>  $ FATAL  : 'loglevel' int 100
-#>  $ OFF    : 'loglevel' int 0
-
-
-
-

Performance

-

Performance-wise, there’s no big difference between the two packages, -but for more details, check the Simple -Benchmarks on Performance vignette.

-
-
-

Log record layout

-

Getting and setting the layout of the log record should happen -up-front in both packages:

-
-

logging

-
getLogger()[['handlers']]$basic.stdout$formatter
-#> function (record) 
-#> {
-#>     msg <- trimws(record$msg)
-#>     text <- paste(record$timestamp, paste(record$levelname, record$logger, 
-#>         msg, sep = ":"))
-#>     return(text)
-#> }
-#> <bytecode: 0x1375de6b8>
-#> <environment: namespace:logging>
-
-
-

logger

-
log_layout()
-#> layout_simple
-
-

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 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

-
loginfo('hi')
-#> 2024-08-06 14:28:37.726038 INFO::hi
-loginfo('hi %s', 84/2)
-#> 2024-08-06 14:28:37.726619 INFO::hi 42
-loginfo(paste('hi', 84/2))
-#> 2024-08-06 14:28:37.727083 INFO::hi 42
-loginfo(glue::glue('hi {84/2}'))
-#> 2024-08-06 14:28:37.727625 INFO::hi 42
-
-
-

logger

-
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:

-
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)
-
-
-

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

-
?addHandler
-?writeToConsole
-?writeToFile
-
-
-

logger

-
?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 and ) are supported:

-
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 -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

-
library(log4r)
-#> 
-#> Attaching package: 'log4r'
-#> The following object is masked from 'package:logging':
-#> 
-#>     levellog
-#> The following objects are masked from 'package:logger':
-#> 
-#>     as.loglevel, logger
-#> The following object is masked from 'package:base':
-#> 
-#>     debug
-logger <- create.logger(logfile = stdout(), level = "INFO")
-
-
-

logger

-
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

-
info(logger, 'hi there')
-#> INFO  [2024-08-06 14:28:37] hi there
-warn(logger, 'watch out')
-#> WARN  [2024-08-06 14:28:37] watch out
-
-
-

logger

-
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 -namespaces.

-
-
-
-

loggit

-

Sorry, no direct replacement for loggit -– capturing message, warning and -stop function messages, but it’s on the roadmap to -provide helper functions to be used as message hooks feed -logger.

-
- - - - -
- - - - - - - - - - - - - - - diff --git a/vignettes/r_packages.Rmd b/vignettes/r_packages.Rmd index bfeca803..0b63ce5d 100644 --- a/vignettes/r_packages.Rmd +++ b/vignettes/r_packages.Rmd @@ -24,7 +24,7 @@ knitr::opts_chunk$set( ) ``` -In this vignette I suppose that you are already familiar with [Customizing the format and destination of log records](https://daroczig.github.io/logger/articles/customize_logger.html) vignette, especially with the [Log namespaces](https://daroczig.github.io/logger/articles/customize_logger.html#log-namespaces) section. +In this vignette I suppose that you are already familiar with `vignette("customize_logger")`, especially the Log namespaces section. So that your R package's users can suppress (or render with custom layout) the log messages triggered by your R package, it's wise to record all those log messages in a custom namespace. By default, if you are calling the `?log_level` function family from an R package after importing from the `logger` package, then `logger` will try to auto-guess the calling R package name and use that as the default namespace, see eg: diff --git a/vignettes/write_custom_extensions.Rmd b/vignettes/write_custom_extensions.Rmd index 922c85fc..82e1b102 100644 --- a/vignettes/write_custom_extensions.Rmd +++ b/vignettes/write_custom_extensions.Rmd @@ -13,7 +13,7 @@ knitr::opts_chunk$set( ) ``` -In this vignette I suppose that you are already familiar with [Customizing the format and destination of log records](https://daroczig.github.io/logger/articles/customize_logger.html) vignette. +In this vignette I suppose that you are already familiar with `vignette("customize_logger")`. ## Custom log message formatter functions @@ -43,7 +43,7 @@ layout_simple <- function(level, msg, ...) { Or much more complex, eg looking up the hostname of the machine, public IP address etc and logging all these automatically with the message of the log request. -Your easiest option to set up a custom layout is calling `?layout_glue_generator` that comes with a nice API being able to access a bunch of meta-information on the log request via `?get_logger_meta_variables`. For such example, see the [Customizing the format and destination of log records](https://daroczig.github.io/logger/articles/customize_logger.html) vignette. +Your easiest option to set up a custom layout is calling `?layout_glue_generator` that comes with a nice API being able to access a bunch of meta-information on the log request via `?get_logger_meta_variables`. For such example, see the `vignette("customize_logger")`. If you are writing such a function or a generator function returning a log message formatter function, please keep the actual call resulting in the formatter function (eg `match.call()` in the generator function or the quoted function call) recorded in the `generator` attribute of the function so that `?log_layout` can pretty-print that instead of the unnamed function body. See the [`layouts.R`](https://github.com/daroczig/logger/blob/master/R/layouts.R) file for more examples.