diff --git a/.gitignore b/.gitignore index 0906bc7e..35dc13a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ docs/ README.html + +/.quarto/ diff --git a/DESCRIPTION b/DESCRIPTION index e112b4aa..f489bdf8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -15,6 +15,7 @@ RoxygenNote: 7.3.2 License: AGPL-3 Imports: utils +Depends: R (>= 4.0.0) Suggests: glue, pander, diff --git a/R/color.R b/R/color.R index 78994bfc..6ef91e99 100644 --- a/R/color.R +++ b/R/color.R @@ -1,9 +1,12 @@ -#' Colorize string by the related log level +#' Color string by the related log level #' -#' Adding color to a string to be used in terminal output. Supports ANSI standard colors 8 or 256. -#' @param msg string +#' Color log messages according to their severity with either a rainbow +#' or grayscale color scheme. The greyscale theme assumes a dark background on +#' the terminal. +#' +#' @param msg String to color. #' @param level see [log_levels()] -#' @return string with ANSI escape code +#' @return A string with ANSI escape codes. #' @export #' @examplesIf requireNamespace("crayon") #' cat(colorize_by_log_level('foobar', FATAL), '\n') @@ -13,6 +16,14 @@ #' cat(colorize_by_log_level('foobar', INFO), '\n') #' cat(colorize_by_log_level('foobar', DEBUG), '\n') #' cat(colorize_by_log_level('foobar', TRACE), '\n') +#' +#' cat(grayscale_by_log_level('foobar', FATAL), '\n') +#' cat(grayscale_by_log_level('foobar', ERROR), '\n') +#' cat(grayscale_by_log_level('foobar', WARN), '\n') +#' cat(grayscale_by_log_level('foobar', SUCCESS), '\n') +#' cat(grayscale_by_log_level('foobar', INFO), '\n') +#' cat(grayscale_by_log_level('foobar', DEBUG), '\n') +#' cat(grayscale_by_log_level('foobar', TRACE), '\n') colorize_by_log_level <- function(msg, level) { fail_on_missing_package('crayon') @@ -33,22 +44,8 @@ colorize_by_log_level <- function(msg, level) { } - -#' Render a string with light/dark gray based on the related log level -#' -#' Adding color to a string to be used in terminal output. Supports ANSI standard colors 8 or 256. -#' @param msg string -#' @param level see [log_levels()] -#' @return string with ANSI escape code #' @export -#' @examplesIf requireNamespace("crayon") -#' cat(grayscale_by_log_level('foobar', FATAL), '\n') -#' cat(grayscale_by_log_level('foobar', ERROR), '\n') -#' cat(grayscale_by_log_level('foobar', WARN), '\n') -#' cat(grayscale_by_log_level('foobar', SUCCESS), '\n') -#' cat(grayscale_by_log_level('foobar', INFO), '\n') -#' cat(grayscale_by_log_level('foobar', DEBUG), '\n') -#' cat(grayscale_by_log_level('foobar', TRACE), '\n') +#' @rdname colorize_by_log_level grayscale_by_log_level <- function(msg, level) { fail_on_missing_package('crayon') diff --git a/R/hooks.R b/R/hooks.R index d41dba9d..83a7b30a 100644 --- a/R/hooks.R +++ b/R/hooks.R @@ -1,12 +1,3 @@ -#' Warn to update R to 4+ -#' @keywords internal -warn_if_globalCallingHandlers_is_not_available <- function() { - log_warn( - 'Using legacy version of global message/warning/error hook, ', - 'please update your R installation to at least 4.0.0 ', - 'to make use of the much more elegant globalCallingHandlers approach.') -} - #' Injects a logger call to standard messages #' @@ -17,23 +8,14 @@ warn_if_globalCallingHandlers_is_not_available <- function() { #' message('hi there') #' } log_messages <- function() { - if (R.Version()$major >= 4) { - if (any(sapply(globalCallingHandlers()[names(globalCallingHandlers()) == 'message'], - attr, which = 'implements') == 'log_messages')) { - warning('Ignoring this call to log_messages as it was registered previously.') - } else { - globalCallingHandlers( - message = structure(function(m) { - logger::log_level(logger::INFO, m$message, .topcall = m$call) - }, implements = 'log_messages')) - } + if (any(sapply(globalCallingHandlers()[names(globalCallingHandlers()) == 'message'], + attr, which = 'implements') == 'log_messages')) { + warning('Ignoring this call to log_messages as it was registered previously.') } else { - warn_if_globalCallingHandlers_is_not_available() - invisible(suppressMessages(trace( - what = 'message', - exit = substitute(logger::log_info(logger::skip_formatter(cond$message))), - print = FALSE, - where = baseenv()))) + globalCallingHandlers( + message = structure(function(m) { + logger::log_level(logger::INFO, m$message, .topcall = m$call) + }, implements = 'log_messages')) } } @@ -48,26 +30,17 @@ log_messages <- function() { #' for (i in 1:5) { Sys.sleep(runif(1)); warning(i) } #' } log_warnings <- function(muffle = getOption('logger_muffle_warnings', FALSE)) { - if (R.Version()$major >= 4) { - if (any(sapply(globalCallingHandlers()[names(globalCallingHandlers()) == 'warning'], - attr, which = 'implements') == 'log_warnings')) { - warning('Ignoring this call to log_warnings as it was registered previously.') - } else { - globalCallingHandlers( - warning = structure(function(m) { - logger::log_level(logger::WARN, m$message, .topcall = m$call) - if (isTRUE(muffle)) { - invokeRestart('muffleWarning') - } - }, implements = 'log_warnings')) - } + if (any(sapply(globalCallingHandlers()[names(globalCallingHandlers()) == 'warning'], + attr, which = 'implements') == 'log_warnings')) { + warning('Ignoring this call to log_warnings as it was registered previously.') } else { - warn_if_globalCallingHandlers_is_not_available() - invisible(suppressMessages(trace( - what = 'warning', - tracer = substitute(logger::log_warn(logger::skip_formatter(paste(list(...), collapse = '')))), - print = FALSE, - where = baseenv()))) + globalCallingHandlers( + warning = structure(function(m) { + logger::log_level(logger::WARN, m$message, .topcall = m$call) + if (isTRUE(muffle)) { + invokeRestart('muffleWarning') + } + }, implements = 'log_warnings')) } } @@ -82,26 +55,17 @@ log_warnings <- function(muffle = getOption('logger_muffle_warnings', FALSE)) { #' stop('foobar') #' } log_errors <- function(muffle = getOption('logger_muffle_errors', FALSE)) { - if (R.Version()$major >= 4) { - if (any(sapply(globalCallingHandlers()[names(globalCallingHandlers()) == 'error'], - attr, which = 'implements') == 'log_errors')) { - warning('Ignoring this call to log_errors as it was registered previously.') - } else { - globalCallingHandlers( - error = structure(function(m) { - logger::log_level(logger::ERROR, m$message, .topcall = m$call) - if (isTRUE(muffle)) { - invokeRestart('abort') - } - }, implements = 'log_errors')) - } + if (any(sapply(globalCallingHandlers()[names(globalCallingHandlers()) == 'error'], + attr, which = 'implements') == 'log_errors')) { + warning('Ignoring this call to log_errors as it was registered previously.') } else { - warn_if_globalCallingHandlers_is_not_available() - invisible(suppressMessages(trace( - what = 'stop', - tracer = substitute(logger::log_error(logger::skip_formatter(paste(list(...), collapse = '')))), - print = FALSE, - where = baseenv()))) + globalCallingHandlers( + error = structure(function(m) { + logger::log_level(logger::ERROR, m$message, .topcall = m$call) + if (isTRUE(muffle)) { + invokeRestart('abort') + } + }, implements = 'log_errors')) } } diff --git a/R/layouts.R b/R/layouts.R index 74149540..7c5fa439 100644 --- a/R/layouts.R +++ b/R/layouts.R @@ -168,10 +168,15 @@ layout_glue <- layout_glue_generator() #' Format a log message with `glue` and ANSI escape codes to add colors +#' +#' Colour log levels based on their severity. Log levels are coloured +#' with [colorize_by_log_level()] and the messages are coloured with +#' [grayscale_by_log_level()]. +#' #' @inheritParams layout_simple #' @return character vector #' @export -#' @examples \dontrun{ +#' @examplesIf requireNamespace("crayon") #' log_layout(layout_glue_colors) #' log_threshold(TRACE) #' log_info('Starting the script...') @@ -182,7 +187,6 @@ layout_glue <- layout_glue_generator() #' log_debug('Getting an error is usually bad') #' log_error('This is another problem') #' log_fatal('The last problem.') -#' } #' @seealso This is a [log_layout()], for alternatives, see [layout_blank()], [layout_simple()], [layout_glue()], [layout_json()], [layout_json_parser()], or generator functions such as [layout_glue_generator()] #' @note This functionality depends on the \pkg{crayon} package. layout_glue_colors <- layout_glue_generator( diff --git a/R/utils.R b/R/utils.R index 61972994..62dc9292 100644 --- a/R/utils.R +++ b/R/utils.R @@ -92,3 +92,7 @@ catch_base_log <- function( log_appender(orginal_appender, namespace = namespace) res } + +in_pkgdown <- function() { + identical(Sys.getenv("IN_PKGDOWN"), "true") +} diff --git a/R/zzz.R b/R/zzz.R index f6b58bd1..7d108136 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -10,7 +10,7 @@ namespaces <- new.env() threshold = as.loglevel(Sys.getenv('LOGGER_LOG_LEVEL', unset = 'INFO')), layout = layout_simple, formatter = formatter_sprintf, - appender = appender_console)) + appender = if (in_pkgdown()) appender_stdout else appender_console)) if (requireNamespace('glue', quietly = TRUE)) { log_formatter(formatter_glue, namespace = 'global', index = 1) diff --git a/README.Rmd b/README.Rmd index a9af716a..99880ea7 100644 --- a/README.Rmd +++ b/README.Rmd @@ -67,7 +67,7 @@ for (letter in letters) { log_warn('There might be many, like {1:2} or more warnings!!!') ``` -You can even use a custom log layout to render the log records with colors, as you can see in `demo(colors, package = 'logger', echo = FALSE)`: +You can even use a custom log layout to render the log records with colors, as you can see in `layout_glue_colors()`: colored log output @@ -100,10 +100,10 @@ Why use logger? I decided to write the `n+1`th extensible `log4j` logger that fi Welcome to the [Bazaar](https://en.wikipedia.org/wiki/The_Cathedral_and_the_Bazaar)! If you already use any of the above packages for logging, you might find `vignette("migration")` useful. -::: .pkgdown-hide - ## Interested in more details? +::: .pkgdown-hide + 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) @@ -115,3 +115,7 @@ Check out the main documentation site at or * [Simple Benchmarks on Performance](https://daroczig.github.io/logger/articles/performance.html) ::: + +If you prefer visual content, you can watch the video recording of the "Getting things logged" talk at RStudio::conf(2020): + +[![Gergely Daroczi presenting "Getting things logged" on using the `logger` R package at the RStudio conference in 2020](https://img.youtube.com/vi/_rUuBbml9dU/0.jpg)](https://www.youtube.com/watch?v=_rUuBbml9dU) diff --git a/README.md b/README.md index fa22a5d6..633663da 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-07-31 08:26:30] Script starting up... +#> INFO [2024-08-05 16:05:22] Script starting up... pkgs <- available.packages() log_info('There are {nrow(pkgs)} R packages hosted on CRAN!') -#> INFO [2024-07-31 08:26:30] There are 21122 R packages hosted on CRAN! +#> INFO [2024-08-05 16:05:23] There are 21132 R packages hosted on CRAN! for (letter in letters) { lpkgs <- sum(grepl(letter, pkgs[, 'Package'], ignore.case = TRUE)) @@ -59,28 +59,27 @@ for (letter in letters) { '{lpkgs} R packages including the {shQuote(letter)} letter' ) } -#> DEBUG [2024-07-31 08:26:30] 10188 R packages including the 'a' letter -#> DEBUG [2024-07-31 08:26:30] 7013 R packages including the 'c' letter -#> DEBUG [2024-07-31 08:26:30] 5750 R packages including the 'd' letter -#> DEBUG [2024-07-31 08:26:30] 10902 R packages including the 'e' letter -#> DEBUG [2024-07-31 08:26:30] 8821 R packages including the 'i' letter -#> DEBUG [2024-07-31 08:26:30] 7055 R packages including the 'l' letter -#> DEBUG [2024-07-31 08:26:30] 7039 R packages including the 'm' letter -#> DEBUG [2024-07-31 08:26:30] 6661 R packages including the 'n' letter -#> DEBUG [2024-07-31 08:26:30] 7859 R packages including the 'o' letter -#> DEBUG [2024-07-31 08:26:30] 6579 R packages including the 'p' letter -#> DEBUG [2024-07-31 08:26:31] 11224 R packages including the 'r' letter -#> DEBUG [2024-07-31 08:26:31] 10292 R packages including the 's' letter -#> DEBUG [2024-07-31 08:26:31] 9526 R packages including the 't' letter +#> DEBUG [2024-08-05 16:05:23] 10194 R packages including the 'a' letter +#> DEBUG [2024-08-05 16:05:23] 7016 R packages including the 'c' letter +#> DEBUG [2024-08-05 16:05:23] 5751 R packages including the 'd' letter +#> DEBUG [2024-08-05 16:05:23] 10908 R packages including the 'e' letter +#> DEBUG [2024-08-05 16:05:23] 8825 R packages including the 'i' letter +#> DEBUG [2024-08-05 16:05:23] 7060 R packages including the 'l' letter +#> DEBUG [2024-08-05 16:05:23] 7045 R packages including the 'm' letter +#> DEBUG [2024-08-05 16:05:23] 6665 R packages including the 'n' letter +#> DEBUG [2024-08-05 16:05:23] 7863 R packages including the 'o' letter +#> DEBUG [2024-08-05 16:05:23] 6582 R packages including the 'p' letter +#> DEBUG [2024-08-05 16:05:23] 11230 R packages including the 'r' letter +#> DEBUG [2024-08-05 16:05:23] 10296 R packages including the 's' letter +#> DEBUG [2024-08-05 16:05:23] 9531 R packages including the 't' letter log_warn('There might be many, like {1:2} or more warnings!!!') -#> WARN [2024-07-31 08:26:31] There might be many, like 1 or more warnings!!! -#> WARN [2024-07-31 08:26:31] There might be many, like 2 or more warnings!!! +#> WARN [2024-08-05 16:05:23] There might be many, like 1 or more warnings!!! +#> WARN [2024-08-05 16:05:23] There might be many, like 2 or more warnings!!! ``` You can even use a custom log layout to render the log records with -colors, as you can see in -`demo(colors, package = 'logger', echo = FALSE)`: +colors, as you can see in `layout_glue_colors()`: colored log output @@ -137,10 +136,10 @@ Welcome to the you already use any of the above packages for logging, you might find `vignette("migration")` useful. -
- ## Interested in more details? +
+ Check out the main documentation site at or the vignettes on the below topics: @@ -161,3 +160,7 @@ topics: Performance](https://daroczig.github.io/logger/articles/performance.html)
+ +If you prefer visual content, you can watch the video recording of the "Getting things logged" talk at RStudio::conf(2020): + +[![Gergely Daroczi presenting "Getting things logged" on using the `logger` R package at the RStudio conference in 2020](https://img.youtube.com/vi/_rUuBbml9dU/0.jpg)](https://www.youtube.com/watch?v=_rUuBbml9dU) diff --git a/_pkgdown.yml b/_pkgdown.yml index df07a0aa..f6ffac89 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -61,7 +61,6 @@ reference: - title: Other helpers contents: - colorize_by_log_level - - grayscale_by_log_level - logger - delete_logger_index - "%except%" diff --git a/demo/00Index b/demo/00Index deleted file mode 100644 index d522594d..00000000 --- a/demo/00Index +++ /dev/null @@ -1 +0,0 @@ -colors Using a colored layout diff --git a/demo/colors.R b/demo/colors.R deleted file mode 100644 index c8a3d9b4..00000000 --- a/demo/colors.R +++ /dev/null @@ -1,11 +0,0 @@ -suppressWarnings(suppressMessages(library(logger))) -log_layout(layout_glue_colors) -log_threshold(TRACE) -log_info('Starting the script...') -log_debug('This is the second line') -log_trace('That is being placed right after the first one.') -log_warn('Some errors might come!') -log_error('This is a problem') -log_debug('Getting an error is usually bad') -log_error('This is another problem') -log_fatal('The last problem.') \ No newline at end of file diff --git a/man/colorize_by_log_level.Rd b/man/colorize_by_log_level.Rd index dcbe6652..727c9dff 100644 --- a/man/colorize_by_log_level.Rd +++ b/man/colorize_by_log_level.Rd @@ -2,20 +2,25 @@ % Please edit documentation in R/color.R \name{colorize_by_log_level} \alias{colorize_by_log_level} -\title{Colorize string by the related log level} +\alias{grayscale_by_log_level} +\title{Color string by the related log level} \usage{ colorize_by_log_level(msg, level) + +grayscale_by_log_level(msg, level) } \arguments{ -\item{msg}{string} +\item{msg}{String to color.} \item{level}{see \code{\link[=log_levels]{log_levels()}}} } \value{ -string with ANSI escape code +A string with ANSI escape codes. } \description{ -Adding color to a string to be used in terminal output. Supports ANSI standard colors 8 or 256. +Color log messages according to their severity with either a rainbow +or grayscale color scheme. The greyscale theme assumes a dark background on +the terminal. } \examples{ \dontshow{if (requireNamespace("crayon")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} @@ -26,5 +31,13 @@ cat(colorize_by_log_level('foobar', SUCCESS), '\n') cat(colorize_by_log_level('foobar', INFO), '\n') cat(colorize_by_log_level('foobar', DEBUG), '\n') cat(colorize_by_log_level('foobar', TRACE), '\n') + +cat(grayscale_by_log_level('foobar', FATAL), '\n') +cat(grayscale_by_log_level('foobar', ERROR), '\n') +cat(grayscale_by_log_level('foobar', WARN), '\n') +cat(grayscale_by_log_level('foobar', SUCCESS), '\n') +cat(grayscale_by_log_level('foobar', INFO), '\n') +cat(grayscale_by_log_level('foobar', DEBUG), '\n') +cat(grayscale_by_log_level('foobar', TRACE), '\n') \dontshow{\}) # examplesIf} } diff --git a/man/grayscale_by_log_level.Rd b/man/grayscale_by_log_level.Rd deleted file mode 100644 index 22d777e5..00000000 --- a/man/grayscale_by_log_level.Rd +++ /dev/null @@ -1,30 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/color.R -\name{grayscale_by_log_level} -\alias{grayscale_by_log_level} -\title{Render a string with light/dark gray based on the related log level} -\usage{ -grayscale_by_log_level(msg, level) -} -\arguments{ -\item{msg}{string} - -\item{level}{see \code{\link[=log_levels]{log_levels()}}} -} -\value{ -string with ANSI escape code -} -\description{ -Adding color to a string to be used in terminal output. Supports ANSI standard colors 8 or 256. -} -\examples{ -\dontshow{if (requireNamespace("crayon")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} -cat(grayscale_by_log_level('foobar', FATAL), '\n') -cat(grayscale_by_log_level('foobar', ERROR), '\n') -cat(grayscale_by_log_level('foobar', WARN), '\n') -cat(grayscale_by_log_level('foobar', SUCCESS), '\n') -cat(grayscale_by_log_level('foobar', INFO), '\n') -cat(grayscale_by_log_level('foobar', DEBUG), '\n') -cat(grayscale_by_log_level('foobar', TRACE), '\n') -\dontshow{\}) # examplesIf} -} diff --git a/man/layout_glue_colors.Rd b/man/layout_glue_colors.Rd index ca595d8d..03307afc 100644 --- a/man/layout_glue_colors.Rd +++ b/man/layout_glue_colors.Rd @@ -30,13 +30,15 @@ layout_glue_colors( character vector } \description{ -Format a log message with \code{glue} and ANSI escape codes to add colors +Colour log levels based on their severity. Log levels are coloured +with \code{\link[=colorize_by_log_level]{colorize_by_log_level()}} and the messages are coloured with +\code{\link[=grayscale_by_log_level]{grayscale_by_log_level()}}. } \note{ This functionality depends on the \pkg{crayon} package. } \examples{ -\dontrun{ +\dontshow{if (requireNamespace("crayon")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} log_layout(layout_glue_colors) log_threshold(TRACE) log_info('Starting the script...') @@ -47,7 +49,7 @@ log_error('This is a problem') log_debug('Getting an error is usually bad') log_error('This is another problem') log_fatal('The last problem.') -} +\dontshow{\}) # examplesIf} } \seealso{ This is a \code{\link[=log_layout]{log_layout()}}, for alternatives, see \code{\link[=layout_blank]{layout_blank()}}, \code{\link[=layout_simple]{layout_simple()}}, \code{\link[=layout_glue]{layout_glue()}}, \code{\link[=layout_json]{layout_json()}}, \code{\link[=layout_json_parser]{layout_json_parser()}}, or generator functions such as \code{\link[=layout_glue_generator]{layout_glue_generator()}} diff --git a/man/warn_if_globalCallingHandlers_is_not_available.Rd b/man/warn_if_globalCallingHandlers_is_not_available.Rd deleted file mode 100644 index 97ef5164..00000000 --- a/man/warn_if_globalCallingHandlers_is_not_available.Rd +++ /dev/null @@ -1,12 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/hooks.R -\name{warn_if_globalCallingHandlers_is_not_available} -\alias{warn_if_globalCallingHandlers_is_not_available} -\title{Warn to update R to 4+} -\usage{ -warn_if_globalCallingHandlers_is_not_available() -} -\description{ -Warn to update R to 4+ -} -\keyword{internal} diff --git a/tests/testthat/helper.R b/tests/testthat/helper.R new file mode 100644 index 00000000..0e600211 --- /dev/null +++ b/tests/testthat/helper.R @@ -0,0 +1,22 @@ + +local_test_logger <- function(threshold = INFO, + formatter = formatter_glue, + layout = layout_simple, + appender = appender_stdout, + namespace = "global", + frame = parent.frame()) { + + old <- namespaces[[namespace]] + + namespaces[[namespace]] <- list( + default = list( + threshold = as.loglevel(threshold), + layout = layout, + formatter = formatter, + appender = appender + ) + ) + + withr::defer(namespaces[[namespace]] <- old, frame) + invisible() +} diff --git a/tests/testthat/test-CRANSKIP-appenders.R b/tests/testthat/test-CRANSKIP-appenders.R index 814b4548..e37741f7 100644 --- a/tests/testthat/test-CRANSKIP-appenders.R +++ b/tests/testthat/test-CRANSKIP-appenders.R @@ -1,22 +1,12 @@ -## save current settings so that we can reset later -threshold <- log_threshold() -layout <- log_layout() -appender <- log_appender() - test_that('async logging', { - t <- tempfile() - my_appender <- appender_async(appender_file(file = t)) - log_appender(my_appender) - log_layout(layout_blank) + t <- withr::local_tempfile() + local_test_logger( + layout = layout_blank, + appender = appender_async(appender_file(file = t)) + ) + for (i in 1:5) log_info(i) Sys.sleep(0.25) expect_equal(readLines(t)[1], '1') expect_equal(length(readLines(t)), 5) - unlink(t) - rm(t) }) - -## reset settings -log_threshold(threshold) -log_layout(layout) -log_appender(appender) diff --git a/tests/testthat/test-CRANSKIP-logger-namespaces.R b/tests/testthat/test-CRANSKIP-logger-namespaces.R index 876a9863..99a18e0f 100644 --- a/tests/testthat/test-CRANSKIP-logger-namespaces.R +++ b/tests/testthat/test-CRANSKIP-logger-namespaces.R @@ -4,8 +4,7 @@ appender <- log_appender() test_that('called from package', { devtools::load_all(system.file('demo-packages/logger-tester-package', package = 'logger')) - log_layout(layout_simple) - log_appender(appender_stdout) + local_test_logger(layout = layout_simple) expect_output(logger_tester_function(INFO, 'x = '), 'INFO') expect_output(logger_info_tester_function('everything = '), 'INFO') }) diff --git a/tests/testthat/test-appender.R b/tests/testthat/test-appender.R index 6afd1128..6037c311 100644 --- a/tests/testthat/test-appender.R +++ b/tests/testthat/test-appender.R @@ -1,56 +1,57 @@ -## save current settings so that we can reset later -threshold <- log_threshold() -layout <- log_layout() -appender <- log_appender() - test_that('append to file', { - log_layout(layout_glue_generator('{level} {msg}')) - log_threshold(TRACE) - t <- tempfile() - log_appender(appender_file(t)) + t <- withr::local_tempfile() + local_test_logger( + appender = appender_file(t), + layout = layout_glue_generator('{level} {msg}'), + threshold = TRACE + ) log_info('foobar') log_info('{1:2}') expect_equal(length(readLines(t)), 3) expect_equal(readLines(t)[1], 'INFO foobar') expect_equal(readLines(t)[3], 'INFO 2') - unlink(t) - rm(t) }) test_that('overwrite file', { - log_layout(layout_glue_generator('{level} {msg}')) - log_threshold(TRACE) - t <- tempfile() - log_appender(appender_file(t, append = FALSE)) + t <- withr::local_tempfile() + local_test_logger( + appender = appender_file(t, append = FALSE), + layout = layout_glue_generator('{level} {msg}'), + threshold = TRACE + ) + log_info('foobar') log_info('{1:2}') expect_equal(length(readLines(t)), 2) expect_equal(readLines(t), c('INFO 1', 'INFO 2')) + log_info('42') expect_equal(length(readLines(t)), 1) expect_equal(readLines(t), 'INFO 42') - unlink(t) - rm(t) }) test_that('append to file + print to console', { - t <- tempfile() - log_appender(appender_tee(t)) + t <- withr::local_tempfile() + local_test_logger( + appender = appender_tee(t), + layout = layout_glue_generator('{level} {msg}'), + ) + expect_equal(capture.output(log_info('foobar'), type = 'message'), 'INFO foobar') - devnull <- capture.output(log_info('{1:2}'), type = 'message') + capture.output(log_info('{1:2}'), type = 'message') expect_equal(length(readLines(t)), 3) expect_equal(readLines(t)[1], 'INFO foobar') - unlink(t) - rm(t) }) test_that('logrotate', { - log_layout(layout_glue_generator('{msg}')) - log_threshold(TRACE) - t <- tempfile() - dir.create(t) + t <- withr::local_tempdir() f <- file.path(t, 'log') - log_appender(appender_file(f, max_lines = 2, max_files = 5L)) + local_test_logger( + appender = appender_file(f, max_lines = 2, max_files = 5L), + layout = layout_glue_generator('{msg}'), + threshold = TRACE + ) + for (i in 1:24) log_info(i) expect_equal(length(readLines(f)), 2) expect_equal(length(list.files(t)), 5) @@ -58,11 +59,4 @@ test_that('logrotate', { log_info('42') expect_equal(length(readLines(f)), 1) expect_equal(readLines(f), '42') - unlink(t) - rm(t) }) - -## reset settings -log_threshold(threshold) -log_layout(layout) -log_appender(appender) diff --git a/tests/testthat/test-eval.R b/tests/testthat/test-eval.R index 49440e7c..6f3e0461 100644 --- a/tests/testthat/test-eval.R +++ b/tests/testthat/test-eval.R @@ -1,33 +1,25 @@ -## save current settings so that we can reset later -layout <- log_layout() -threshold <- log_threshold() -appender <- log_appender() - -log_layout(layout_glue_generator('{level} {msg}')) -log_appender(appender_stdout) - test_that('single line', { + local_test_logger(layout = layout_glue_generator('{level} {msg}')) + expect_output(log_eval(4, INFO), sprintf("INFO %s => %s", shQuote(4), shQuote(4))) }) test_that('multi line', { + local_test_logger(layout = layout_glue_generator('{level} {msg}')) + expect_output(log_eval(4, INFO, multiline = TRUE), "Running expression") expect_output(log_eval(4, INFO, multiline = TRUE), "Results:") expect_output(log_eval(4, INFO, multiline = TRUE), "INFO 4") }) test_that('invisible return', { + local_test_logger(layout = layout_glue_generator('{level} {msg}')) expect_output(log_eval(require(logger), INFO), sprintf("INFO %s => %s", shQuote('require\\(logger\\)'), shQuote(TRUE))) }) -log_threshold(TRACE) test_that('lower log level', { + local_test_logger(TRACE, layout = layout_glue_generator('{level} {msg}')) expect_output(log_eval(4), sprintf("TRACE %s => %s", shQuote(4), shQuote(4))) }) - -## reset settings -log_threshold(threshold) -log_layout(layout) -log_appender(appender) diff --git a/tests/testthat/test-formatters.R b/tests/testthat/test-formatters.R index 11999314..cc95126a 100644 --- a/tests/testthat/test-formatters.R +++ b/tests/testthat/test-formatters.R @@ -1,7 +1,3 @@ -## save current settings so that we can reset later -formatter <- log_formatter() -appender <- log_appender() - everything <- 42 g <- function() { log_info("Hi {everything}") @@ -10,9 +6,8 @@ f <- function() { log_info("Hi %s", everything) } -log_formatter(formatter_glue) -log_appender(appender_stdout) test_that('glue works', { + local_test_logger(formatter = formatter_glue) expect_equal(formatter_glue("Hi"), "Hi") expect_equal(formatter_glue(" Hi"), " Hi") @@ -31,7 +26,10 @@ test_that('glue works', { expect_output(log_warn("Hi {everything}"), '42') expect_output(g(), '42') - log_appender(appender_void) + local_test_logger( + formatter = formatter_glue, + appender = appender_void, + ) expect_error(formatter_glue('malformed {')) expect_error(formatter_glue('malformed {{'), NA) @@ -39,12 +37,10 @@ test_that('glue works', { ## expect_warning(formatter_glue(NULL)) ## expect_warning(log_info(NULL)) ## expect_warning(log_info(a = 42, b = "foobar")) - log_appender(appender_stdout) - }) -log_formatter(formatter_glue_safe) test_that('glue_safe works', { + local_test_logger(formatter = formatter_glue_safe) expect_equal(formatter_glue_safe("Hi"), "Hi") expect_equal(formatter_glue_safe(" Hi"), " Hi") @@ -61,8 +57,8 @@ test_that('glue_safe works', { }) -log_formatter(formatter_sprintf) test_that('sprintf works', { + local_test_logger(formatter = formatter_sprintf) expect_equal(formatter_sprintf("Hi"), "Hi") expect_equal(formatter_sprintf("Hi %s", 42), "Hi 42") @@ -100,18 +96,18 @@ test_that('glue+sprintf works', { expect_equal(formatter_glue_or_sprintf('fun{fun}'), 'fun{fun}') for (fn in c(formatter_sprintf, formatter_glue_or_sprintf)) { - log_formatter(fn) - log_appender(appender_void) + local_test_logger(formatter = fn, appender = appender_void) expect_error(log_info(character(0)), NA) - log_appender(appender_stdout) + + local_test_logger(formatter = fn) expect_output(log_info(character(0)), 'INFO') } }) test_that('formatter_logging works', { + local_test_logger(formatter = formatter_logging) - log_formatter(formatter_logging) expect_output(log_info('42'), '42') expect_output(log_info(42), '42') expect_output(log_info(4+2), '4 \\+ 2') @@ -128,15 +124,17 @@ test_that('formatter_logging works', { test_that('special chars in the text work', { expect_equal(formatter_glue('JSON: {jsonlite::toJSON(1:4)}'), 'JSON: [1,2,3,4]') expect_equal(formatter_glue('JSON: {jsonlite::toJSON(iris[1:2, ], auto_unbox = TRUE)}'), 'JSON: [{"Sepal.Length":5.1,"Sepal.Width":3.5,"Petal.Length":1.4,"Petal.Width":0.2,"Species":"setosa"},{"Sepal.Length":4.9,"Sepal.Width":3,"Petal.Length":1.4,"Petal.Width":0.2,"Species":"setosa"}]') # nolint + + local_test_logger() expect_output(log_info('JSON: {jsonlite::toJSON(1:4)}'), '[1,2,3,4]') expect_output(log_info('JSON: {jsonlite::toJSON(iris[1:2, ], auto_unbox = TRUE)}'), '[{"Sepal.Length":5.1,"Sepal.Width":3.5,"Petal.Length":1.4,"Petal.Width":0.2,"Species":"setosa"},{"Sepal.Length":4.9,"Sepal.Width":3,"Petal.Length":1.4,"Petal.Width":0.2,"Species":"setosa"}]') # nolint }) test_that('pander formatter', { - log_formatter(formatter_pander) + local_test_logger(formatter = formatter_pander) # pander partially matches coef to coefficient withr::local_options(warnPartialMatchDollar = FALSE) - + expect_output(log_info(42), '_42_') expect_output(log_info('42'), '42') expect_output(log_info(head(iris)), 'Sepal.Length') @@ -147,23 +145,20 @@ test_that('pander formatter', { rm(everything) rm(f) -log_formatter(formatter_paste) test_that('paste formatter in actual logs', { + local_test_logger(formatter = formatter_paste) expect_output(log_info('hi', 5), 'hi 5') }) -log_formatter(formatter_glue) test_that('skip formatter', { - expect_output(log_info(skip_formatter('hi {pi}')), 'hi \\{pi\\}') + local_test_logger(formatter = formatter_glue) + expect_output(log_info(skip_formatter('hi {pi}')), 'hi \\{pi\\}') expect_error(log_info(skip_formatter(mtcars))) expect_error(log_info(skip_formatter('hi {x}', x = 4))) }) -log_formatter(formatter_json) test_that('skip formatter', { + local_test_logger(formatter = formatter_json) expect_output(log_info(skip_formatter('hi {pi}')), 'hi \\{pi\\}') expect_output(log_info(x = 1), '\\{"x":1\\}') }) - -log_formatter(formatter) -log_appender(appender) diff --git a/tests/testthat/test-helpers.R b/tests/testthat/test-helpers.R index 193de326..06cd2e5c 100644 --- a/tests/testthat/test-helpers.R +++ b/tests/testthat/test-helpers.R @@ -1,37 +1,28 @@ -## save current settings so that we can reset later -appender <- log_appender() - -log_appender(appender_stdout) - test_that('separator', { - original_layout <- log_layout() - log_layout(layout_blank) + local_test_logger(layout = layout_blank) expect_output(log_separator(), '={80,80}') - log_layout(original_layout) + + local_test_logger() expect_output(log_separator(separator = '-'), '---') expect_output(log_separator(), 'INFO') expect_output(log_separator(WARN), 'WARN') }) test_that('tictoc', { + local_test_logger() expect_output(log_tictoc(), 'timer') }) test_that('log with separator', { + local_test_logger() expect_output(log_with_separator(42), '===') - logger <- layout_glue_generator(format = '{node}/{pid}/{namespace}/{fn} {time} {level}: {msg}') - layout_original <- log_layout() - log_layout(logger) expect_output(log_with_separator('Boo!', level = FATAL, width = 120), width = 120) - log_layout(layout_original) }) test_that('log failure', { + local_test_logger() expect_output(log_failure("foobar"), NA) expect_output(try(log_failure(foobar), silent = TRUE), 'ERROR.*foobar') expect_error(log_failure('foobar'), NA) expect_match(capture.output(expect_error(log_failure(foobar))), 'not found') }) - -## reset settings -log_appender(appender) diff --git a/tests/testthat/test-layout.R b/tests/testthat/test-layout.R index 0d809d1b..1e799614 100644 --- a/tests/testthat/test-layout.R +++ b/tests/testthat/test-layout.R @@ -1,17 +1,11 @@ -## save current settings so that we can reset later -layout <- log_layout() -appender <- log_appender() - -log_appender(appender_stdout) - -log_layout(layout_blank) test_that('blank layout', { + local_test_logger(layout = layout_blank) expect_output(log_info('foobar'), 'foobar') expect_equal(capture.output(log_info('foobar')), 'foobar') }) -log_layout(layout_glue_colors) test_that('colorized layout', { + local_test_logger(layout = layout_glue_colors) expect_output(log_info('foobar'), 'INFO') expect_output(log_info('foobar'), 'foobar') expect_output(log_error('foobar'), 'ERROR') @@ -19,22 +13,24 @@ test_that('colorized layout', { }) test_that('metavars', { - log_layout(layout_glue_generator(format = '{level} {ans} {fn} {msg}')) + local_test_logger(layout = layout_glue_generator('{level} {ans} {fn} {msg}')) expect_output((function(){log_info(42)})(), 'INFO') expect_output((function(){log_warn(42)})(), 'WARN') expect_output((function(){log_info(42)})(), 'log_info') - log_layout(layout_glue_generator(format = '{fn}')) + + local_test_logger(layout = layout_glue_generator('{fn}')) expect_output({fun42<-function(){log_info(42)};fun42();rm(fun42)}, 'fun42') }) -log_layout(layout_json()) test_that('JSON layout', { + local_test_logger(layout = layout_json()) + expect_equal(jsonlite::fromJSON(capture.output(log_info('foobar')))$level, 'INFO') expect_equal(jsonlite::fromJSON(capture.output(log_info('foobar')))$msg, 'foobar') }) -log_layout(layout_json_parser(fields = c())) test_that('JSON parser layout', { + local_test_logger(layout = layout_json_parser(fields = c())) expect_output(log_info(skip_formatter('{"x": 4}')), '\\{"x":4\\}') expect_equal(capture.output(log_info(skip_formatter('{"x": 4}'))), '{"x":4}') }) @@ -52,15 +48,11 @@ test_that('must throw errors', { }) -log_layout(layout_logging) test_that('logging layout', { + local_test_logger(layout = layout_logging) expect_output(log_level(INFO, 'foo', namespace = 'bar'), 'INFO:bar:foo') expect_output(log_info('foobar'), 'INFO') expect_output(log_info('foo', namespace = 'bar'), 'foo') expect_output(log_info('foo', namespace = 'bar'), 'bar') expect_output(log_info('foo', namespace = 'bar'), 'INFO:bar:foo') }) - -## reset settings -log_layout(layout) -log_appender(appender) diff --git a/tests/testthat/test-logger.R b/tests/testthat/test-logger.R index 3e6220cf..6433046f 100644 --- a/tests/testthat/test-logger.R +++ b/tests/testthat/test-logger.R @@ -1,12 +1,6 @@ -## save current settings so that we can reset later -threshold <- log_threshold() -appender <- log_appender() -layout <- log_layout() - -log_appender(appender_stdout) - -log_threshold(WARN) test_that('log levels', { + local_test_logger(WARN) + expect_output(log_fatal('foo'), 'FATAL.*foo') expect_output(log_error('foo'), 'ERROR.*foo') expect_output(log_warn('foo'), 'WARN.*foo') @@ -26,8 +20,8 @@ test_that('log levels', { expect_output(log_level(as.loglevel(600L), 'foo'), NA) }) -log_threshold(OFF) test_that('log levels - OFF', { + local_test_logger(OFF) expect_output(log_fatal('foo'), NA) expect_output(log_error('foo'), NA) expect_output(log_warn('foo'), NA) @@ -37,8 +31,8 @@ test_that('log levels - OFF', { expect_output(log_trace('foo'), NA) }) -log_threshold(TRACE) test_that('log thresholds', { + local_test_logger(TRACE) expect_output(log_fatal('foo'), 'FATAL.*foo') expect_output(log_error('foo'), 'ERROR.*foo') expect_output(log_warn('foo'), 'WARN.*foo') @@ -48,8 +42,8 @@ test_that('log thresholds', { expect_output(log_trace('foo'), 'TRACE.*foo') }) -log_threshold(WARN) test_that('with log thresholds', { + local_test_logger(WARN) expect_output(with_log_threshold(log_fatal('foo'), threshold = TRACE), 'FATAL.*foo') expect_output(with_log_threshold(log_error('foo'), threshold = TRACE), 'ERROR.*foo') expect_output(with_log_threshold(log_error('foo'), threshold = FATAL), NA) @@ -57,9 +51,9 @@ test_that('with log thresholds', { expect_output(with_log_threshold(log_debug('foo'), threshold = INFO), NA) }) -log_layout(layout_glue_generator('{level} {msg}')) -log_threshold(TRACE) test_that('simple glue layout with no threshold', { + local_test_logger(TRACE, layout = layout_glue_generator('{level} {msg}')) + expect_equal(capture.output(log_fatal('foobar')), 'FATAL foobar') expect_equal(capture.output(log_error('foobar')), 'ERROR foobar') expect_equal(capture.output(log_warn('foobar')), 'WARN foobar') @@ -68,8 +62,8 @@ test_that('simple glue layout with no threshold', { expect_equal(capture.output(log_trace('foobar')), 'TRACE foobar') }) -log_threshold(INFO) test_that('simple glue layout with threshold', { + local_test_logger(INFO, layout = layout_glue_generator('{level} {msg}')) expect_equal(capture.output(log_fatal('foobar')), 'FATAL foobar') expect_equal(capture.output(log_error('foobar')), 'ERROR foobar') expect_equal(capture.output(log_warn('foobar')), 'WARN foobar') @@ -79,20 +73,22 @@ test_that('simple glue layout with threshold', { }) test_that('namespaces', { - log_threshold(ERROR, namespace = 'custom') + local_test_logger(ERROR, namespace = 'custom', layout = layout_glue_generator('{level} {msg}')) expect_output(log_fatal('foobar', namespace = 'custom'), 'FATAL foobar') expect_output(log_error('foobar', namespace = 'custom'), 'ERROR foobar') expect_output(log_info('foobar', namespace = 'custom'), NA) expect_output(log_debug('foobar', namespace = 'custom'), NA) - log_threshold(INFO, namespace = 'custom') + + local_test_logger(INFO, namespace = 'custom', layout = layout_glue_generator('{level} {msg}')) expect_output(log_info('foobar', namespace = 'custom'), 'INFO foobar') expect_output(log_debug('foobar', namespace = 'custom'), NA) + log_threshold(TRACE, namespace = log_namespaces()) expect_output(log_debug('foobar', namespace = 'custom'), 'DEBUG foobar') - log_threshold(INFO) }) test_that('simple glue layout with threshold directly calling log', { + local_test_logger(layout = layout_glue_generator('{level} {msg}')) expect_equal(capture.output(log_level(FATAL, 'foobar')), 'FATAL foobar') expect_equal(capture.output(log_level(ERROR, 'foobar')), 'ERROR foobar') expect_equal(capture.output(log_level(WARN, 'foobar')), 'WARN foobar') @@ -102,12 +98,12 @@ test_that('simple glue layout with threshold directly calling log', { }) test_that('built in variables: pid', { - log_layout(layout_glue_generator('{pid}')) + local_test_logger(layout = layout_glue_generator('{pid}')) expect_equal(capture.output(log_info('foobar')), as.character(Sys.getpid())) }) test_that('built in variables: fn and call', { - log_layout(layout_glue_generator('{fn} / {call}')) + local_test_logger(layout = layout_glue_generator('{fn} / {call}')) f <- function() log_info('foobar') expect_output(f(), 'f / f()') g <- function() f() @@ -117,9 +113,10 @@ test_that('built in variables: fn and call', { }) test_that('built in variables: namespace', { - log_layout(layout_glue_generator('{ns}')) + local_test_logger(layout = layout_glue_generator('{ns}')) expect_output(log_info('bar', namespace = 'foo'), 'foo') - log_layout(layout_glue_generator('{ans}')) + + local_test_logger(layout = layout_glue_generator('{ans}')) expect_output(log_info('bar', namespace = 'foo'), 'global') }) @@ -128,6 +125,8 @@ test_that('print.level', { }) test_that('config setter called from do.call', { + local_test_logger() + t <- tempfile() expect_error(do.call(log_appender, list(appender_file(t))), NA) log_info(42) @@ -145,10 +144,6 @@ test_that('config setter called from do.call', { }) test_that('providing log_level() args to wrappers diretly is OK', { + local_test_logger(WARN) expect_silent(log_info('{Sepal.Length}', .topenv = iris)) }) - -## reset settings -log_threshold(threshold) -log_layout(layout) -log_appender(appender) diff --git a/tests/testthat/test-return.R b/tests/testthat/test-return.R index fef5219d..b596c17a 100644 --- a/tests/testthat/test-return.R +++ b/tests/testthat/test-return.R @@ -1,15 +1,10 @@ -## save current settings so that we can reset later -formatter <- log_formatter() -appender <- log_appender() - -t <- tempfile() -log_appender(appender_file(t)) - glue_or_sprintf_result <- c( "Hi foo, did you know that 2*4=8?", "Hi bar, did you know that 2*4=8?") test_that("return value is formatted string", { + local_test_logger(appender = appender_file(withr::local_tempfile())) + log_formatter(formatter_glue) expect_equal(log_info('pi is {round(pi, 2)}')[[1]]$message, 'pi is 3.14') expect_match(log_info('pi is {round(pi, 2)}')[[1]]$record, 'INFO [[0-9: -]*] pi is 3.14') @@ -17,9 +12,3 @@ test_that("return value is formatted string", { expect_equal(log_info('pi is {round(pi, 2)}')[[1]]$message, 'pi is 3.14') expect_equal(log_info('pi is {round(pi, 2)}')[[2]]$message, 'pi is {round(pi, 2)}') }) - -## reset settings -unlink(t) -delete_logger_index(index = 2) -log_formatter(formatter) -log_appender(appender) diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index 31ef445f..17357748 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -1,7 +1,3 @@ -## save current settings so that we can reset later -appender <- log_appender() -log_appender(appender_stdout) - test_that('fail_on_missing_package', { expect_error(fail_on_missing_package('logger'), NA) expect_error(fail_on_missing_package('logger', '9.9.9')) @@ -9,6 +5,7 @@ test_that('fail_on_missing_package', { }) test_that('except helper', { + local_test_logger() expect_equal(FunDoesNotExist(1:10) %except% sum(1:10) / length(1:10), 5.5) expect_output(FunDoesNotExist(1:10) %except% sum(1:10) / length(1:10), 'WARN') }) @@ -22,17 +19,13 @@ test_that('validate_log_level', { test_that('catch_base_log', { expect_true(nchar(catch_base_log(ERROR, NA_character_)) == 28) expect_true(nchar(catch_base_log(INFO, NA_character_)) == 27) - layout_original <- log_layout() - log_layout(layout_blank) + local_test_logger(layout = layout_blank) expect_true(nchar(catch_base_log(INFO, NA_character_)) == 0) - log_layout(layout_original) - layout_original <- log_layout(namespace = 'TEMP') - logger <- layout_glue_generator(format = '{namespace}/{fn} {level}: {msg}') - log_layout(logger, namespace = 'TEMP') + + local_test_logger( + layout = layout_glue_generator(format = '{namespace}/{fn} {level}: {msg}'), + namespace = "TEMP" + ) expect_true(nchar(catch_base_log(INFO, 'TEMP', .topcall = NA)) == 14) expect_true(nchar(catch_base_log(INFO, 'TEMP', .topcall = call('5char'))) == 17) - log_layout(layout_original) }) - -## reset settings -log_appender(appender) diff --git a/vignettes/anatomy.Rmd b/vignettes/anatomy.Rmd index a8141153..3396ab73 100644 --- a/vignettes/anatomy.Rmd +++ b/vignettes/anatomy.Rmd @@ -5,6 +5,8 @@ vignette: > %\VignetteIndexEntry{The Anatomy of a Log Request} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} +resource_files: + - logger_structure.svg --- ```{r setup, include = FALSE}