#' Collect useful information about the logging environment to be used in log messages
#'
#' Available variables to be used in the log formatter functions, eg in \code{\link{layout_glue_generator}}:
#' \itemize{
#' \item levelr: log level as an R object, eg \code{\link{INFO}}
#' \item level: log level as a string, eg \code{\link{INFO}}
#' \item time: current time as \code{POSIXct}
#' \item node: name by which the machine is known on the network as reported by \code{Sys.info}
#' \item arch: machine type, typically the CPU architecture
#' \item os_name: Operating System's name
#' \item os_release: Operating System's release
#' \item os_version: Operating System's version
#' \item user: name of the real user id as reported by \code{Sys.info}
#' \item pid: the process identification number of the R session
#' \item node: name by which the machine is known on the network as reported by \code{Sys.info}
#' \item r_version: R's major and minor version as a string
#' \item ns: namespace usually defaults to \code{global} or the name of the holding R package of the calling the logging function
#' \item ns_pkg_version: the version of \code{ns} when it's a package
#' \item ans: same as \code{ns} if there's a defined \code{\link{logger}} for the namespace, otherwise a fallback namespace (eg usually \code{global})
#' \item topenv: the name of the top environment from which the parent call was called (eg R package name or \code{GlobalEnv})
#' \item call: parent call (if any) calling the logging function
#' \item fn: function's (if any) name calling the logging function
#' }
#' @param log_level log level as per \code{\link{log_levels}}
#' @inheritParams log_level
#' @return list
#' @export
#' @importFrom utils packageVersion
#' @seealso \code{\link{layout_glue_generator}}
get_logger_meta_variables <- function(log_level = NULL, namespace = NA_character_,
.logcall = sys.call(), .topcall = sys.call(-1), .topenv = parent.frame()) {
sysinfo <- Sys.info()
timestamp <- Sys.time()
list(
ns = namespace,
ans = fallback_namespace(namespace),
topenv = top_env_name(.topenv),
fn = deparse_to_one_line(.topcall[[1]]),
call = deparse_to_one_line(.topcall),
time = timestamp,
levelr = log_level,
level = attr(log_level, 'level'),
pid = Sys.getpid(),
## R and ns package versions
r_version = paste0(R.Version()[c('major', 'minor')], collapse = '.'),
ns_pkg_version = tryCatch(as.character(packageVersion(namespace)), error = function(e) NA_character_),
## stuff from Sys.info
node = sysinfo[['nodename']],
arch = sysinfo[['machine']],
os_name = sysinfo[['sysname']],
os_release = sysinfo[['release']],
os_version = sysinfo[['version']],
user = sysinfo[['user']]
## NOTE might be better to rely on the whoami pkg?
## TODO jenkins (or any) env vars => no need to get here, users can write custom layouts
## TODO seed
)
}
#' Generate log layout function using common variables available via glue syntax
#'
#' \code{format} is passed to \code{glue} with access to the below variables:
#' \itemize{
#' \item msg: the actual log message
#' \item further variables set by \code{\link{get_logger_meta_variables}}
#' }
#' @param format \code{glue}-flavored layout of the message using the above variables
#' @return function taking \code{level} and \code{msg} arguments - keeping the original call creating the generator in the \code{generator} attribute that is returned when calling \code{\link{log_layout}} for the currently used layout
#' @export
#' @examples \dontrun{
#' example_layout <- layout_glue_generator(
#' format = '{node}/{pid}/{ns}/{ans}/{topenv}/{fn} {time} {level}: {msg}')
#' example_layout(INFO, 'try {runif(1)}')
#'
#' log_layout(example_layout)
#' log_info('try {runif(1)}')
#' }
#' @seealso See example calls from \code{\link{layout_glue}} and \code{\link{layout_glue_colors}}.
layout_glue_generator <- function(format = '{level} [{format(time, "%Y-%m-%d %H:%M:%S")}] {msg}') {
force(format)
structure(function(level, msg, namespace = NA_character_,
.logcall = sys.call(), .topcall = sys.call(-1), .topenv = parent.frame()) {
fail_on_missing_package('glue')
if (!inherits(level, 'loglevel')) {
stop('Invalid log level, see ?log_levels')
}
with(get_logger_meta_variables(
log_level = level, namespace = namespace,
.logcall = .logcall, .topcall = .topcall, .topenv = .topenv),
glue::glue(format))
}, generator = deparse(match.call()))
}
#' Format a log record by including the raw message without anything added or modified
#' @inheritParams log_level
#' @param msg string message
#' @return character vector
#' @export
#' @seealso This is a \code{\link{log_layout}}, for alternatives, see \code{\link{layout_simple}}, \code{\link{layout_glue_colors}}, \code{\link{layout_json}}, or generator functions such as \code{\link{layout_glue_generator}}
layout_blank <- structure(function(level, msg, namespace = NA_character_,
.logcall = sys.call(), .topcall = sys.call(-1), .topenv = parent.frame()) {
msg
}, generator = quote(layout_blank()))
#' Format a log record by concatenating the log level, timestamp and message
#' @inheritParams log_level
#' @param msg string message
#' @return character vector
#' @export
#' @seealso This is a \code{\link{log_layout}}, for alternatives, see \code{\link{layout_blank}}, \code{\link{layout_glue}}, \code{\link{layout_glue_colors}}, \code{\link{layout_json}}, \code{\link{layout_json_parser}}, or generator functions such as \code{\link{layout_glue_generator}}
layout_simple <- structure(function(level, msg, namespace = NA_character_,
.logcall = sys.call(), .topcall = sys.call(-1), .topenv = parent.frame()) {
paste0(attr(level, 'level'), ' [', format(Sys.time(), "%Y-%m-%d %H:%M:%S"), '] ', msg)
}, generator = quote(layout_simple()))
#' Format a log record as the logging package does by default
#' @inheritParams layout_simple
#' @param msg string message
#' @return character vector
#' @export
#' @seealso This is a \code{\link{log_layout}}, for alternatives, see \code{\link{layout_blank}}, \code{\link{layout_glue}}, \code{\link{layout_glue_colors}}, \code{\link{layout_json}}, \code{\link{layout_json_parser}}, or generator functions such as \code{\link{layout_glue_generator}}
#' @examples \dontrun{
#' log_layout(layout_logging)
#' log_info(42)
#' log_info(42, namespace = 'everything')
#'
#' devtools::load_all(system.file('demo-packages/logger-tester-package', package = 'logger'))
#' logger_tester_function(INFO, 42)
#' }
layout_logging <- structure(function(level, msg, namespace = NA_character_,
.logcall = sys.call(), .topcall = sys.call(-1), .topenv = parent.frame()) {
meta <- get_logger_meta_variables(
log_level = level, namespace = namespace,
.logcall = .logcall, .topcall = .topcall, .topenv = .topenv)
paste0(format(Sys.time(), "%Y-%m-%d %H:%M:%S"), ' ',
attr(level, 'level'), ':',
ifelse(meta$ns == 'global', '', meta$ns), ':',
msg)
}, generator = quote(layout_logging()))
#' Format a log message with \code{glue}
#'
#' By default, this layout includes the log level of the log record as per \code{\link{log_levels}}, the current timestamp and the actual log message -- that you can override via calling \code{\link{layout_glue_generator}} directly. For colorized output, see \code{\link{layout_glue_colors}}.
#' @inheritParams layout_simple
#' @return character vector
#' @export
#' @seealso This is a \code{\link{log_layout}}, for alternatives, see \code{\link{layout_blank}}, \code{\link{layout_simple}}, \code{\link{layout_glue_colors}}, \code{\link{layout_json}}, \code{\link{layout_json_parser}}, or generator functions such as \code{\link{layout_glue_generator}}
layout_glue <- layout_glue_generator()
#' Format a log message with \code{glue} and ANSI escape codes to add colors
#' @inheritParams layout_simple
#' @return character vector
#' @export
#' @examples \dontrun{
#' 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.')
#' }
#' @seealso This is a \code{\link{log_layout}}, for alternatives, see \code{\link{layout_blank}}, \code{\link{layout_simple}}, \code{\link{layout_glue}}, \code{\link{layout_json}}, \code{\link{layout_json_parser}}, or generator functions such as \code{\link{layout_glue_generator}}
#' @note This functionality depends on the \pkg{crayon} package.
layout_glue_colors <- layout_glue_generator(
format = paste(
'{crayon::bold(colorize_by_log_level(level, levelr))}',
'[{crayon::italic(format(time, "%Y-%m-%d %H:%M:%S"))}]',
'{grayscale_by_log_level(msg, levelr)}'))
#' Generate log layout function rendering JSON
#' @param fields character vector of field names to be included in the JSON
#' @return character vector
#' @export
#' @examples \dontrun{
#' log_layout(layout_json())
#' log_info(42)
#' log_info('ok {1:3} + {1:3} = {2*(1:3)}')
#' }
#' @note This functionality depends on the \pkg{jsonlite} package.
#' @seealso This is a \code{\link{log_layout}}, for alternatives, see \code{\link{layout_blank}}, \code{\link{layout_simple}}, \code{\link{layout_glue}}, \code{\link{layout_glue_colors}}, \code{\link{layout_json_parser}}, or generator functions such as \code{\link{layout_glue_generator}}
layout_json <- function(fields = c('time', 'level', 'ns', 'ans', 'topenv', 'fn', 'node', 'arch', 'os_name', 'os_release', 'os_version', 'pid', 'user', 'msg')) {
force(fields)
structure(function(level, msg, namespace = NA_character_,
.logcall = sys.call(), .topcall = sys.call(-1), .topenv = parent.frame()) {
fail_on_missing_package('jsonlite')
json <- get_logger_meta_variables(
log_level = level, namespace = namespace,
.logcall = .logcall, .topcall = .topcall, .topenv = .topenv)
sapply(msg, function(msg) jsonlite::toJSON(c(json, list(msg = msg))[fields], auto_unbox = TRUE))
}, generator = deparse(match.call()))
}
#' Generate log layout function rendering JSON after merging meta fields with parsed list from JSON message
#' @param fields character vector of field names to be included in the JSON
#' @export
#' @examples \dontrun{
#' log_formatter(formatter_json)
#' log_info(everything = 42)
#' log_layout(layout_json_parser())
#' log_info(everything = 42)
#' log_layout(layout_json_parser(fields = c('time', 'node')))
#' log_info(cars = row.names(mtcars), species = unique(iris$Species))
#' }
#' @note This functionality depends on the \pkg{jsonlite} package.
#' @seealso This is a \code{\link{log_layout}} potentially to be used with \code{\link{formatter_json}}, for alternatives, see \code{\link{layout_simple}}, \code{\link{layout_glue}}, \code{\link{layout_glue_colors}}, \code{\link{layout_json}} or generator functions such as \code{\link{layout_glue_generator}}
layout_json_parser <- function(fields = c('time', 'level', 'ns', 'ans', 'topenv', 'fn', 'node', 'arch', 'os_name', 'os_release', 'os_version', 'pid', 'user')) {
force(fields)
structure(function(level, msg, namespace = NA_character_,
.logcall = sys.call(), .topcall = sys.call(-1), .topenv = parent.frame()) {
fail_on_missing_package('jsonlite')
meta <- get_logger_meta_variables(
log_level = level, namespace = namespace,
.logcall = .logcall, .topcall = .topcall, .topenv = .topenv)[fields]
msg <- jsonlite::fromJSON(msg)
jsonlite::toJSON(c(meta, msg), auto_unbox = TRUE, null = 'null')
}, generator = deparse(match.call()))
}
#nocov start
#' Format a log record for syslognet
#'
#' Format a log record for syslognet.
#' This function converts the logger log level to a
#' log severity level according to RFC 5424 "The Syslog Protocol".
#'
#' @inheritParams layout_simple
#' @return A character vector with a severity attribute.
#' @export
layout_syslognet <- structure(
function(level, msg, namespace = NA_character_,
.logcall = sys.call(), .topcall = sys.call(-1), .topenv = parent.frame()) {
ret <- paste(attr(level, 'level'), msg)
attr(ret, 'severity') <- switch(
attr(level, 'level', exact = TRUE),
'FATAL' = 'CRITICAL',
'ERROR' = 'ERR',
'WARN' = 'WARNING',
'SUCCESS' = 'NOTICE',
'INFO' = 'INFO',
'DEBUG' = 'DEBUG',
'TRACE' = 'DEBUG')
return(ret)
},
generator = quote(layout_syslognet())
)
#nocov end
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.