Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ Suggests:
Encoding: UTF-8
LazyLoad: yes
Roxygen: list(old_usage = TRUE)
RoxygenNote: 7.1.1
RoxygenNote: 7.2.3
VignetteBuilder: knitr
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export(logformat)
export(logger)
export(loglevel)
export(simple_log_layout)
export(stderr_appender)
export(syslog_appender)
export(tcp_appender)
export(verbosity)
Expand Down
11 changes: 11 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# log4r 0.4.2

* New `stderr_appender()` that writes to stderr (useful for RMarkdown docs).

* `json_log_layout()` gains `include_timestamp` argument which can be set
to `FALSE` if whatever system collating your logs already adds a timestamp.

* `logger()` uses a more complex algorithm to determine the default `logger`.
It uses `stderr_appender()` if you're running in knitr (so the result appears
in the log, and not in your doc) and it uses `simple_layout()` if you're
in a situation that's likely to automatically timestamps (i.e. running on
CI platform or in Posit connect).

* Fixes a crash where `logfmt_log_layout()` would not correctly handle memory
reallocation of the underlying buffer.

Expand Down
10 changes: 8 additions & 2 deletions R/appenders.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
#' the nature of the destination, the format of the messages may be controlled
#' using a \strong{\link[=layouts]{Layout}}.
#'
#' The most basic appenders log messages to the console or to a file; these are
#' described below.
#' The most basic appenders log messages to the console (stdout), stderr,
#' or to a file; these are described below.
#'
#' For implementing your own appenders, see Details.
#'
Expand Down Expand Up @@ -40,6 +40,12 @@ console_appender <- function(layout = default_log_layout()) {
file_appender(file = "", layout = layout)
}

#' @export
#' @rdname appenders
stderr_appender <- function(layout = default_log_layout()) {
file_appender(file = stderr(), layout = layout)
}

#' @param file The file to write messages to.
#' @param append When \code{TRUE}, the file is not truncated when opening for
#' the first time.
Expand Down
36 changes: 35 additions & 1 deletion R/create.logger.R
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ function(logfile = 'logfile.log', level = 'FATAL', logformat = NULL)
#' level will be discarded. See \code{\link{loglevel}}.
#' @param appenders The logging appenders; both single appenders and a
#' \code{list()} of them are supported. See \code{\link{appenders}}.
#' The default value, `NULL`, creates an appender that should work well
#' in your current environment. For example, if you are inside an `.Rmd`
#' or `.qmd` it will log to `stderr()`, and if you're running on a CI
#' service or in Posit connect, it will suppress the timestamps (since
#' they will typically be added automatically).
#'
#' @return An object of class \code{"logger"}.
#'
Expand All @@ -54,8 +59,12 @@ function(logfile = 'logfile.log', level = 'FATAL', logformat = NULL)
#' for information on controlling the behaviour of the logger object.
#'
#' @export
logger <- function(threshold = "INFO", appenders = console_appender()) {
logger <- function(threshold = "INFO", appenders = NULL) {
threshold <- as.loglevel(threshold)

if (is.null(appenders)) {
appenders <- default_appender()
}
if (!is.list(appenders)) {
appenders <- list(appenders)
}
Expand All @@ -68,3 +77,28 @@ logger <- function(threshold = "INFO", appenders = console_appender()) {
class = "logger"
)
}

default_appender <- function() {
if (on_ci() || on_connect()) {
layout <- simple_log_layout()
} else {
layout <- default_log_layout()
}

if (is_knitr()) {
stderr_appender(layout)
} else {
console_appender(layout)
}
}

is_knitr <- function() {
isTRUE(getOption("knitr.in.progress", FALSE))
}

on_ci <- function() {
isTRUE(as.logical(Sys.getenv("CI")))
}
on_connect <- function() {
identical(Sys.getenv("RSTUDIO_PRODUCT"), "CONNECT")
}
9 changes: 6 additions & 3 deletions R/layouts.R
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,13 @@ logfmt_log_layout <- function() {
#' @details \code{json_log_layout} requires the \code{jsonlite} package.
#'
#' @rdname layouts
#' @aliases json_log_layout
#' @param include_timestamp Include the current timestamp in the output?
#' @export
json_log_layout <- function() {
json_log_layout <- function(include_timestamp = TRUE) {
if (!requireNamespace("jsonlite", quietly = TRUE)) {
stop("The 'jsonlite' package is required to use this JSON layout.")
}
force(include_timestamp)
time_format <- "%Y-%m-%dT%H:%M:%SZ"

function(level, ...) {
Expand All @@ -97,7 +98,9 @@ json_log_layout <- function() {
fields <- list(message = paste0(fields, collapse = ""))
}
fields$level <- as.character(level)
fields$time <- fmt_current_time(time_format, TRUE)
if (include_timestamp) {
fields$time <- fmt_current_time(time_format, TRUE)
}
jsonlite::toJSON(fields, auto_unbox = TRUE)
}
}
Expand Down
7 changes: 5 additions & 2 deletions man/appenders.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion man/layouts.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions man/logger.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tests/testthat/test-layouts.R
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,5 @@ test_that("JSON layouts work correctly", {
})

test_that("Wonky times formats are caught early", {
expect_error(default_log_layout(strrep("%Y", 30)), regex = "Invalid")
expect_error(default_log_layout(strrep("%Y", 30)), regexp = "Invalid")
})