Skip to content
Merged
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
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
^\.httr-oauth$
^revdep$
^.github$
^CRAN-SUBMISSION$
3 changes: 3 additions & 0 deletions CRAN-SUBMISSION
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Version: 0.4.4
Date: 2022-09-05 21:01:18 UTC
SHA: c725c929678ac9d18a9f45a42f1147d41b7a13d5
8 changes: 8 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@
`{knitr}` rendering process, log messages are now output to `stderr` instead
of `stdout` by default, to avoid polluting markdown documents (#62, thx @gadenbuie).

* BREAKING: added `rawMsg` property to LogEvents to store message without
string interpolation (e.g. that still contains the placeholders from
`sprintf()` or `glue()`). rawMessage will be added by default to json
log files (#60)

* updated Readme


# lgr 0.4.4

* `%k` and `%K` parameters in `format.LogEvent` now work as expected when using
Expand Down
19 changes: 13 additions & 6 deletions R/LogEvent.R
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ LogEvent <- R6::R6Class(
timestamp = Sys.time(),
caller = NA,
msg = NA,
rawMsg = msg,
...
){
assert(inherits(logger, "Logger"), "Logger must be a <Logger> object, not a ", class_fmt(logger))
Expand All @@ -64,6 +65,7 @@ LogEvent <- R6::R6Class(
assign("timestamp", timestamp, self)
assign("caller", caller, self)
assign("msg", msg, self)
assign("rawMsg", rawMsg, self)

# custom values
if (!missing(...)){
Expand Down Expand Up @@ -94,19 +96,22 @@ LogEvent <- R6::R6Class(

#' @field .logger [Logger]. A reference to the Logger that created the
#' event (equivalent to `get_logger(event$logger)`).
.logger = NULL
.logger = NULL,

#' @field rawMsg `character`. The raw log message without string
#' interpolation.
rawMsg = NULL
),

active = list(

#' @field values `list`. All values stored in the `LogEvent`, including
#' all *custom fields*, but not including `event$.logger`.
values = function(){
fixed_vals <- c("level", "timestamp", "logger", "caller", "msg")
fixed_vals <- c("level", "timestamp", "logger", "caller", "msg", "rawMsg")
custom_vals <- setdiff(
names(get(".__enclos_env__", self)[["self"]]),
c(".__enclos_env__", "level_name", "initialize", "clone", "values",
".logger")
c(".__enclos_env__", "level_name", "initialize", "clone", "values", ".logger")
)
valnames <- union(fixed_vals, custom_vals) # to enforce order of fixed_vals
mget(valnames, envir = self)
Expand Down Expand Up @@ -329,6 +334,7 @@ as_tibble.LogEvent <- function(
#' multiple threads.}
#' \item{`%c`}{the calling function}
#' \item{`%m`}{the log message}
#' \item{`%r`}{the raw log message (without string interpolation)
#' \item{`%f`}{all custom fields of `x` in a pseudo-JSON like format that is
#' optimized for human readability and console output}
#' \item{`%j`}{all custom fields of `x` in proper JSON. This requires that you
Expand Down Expand Up @@ -427,7 +433,7 @@ format.LogEvent <- function(
fmt,
valid_tokens = paste0(
"%",
c("t", "p", "c", "m", "l", "L", "n", "f", "j", "k", "K", "g"))
c("t", "p", "c", "m", "r", "l", "L", "n", "f", "j", "k", "K", "g"))
)

# format
Expand All @@ -444,6 +450,7 @@ format.LogEvent <- function(
"%K" = colorize_levels(lvls, colors, transform = function(.) toupper(strtrim(., 1))),
"%t" = format(get("timestamp", envir = x), format = timestamp_fmt),
"%m" = get("msg", envir = x),
"%r" = get("rawMsg", envir = x),
"%c" = get("caller", envir = x),
"%g" = get("logger", envir = x),
"%p" = Sys.getpid(),
Expand Down Expand Up @@ -591,4 +598,4 @@ tokenize_format <- function(

# globals --------------------------------------------------------

DEFAULT_FIELDS <- c("level", "timestamp", "logger", "caller", "msg")
DEFAULT_FIELDS <- c("level", "timestamp", "logger", "caller", "msg", "rawMsg")
45 changes: 27 additions & 18 deletions R/Logger.R
Original file line number Diff line number Diff line change
Expand Up @@ -226,14 +226,16 @@ Logger <- R6::R6Class(
}

force(caller)
rawMsg <- msg

if (missing(...)){
vals <- list(
logger = self,
level = level,
timestamp = timestamp,
caller = caller,
msg = msg
msg = msg,
rawMsg = rawMsg
)
} else {
dots <- list(...)
Expand All @@ -245,7 +247,8 @@ Logger <- R6::R6Class(
level = level,
timestamp = timestamp,
caller = caller,
msg = msg
msg = msg,
rawMsg = rawMsg
)
} else {
not_named <- vapply(names(dots), is_blank, TRUE, USE.NAMES = FALSE)
Expand All @@ -259,7 +262,8 @@ Logger <- R6::R6Class(
level = level,
timestamp = timestamp,
caller = caller,
msg = msg
msg = msg,
rawMsg = rawMsg
),
dots[!not_named]
)
Expand Down Expand Up @@ -882,6 +886,11 @@ LoggerGlue <- R6::R6Class(
"Can only utilize vectorized logging if log level is the same for all entries"
)

assert(
!missing(...),
"No log message or structured logging fields supplied"
)

dots <- list(...)

if ("msg" %in% names(dots)){
Expand Down Expand Up @@ -909,6 +918,7 @@ LoggerGlue <- R6::R6Class(
}
})

rawMsg <- dots[[1]]
msg <- do.call(glue::glue, args = c(dots_msg, list(.envir = .envir)))

# Check if LogEvent should be created
Expand All @@ -921,22 +931,21 @@ LoggerGlue <- R6::R6Class(

force(caller)

if (missing(...)){
stop("No log message or structured logging fields supplied")
} else {
custom_fields <- !(grepl("^\\.", names(dots)) | is_blank(names(dots)))
# Create list that contains all values equired for the log event
custom_fields <- !(grepl("^\\.", names(dots)) | is_blank(names(dots)))

vals <- c(
list(
logger = self,
level = level,
timestamp = timestamp,
caller = caller,
msg = msg,
rawMsg = rawMsg
),
dots[custom_fields]
)

vals <- c(
list(
logger = self,
level = level,
timestamp = timestamp,
caller = caller,
msg = msg
),
dots[custom_fields]
)
}

# This code looks really weird, but it really is just replacing all
# instances of [[ with get() for minimal overhead. We want event
Expand Down
21 changes: 12 additions & 9 deletions R/basic_config.R
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,20 @@ basic_config <- function(
if (!is.null(file)){
ext <- tools::file_ext(file)

if (identical(tolower(ext), "json")){
stop(
"Please use `.jsonl` and not `.json` as file extension for JSON log",
"files. The reason is that that JSON files created",
"by lgr are not true JSON files but JSONlines files.",
"See https://jsonlines.org/ for more infos."
)
if (tolower(ext) %in% c("jsonl", "json")){

if (identical(tolower(ext), "json")){
warning(
"Please use `.jsonl` and not `.json` as file extension for JSON log ",
"files. The reason is that that JSON files created ",
"by lgr are not true JSON files but JSONlines files. ",
"See https://jsonlines.org/ for more infos."
)
}

} else if (identical(tolower(ext), "jsonl")){
if (!is.null(fmt) && !identical(fmt, default_fmt))
if (!is.null(fmt) && !identical(fmt, default_fmt)){
warning("`fmt` is ignored if `file` is a '.jsonl' file")
}

l$add_appender(
name = "file",
Expand Down
64 changes: 25 additions & 39 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ basic_config(console_connection = stdout()) # ensure default config
# lgr

[![CRAN status](https://www.r-pkg.org/badges/version/lgr)](https://cran.r-project.org/package=lgr)
[![Lifecycle: maturing](https://img.shields.io/badge/lifecycle-maturing-blue.svg)](https://lifecycle.r-lib.org/articles/stages.html)
[![Lifecycle: maturing](https://img.shields.io/badge/lifecycle-stable-green.svg)](https://lifecycle.r-lib.org/articles/stages.html)

lgr is a logging package for R built on the back of
[R6](https://github.com/r-lib/R6) classes. It is designed to be flexible,
Expand Down Expand Up @@ -144,14 +144,25 @@ file.remove(logfile)

## Development status

lgr in general is stable and safe for use, but **the following features are still experimental**:
lgr is stable and safe for use. I've been using it in production code for
several years myself. There has been very little recent development because
it's pretty stable and contains (nearly) all planned features.

* Database appenders which are available from the separate package
[lgrExtra](https://github.com/s-fleck/lgrExtra).
* yaml/json config files for loggers (do not yet support all planned features)
* The documentation in general. I'm still hoping for more R6-specific features
in [roxygen2](https://github.com/r-lib/roxygen2) before I invest more
time in object documentation.
Notable points that are not

* Support for config files is heavily experimental and incomplete.
This is an important basic feature, but I have not yet found a great way to
support this in a generic way. For now, I recommend you come up with your own
solution if you need to lgr to work in a production environment that relies
on config files.

* The documentation should be mostly complete, but is not perfect. If there's
something missing or something you don't understand - please ask (for example
via a github issue).

* Please also check out the lgrExtra package for a variety of extra appenders
[lgrExtra](https://github.com/s-fleck/lgrExtra) (that might not be as stable
as the main lgr package itself).


## Dependencies
Expand All @@ -169,7 +180,6 @@ lgr comes with a long list of optional dependencies that make a wide range of
appenders possible. You only need the dependencies for the Appenders you
actually want to use. Care was taken to choose packages that are slim, stable,
have minimal dependencies, and are well maintained :


Extra appenders (in the main package):

Expand All @@ -186,38 +196,16 @@ have minimal dependencies, and are well maintained :
- [glue](https://glue.tidyverse.org/) for a more flexible formatting syntax
via LoggerGlue and LayoutGlue.

Extra appenders via [lgrExtra](https://github.com/s-fleck/lgrExtra):

- [DBI](https://github.com/r-dbi/DBI) for logging to databases. lgr is
confirmed to work with the following backends:
- [RSQLite](https://github.com/r-dbi/RSQLite),
- [RMariaDB](https://github.com/r-dbi/RMariaDB) for MariaDB and MySQL,
- [RPostgres](https://cran.r-project.org/package=RPostgres),
- [RJDBC](https://github.com/s-u/RJDBC) for DB2, and
- [odbc](https://github.com/r-dbi/odbc) also for DB2.

In theory all DBI compliant database packages should work. If you
are using lgr with a database backend, please report your (positive and
negative) experiences, as database support is still somewhat experimental.

- [gmailr](https://cran.r-project.org/package=gmailr) or

- [sendmailR](https://cran.r-project.org/package=sendmailR) for email
notifications.

- [RPushbullet](https://github.com/eddelbuettel/rpushbullet) for push
notifications.

- [Rsyslog](https://cran.r-project.org/package=rsyslog) for logging to
syslog on POSIX-compatible systems.

- [elastic](https://cran.r-project.org/package=elastic) for logging
to ElasticSearch
Extra appenders via lgrExtra:

- For support for Elasticsearch, Dynatrace, Push- and Email notifications,
etc... as well as the relevant dependencies please refer to the
documentation of [lgrExtra](https://github.com/s-fleck/lgrExtra)

Other extra features:

- [yaml](https://CRAN.R-project.org/package=yaml) for configuring loggers
via YAML files
via YAML files (experimental)
- [crayon](https://github.com/r-lib/crayon) for colored console output.
- [whoami](https://github.com/r-lib/whoami/blob/master/DESCRIPTION) for
guessing the user name from various sources. You can also set the user name
Expand Down Expand Up @@ -257,8 +245,6 @@ features/appenders that you'd like to see, please feel free to post a feature
request on the issue tracker.




## Acknowledgement

* [diagrams.net](https://app.diagrams.net/) for the flow chart in the vignette
6 changes: 5 additions & 1 deletion man/LogEvent.Rd

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

Loading
Loading