diff --git a/DESCRIPTION b/DESCRIPTION index 168220dd..ea140002 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -22,6 +22,7 @@ Imports: utils Suggests: botor, + cli, covr, crayon, devtools, diff --git a/NAMESPACE b/NAMESPACE index 8b1d5bc0..09188710 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -32,6 +32,7 @@ export(colorize_by_log_level) export(delete_logger_index) export(deparse_to_one_line) export(fail_on_missing_package) +export(formatter_cli) export(formatter_glue) export(formatter_glue_or_sprintf) export(formatter_glue_safe) diff --git a/NEWS.md b/NEWS.md index 5edf5e75..a2644605 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,6 +7,7 @@ * `log_appender()`, `log_layout()` and `log_formatter()` now check that you are calling them with a function, and return the previously set value (#170, @hadley) * new function to return number of log indices (#194, @WurmPeter) * `appender_async` is now using `mirai` instead of a custom background process and queue system (#214, @hadley @shikokuchuo) +* New `formatter_cli()` allows you to use the syntax from the cli package to create log messages (#210, @thomasp85) ## Fixes diff --git a/R/formatters.R b/R/formatters.R index e4ed5299..83000407 100644 --- a/R/formatters.R +++ b/R/formatters.R @@ -173,6 +173,36 @@ formatter_glue_or_sprintf <- function(msg, } attr(formatter_glue_or_sprintf, "generator") <- quote(formatter_glue_or_sprintf()) +#' Apply [cli::cli_text()] to format string with cli syntax +#' @param ... passed to [cli::cli_text()] for the text interpolation +#' @inheritParams log_level +#' @return character vector +#' @export +#' @family log_formatters +#' @importFrom utils str +formatter_cli <- function(..., + .logcall = sys.call(), + .topcall = sys.call(-1), + .topenv = parent.frame()) { + fail_on_missing_package("cli") + + withCallingHandlers( + cli::cli_fmt(cli::cli_text(..., .envir = .topenv)), + error = function(e) { + args <- paste0(capture.output(str(...)), collapse = "\n") + + stop(paste0( + "`cli` failed in `formatter_cli` on:\n\n", + args, + "\n\nRaw error message:\n\n", + conditionMessage(e), + "\n\nPlease consider using another `log_formatter` or ", + "`skip_formatter` on strings with curly braces." + )) + } + ) +} +attr(formatter_cli, "generator") <- quote(formatter_cli()) #' Transforms all passed R objects into a JSON list #' @param ... passed to `toJSON` wrapped into a `list` diff --git a/man/formatter_cli.Rd b/man/formatter_cli.Rd new file mode 100644 index 00000000..c9548835 --- /dev/null +++ b/man/formatter_cli.Rd @@ -0,0 +1,46 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/formatters.R +\name{formatter_cli} +\alias{formatter_cli} +\title{Apply \code{\link[cli:cli_text]{cli::cli_text()}} to format string with cli syntax} +\usage{ +formatter_cli( + ..., + .logcall = sys.call(), + .topcall = sys.call(-1), + .topenv = parent.frame() +) +} +\arguments{ +\item{...}{passed to \code{\link[cli:cli_text]{cli::cli_text()}} for the text interpolation} + +\item{.logcall}{the logging call being evaluated (useful in +formatters and layouts when you want to have access to the raw, +unevaluated R expression)} + +\item{.topcall}{R expression from which the logging function was +called (useful in formatters and layouts to extract the calling +function's name or arguments)} + +\item{.topenv}{original frame of the \code{.topcall} calling function +where the formatter function will be evaluated and that is used +to look up the \code{namespace} as well via \code{logger:::top_env_name}} +} +\value{ +character vector +} +\description{ +Apply \code{\link[cli:cli_text]{cli::cli_text()}} to format string with cli syntax +} +\seealso{ +Other log_formatters: +\code{\link{formatter_glue}()}, +\code{\link{formatter_glue_or_sprintf}()}, +\code{\link{formatter_glue_safe}()}, +\code{\link{formatter_json}()}, +\code{\link{formatter_logging}()}, +\code{\link{formatter_pander}()}, +\code{\link{formatter_paste}()}, +\code{\link{formatter_sprintf}()} +} +\concept{log_formatters} diff --git a/man/formatter_glue.Rd b/man/formatter_glue.Rd index a967f5de..60475bcf 100644 --- a/man/formatter_glue.Rd +++ b/man/formatter_glue.Rd @@ -39,6 +39,7 @@ will be used as a fallback. } \seealso{ Other log_formatters: +\code{\link{formatter_cli}()}, \code{\link{formatter_glue_or_sprintf}()}, \code{\link{formatter_glue_safe}()}, \code{\link{formatter_json}()}, diff --git a/man/formatter_glue_or_sprintf.Rd b/man/formatter_glue_or_sprintf.Rd index 0240dc8f..a47dd326 100644 --- a/man/formatter_glue_or_sprintf.Rd +++ b/man/formatter_glue_or_sprintf.Rd @@ -56,6 +56,7 @@ formatter_glue_or_sprintf("Hi \%s, did you know that 2*4=\%s", c("foo", "bar"), } \seealso{ Other log_formatters: +\code{\link{formatter_cli}()}, \code{\link{formatter_glue}()}, \code{\link{formatter_glue_safe}()}, \code{\link{formatter_json}()}, diff --git a/man/formatter_glue_safe.Rd b/man/formatter_glue_safe.Rd index 7c8016f4..bb14075b 100644 --- a/man/formatter_glue_safe.Rd +++ b/man/formatter_glue_safe.Rd @@ -34,6 +34,7 @@ Apply \code{\link[glue:glue_safe]{glue::glue_safe()}} to convert R objects into } \seealso{ Other log_formatters: +\code{\link{formatter_cli}()}, \code{\link{formatter_glue}()}, \code{\link{formatter_glue_or_sprintf}()}, \code{\link{formatter_json}()}, diff --git a/man/formatter_json.Rd b/man/formatter_json.Rd index 70b7e2c1..b8d7b37a 100644 --- a/man/formatter_json.Rd +++ b/man/formatter_json.Rd @@ -45,6 +45,7 @@ log_info(mtcars = mtcars, species = iris$Species) } \seealso{ Other log_formatters: +\code{\link{formatter_cli}()}, \code{\link{formatter_glue}()}, \code{\link{formatter_glue_or_sprintf}()}, \code{\link{formatter_glue_safe}()}, diff --git a/man/formatter_logging.Rd b/man/formatter_logging.Rd index 502ac8ae..550f6ad5 100644 --- a/man/formatter_logging.Rd +++ b/man/formatter_logging.Rd @@ -50,6 +50,7 @@ log_info(12, 1 + 1, 2 * 2) } \seealso{ Other log_formatters: +\code{\link{formatter_cli}()}, \code{\link{formatter_glue}()}, \code{\link{formatter_glue_or_sprintf}()}, \code{\link{formatter_glue_safe}()}, diff --git a/man/formatter_pander.Rd b/man/formatter_pander.Rd index 235a0c73..67c9fc93 100644 --- a/man/formatter_pander.Rd +++ b/man/formatter_pander.Rd @@ -51,6 +51,7 @@ log_info(lm(hp ~ wt, mtcars)) } \seealso{ Other log_formatters: +\code{\link{formatter_cli}()}, \code{\link{formatter_glue}()}, \code{\link{formatter_glue_or_sprintf}()}, \code{\link{formatter_glue_safe}()}, diff --git a/man/formatter_paste.Rd b/man/formatter_paste.Rd index 05a1a077..2385f068 100644 --- a/man/formatter_paste.Rd +++ b/man/formatter_paste.Rd @@ -34,6 +34,7 @@ Concatenate R objects into a character vector via \code{paste} } \seealso{ Other log_formatters: +\code{\link{formatter_cli}()}, \code{\link{formatter_glue}()}, \code{\link{formatter_glue_or_sprintf}()}, \code{\link{formatter_glue_safe}()}, diff --git a/man/formatter_sprintf.Rd b/man/formatter_sprintf.Rd index c40b91f2..4c10a963 100644 --- a/man/formatter_sprintf.Rd +++ b/man/formatter_sprintf.Rd @@ -37,6 +37,7 @@ Apply \code{\link[=sprintf]{sprintf()}} to convert R objects into a character ve } \seealso{ Other log_formatters: +\code{\link{formatter_cli}()}, \code{\link{formatter_glue}()}, \code{\link{formatter_glue_or_sprintf}()}, \code{\link{formatter_glue_safe}()}, diff --git a/tests/testthat/test-formatters.R b/tests/testthat/test-formatters.R index 165fa910..477b5f15 100644 --- a/tests/testthat/test-formatters.R +++ b/tests/testthat/test-formatters.R @@ -109,6 +109,29 @@ test_that("glue+sprintf works", { } }) +test_that("cli works", { + local_test_logger(formatter = formatter_cli) + + a <- 43 + + expect_equal(formatter_cli("Hi"), "Hi") + expect_equal(formatter_cli("{.arg Hi}"), "`Hi`") + expect_equal(formatter_cli("1 + {1}"), "1 + 1") + expect_equal(formatter_cli("{1:2}"), "1 and 2") + expect_equal(formatter_cli("pi is {round(pi, 2)}"), "pi is 3.14") + expect_equal(formatter_cli("Hi {42}"), "Hi 42") + expect_equal(formatter_cli("Hi {1:2}"), paste("Hi 1 and 2")) + + expect_output(do.call(logger, namespaces$global[[1]])(INFO, 42), "42") + expect_output(do.call(logger, namespaces$global[[1]])(INFO, "Hi {a}"), "43") + + expect_equal(formatter_cli("Hi {a}"), "Hi 43") + expect_output(log_info("Hi {a}"), "43") + expect_output(log_warn("Hi {a}"), "43") + f <- function() log_info("Hi {a}") + expect_output(f(), "43") +}) + test_that("formatter_logging works", { local_test_logger(formatter = formatter_logging)