diff --git a/NAMESPACE b/NAMESPACE index 92cf3b5..d09e32c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -12,3 +12,4 @@ export(set_timestamp_format) export(stop) export(stopifnot) export(warning) +export(with_loggit) diff --git a/NEWS.md b/NEWS.md index e72b060..d38ff0e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,6 +7,7 @@ ## New features * Add `convert_to_csv()` to convert log files to CSV format. +* Add `with_loggit()` to log third-party code or to easily use different `loggit()`-parameters for a chunk of code. ## Bugfixes * `read_logs()` now correctly reads empty character values `""`, as in `{"key": ""}`, as such. diff --git a/R/loggit.R b/R/loggit.R index 31140ae..042ac3b 100644 --- a/R/loggit.R +++ b/R/loggit.R @@ -10,6 +10,7 @@ #' Defaults to `TRUE`. #' @param custom_log_lvl Allow log levels other than "DEBUG", "INFO", "WARN", #' and "ERROR"? Defaults to `FALSE`. +#' @inheritParams write_ndjson #' #' @return Invisible `NULL`. #' @@ -18,7 +19,7 @@ #' sure = "why not?", like = 2, or = 10, what = "ever") #' #' @export -loggit <- function(log_lvl, log_msg, ..., echo = TRUE, custom_log_lvl = FALSE) { +loggit <- function(log_lvl, log_msg, ..., echo = TRUE, custom_log_lvl = FALSE, logfile = get_logfile()) { # Try to suggest limited log levels to prevent typos by users log_lvls <- c("DEBUG", "INFO", "WARN", "ERROR") if (!custom_log_lvl && !(log_lvl %in% log_lvls)) { @@ -52,5 +53,5 @@ loggit <- function(log_lvl, log_msg, ..., echo = TRUE, custom_log_lvl = FALSE) { ) } - write_ndjson(log_df, echo = echo) + write_ndjson(log_df, echo = echo, logfile = logfile) } diff --git a/R/whit_loggit.R b/R/whit_loggit.R new file mode 100644 index 0000000..8510d91 --- /dev/null +++ b/R/whit_loggit.R @@ -0,0 +1,28 @@ +#' Log any expressions +#' +#' Log code without having to explicitly use the `loggit2` handlers. +#' This is particularly useful for code that cannot be customized, e.g. from third-party packages +#' +#' @param exp An expression to evaluate. +#' @inheritParams loggit +#' +#' @details If `loggit2` handlers are already used in the expression, this can lead to conditions being logged +#' twice (in the same or different files) +#' +#' @return The result of the expression. +#' +#' @export +with_loggit <- function(exp, logfile = get_logfile(), echo = TRUE) { + withCallingHandlers( + exp, + error = function(e) { + loggit(log_lvl = "ERROR", log_msg = e$message, echo = echo, logfile = logfile) + }, + warning = function(w) { + loggit(log_lvl = "WARN", log_msg = w$message, echo = echo, logfile = logfile) + }, + message = function(m) { + loggit(log_lvl = "INFO", log_msg = m$message, echo = echo, logfile = logfile) + } + ) +} diff --git a/man/loggit.Rd b/man/loggit.Rd index 3d4e258..94993c6 100644 --- a/man/loggit.Rd +++ b/man/loggit.Rd @@ -4,7 +4,14 @@ \alias{loggit} \title{Log entries to file} \usage{ -loggit(log_lvl, log_msg, ..., echo = TRUE, custom_log_lvl = FALSE) +loggit( + log_lvl, + log_msg, + ..., + echo = TRUE, + custom_log_lvl = FALSE, + logfile = get_logfile() +) } \arguments{ \item{log_lvl}{Log level coerceable to \code{character}. For details see parameter \code{custom_log_lvl}.} @@ -19,6 +26,9 @@ Defaults to \code{TRUE}.} \item{custom_log_lvl}{Allow log levels other than "DEBUG", "INFO", "WARN", and "ERROR"? Defaults to \code{FALSE}.} + +\item{logfile}{Log file to write to. Defaults to currently-configured log +file.} } \value{ Invisible \code{NULL}. diff --git a/man/with_loggit.Rd b/man/with_loggit.Rd new file mode 100644 index 0000000..624e9fe --- /dev/null +++ b/man/with_loggit.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/whit_loggit.R +\name{with_loggit} +\alias{with_loggit} +\title{Log any expressions} +\usage{ +with_loggit(exp, logfile = get_logfile(), echo = TRUE) +} +\arguments{ +\item{exp}{An expression to evaluate.} + +\item{logfile}{Log file to write to. Defaults to currently-configured log +file.} + +\item{echo}{Should the log file entry be printed to the console as well? +Defaults to \code{TRUE}.} +} +\value{ +The result of the expression. +} +\description{ +Log code without having to explicitly use the \code{loggit2} handlers. +This is particularly useful for code that cannot be customized, e.g. from third-party packages +} +\details{ +If \code{loggit2} handlers are already used in the expression, this can lead to conditions being logged +twice (in the same or different files) +} diff --git a/tests/testthat/test-whit_loggit.R b/tests/testthat/test-whit_loggit.R new file mode 100644 index 0000000..5922883 --- /dev/null +++ b/tests/testthat/test-whit_loggit.R @@ -0,0 +1,79 @@ +test_that("Message, warning, and error get writen to the log file", { + + test_function <- function(x) { + base::message("A message") + base::warning("A warning") + } + + expect_warning( + expect_message( + with_loggit(test_function(y), echo = FALSE), + "A message" + ), + "A warning" + ) + + expect_error( + with_loggit({ + base::message("A message") + base::stop("An error") + }, echo = FALSE), + "An error" + ) + + logs_json <- read_logs() + expect_identical(nrow(logs_json), 4L) + expect_identical(logs_json$log_lvl, c("INFO", "WARN", "INFO", "ERROR")) + expect_identical(logs_json$log_msg, c("A message\n", "A warning", "A message\n", "An error")) +}) +cleanup() + +test_that("Different log files for handler and with_loggit", { + + default_logfile <- get_logfile() + with_logfile <- file.path(tempdir(), "with_loggit.log") + + test_function <- function(x) { + with_loggit({ + base::message("A message") + base::warning("A warning") + }, echo = FALSE, logfile = default_logfile) + } + + expect_warning( + expect_message( + with_loggit(test_function(y), echo = FALSE, logfile = with_logfile), + "A message" + ), + "A warning" + ) + + expect_identical(get_logfile(), default_logfile) + default_log <- read_logs(logfile = default_logfile) + with_log <- read_logs(logfile = with_logfile) + expect_identical(default_log$log_lvl, with_log$log_lvl) + expect_identical(default_log$log_msg, with_log$log_msg) + +}) +cleanup() + +test_that("Combination of tryCatch and with_loggit", { + # Message + expect_message( + tryCatch({ + with_loggit(base::message("A message"), echo = FALSE) + }, message = function(e) base::message("tryCatch") + ), + "tryCatch" + ) + + # Warning + expect_message( + tryCatch({ + with_loggit(base::warning("A message"), echo = FALSE) + }, warning = function(e) base::message("tryCatch") + ), + "tryCatch" + ) +}) +cleanup()