futile.logger/0000755000176200001440000000000012740461153013023 5ustar liggesusersfutile.logger/tests/0000755000176200001440000000000012740450476014173 5ustar liggesusersfutile.logger/tests/testthat.R0000644000176200001440000000010512740450476016152 0ustar liggesuserslibrary(testthat) library(futile.logger) test_check("futile.logger") futile.logger/tests/testthat/0000755000176200001440000000000012740461153016025 5ustar liggesusersfutile.logger/tests/testthat/test_layout.R0000644000176200001440000000402612740450476020534 0ustar liggesusers# :vim set ff=R context("format string") test_that("Embedded format string", { flog.threshold(INFO) raw <- capture.output(flog.info("This is a %s message", "log")) #cat("\n[test.default] Raw:",raw,"\n") expect_that(length(grep('INFO', raw)) > 0, is_true()) expect_that(length(grep('log message', raw)) > 0, is_true()) }) test_that("Custom layout dereferences level field", { flog.threshold(INFO) flog.layout(layout.format('xxx[~l]xxx')) raw <- capture.output(flog.info("log message")) flog.layout(layout.simple) expect_that('xxx[INFO]xxx' == raw, is_true()) expect_that(length(grep('log message', raw)) == 0, is_true()) }) context("null values") test_that("Raw null value is printed", { raw <- capture.output(flog.info('xxx[%s]xxx', NULL)) expect_that(length(grep('xxx[NULL]xxx', raw, fixed=TRUE)) == 1, is_true()) }) test_that("Single null value is printed", { opt <- list() raw <- capture.output(flog.info('xxx[%s]xxx', opt$noexist)) expect_that(length(grep('xxx[NULL]xxx', raw, fixed=TRUE)) == 1, is_true()) }) test_that("Null is printed amongst variables", { opt <- list() raw <- capture.output(flog.info('aaa[%s]aaa xxx[%s]xxx', 3, opt$noexist)) expect_that(length(grep('aaa[3]aaa', raw, fixed=TRUE)) == 1, is_true()) expect_that(length(grep('xxx[NULL]xxx', raw, fixed=TRUE)) == 1, is_true()) }) context("sprintf arguments") test_that("no extra arguments are passed", { expect_true(grepl('foobar$', capture.output(flog.info('foobar')))) expect_true(grepl('foobar %s$', capture.output(flog.info('foobar %s')))) expect_true(grepl('10%$', capture.output(flog.info('10%')))) }) test_that("some extra arguments are passed", { expect_true(grepl('foobar$', capture.output(flog.info('foobar', pi)))) expect_true(grepl( 'foobar foo$', capture.output(flog.info('foobar %s', 'foo')))) expect_true(grepl('100$', capture.output(flog.info('10%d', 0)))) expect_true( grepl('foo and bar equals to foobar', capture.output( flog.info('%s and %s equals to %s', 'foo', 'bar', 'foobar')))) }) futile.logger/tests/testthat/test_stringconfig.R0000644000176200001440000000273712740450476021722 0ustar liggesuserscontext("String configuration") test_that("trace threshold is set via string", { flog.threshold("trace") expect_equal(flog.threshold(), "TRACE") expect_equivalent(flog.logger()$threshold, 9) flog.threshold("TRACE") expect_equal(flog.threshold(), "TRACE") }) test_that("debug threshold is set via string", { flog.threshold("debug") expect_equal(flog.threshold(), "DEBUG") expect_equivalent(flog.logger()$threshold, 8) flog.threshold("DEBUG") expect_equal(flog.threshold(), "DEBUG") }) test_that("info threshold is set via string", { flog.threshold("info") expect_equal(flog.threshold(), "INFO") expect_equivalent(flog.logger()$threshold, 6) flog.threshold("INFO") expect_equal(flog.threshold(), "INFO") }) test_that("warn threshold is set via string", { flog.threshold("warn") expect_equal(flog.threshold(), "WARN") expect_equivalent(flog.logger()$threshold, 4) flog.threshold("WARN") expect_equal(flog.threshold(), "WARN") }) test_that("error threshold is set via string", { flog.threshold("error") expect_equal(flog.threshold(), "ERROR") expect_equivalent(flog.logger()$threshold, 2) flog.threshold("ERROR") expect_equal(flog.threshold(), "ERROR") }) test_that("fatal threshold is set via string", { flog.threshold("fatal") expect_equal(flog.threshold(), "FATAL") expect_equivalent(flog.logger()$threshold, 1) flog.threshold("FATAL") expect_equal(flog.threshold(), "FATAL") })futile.logger/tests/testthat/test_debug.R0000644000176200001440000000107512740450476020306 0ustar liggesuserscontext("debug level logging") test_that("debug level is logged", { flog.threshold(DEBUG) expect_output(flog.debug("testlog"), "testlog") }) test_that("higher levels are logged", { testlog = paste0("testlog ", sample(100, 1)) flog.threshold(DEBUG) expect_output(flog.info(testlog), testlog) expect_output(flog.warn(testlog), testlog) expect_output(flog.error(testlog), testlog) expect_output(flog.fatal(testlog), testlog) }) test_that("lower levels are not logged", { flog.threshold(DEBUG) expect_silent(flog.trace("testlog")) }) futile.logger/tests/testthat/test_json.R0000644000176200001440000000226712740450476020175 0ustar liggesusersif (requireNamespace("jsonlite", quietly=TRUE)) { context("JSON: typical usage") flog.threshold(INFO) flog.layout(layout.json) test_that("simple string", { raw <- capture.output(flog.info("log message")) aslist <- jsonlite::fromJSON(raw) expect_equal(aslist$level, "INFO") expect_equal(aslist$message, "log message") ts <- strptime(aslist$timestamp, "%Y-%m-%d %H:%M:%S %z") expect_true('POSIXt' %in% class(ts)) }) test_that("additional objects", { raw <- capture.output( flog.info("log message", pet="hamster", weight=12, stuff=c("a", "b"))) aslist <- jsonlite::fromJSON(raw) expect_equal(aslist$level, "INFO") expect_equal(aslist$message, "log message") expect_equal(aslist$pet, "hamster") expect_equal(aslist$weight, 12) expect_equal(aslist$stuff, c("a", "b")) }) context("JSON: NULL values") #test_that("NULL message", { # raw <- capture.output(flog.info(NULL)) # aslist <- fromJSON(raw) # expect_equal(length(aslist$message), 0) #}) test_that("NULL additional objects", { raw <- capture.output(flog.info("log message", nullthing=NULL)) aslist <- jsonlite::fromJSON(raw) expect_equal(length(aslist$nullthing), 0) }) # knockdown flog.layout(layout.simple) } futile.logger/tests/testthat/test_logger.R0000644000176200001440000000575412740450476020507 0ustar liggesuserscontext("root logger") test_that("Default settings", { flog.threshold(INFO) raw <- capture.output(flog.info("log message")) #cat("\n[test.default] Raw:",raw,"\n") expect_that(length(grep('INFO', raw)) > 0, is_true()) expect_that(length(grep('log message', raw)) > 0, is_true()) }) test_that("Change root threshold", { flog.threshold(ERROR) raw <- capture.output(flog.info("log message")) #cat("\n[test.change_threshold] Raw:",raw,"\n") expect_that(length(raw) == 0, is_true()) }) context("capture output") test_that("Capture works as expected", { flog.threshold(INFO) raw <- capture.output(flog.info("log message", head(cars), capture = TRUE)) expect_that(length(raw) == 9, is_true()) expect_that(grepl('^INFO',raw[1]), is_true()) expect_that(nchar(raw[2]) == 0, is_true()) expect_that(grepl('dist$',raw[3]), is_true()) }) context("new logger") test_that("Create new logger", { flog.threshold(ERROR) flog.threshold(DEBUG, name='my.package') raw.root <- capture.output(flog.info("log message")) raw.mine <- capture.output(flog.info("log message", name='my.package')) expect_that(length(raw.root) == 0, is_true()) expect_that(length(grep('INFO', raw.mine)) > 0, is_true()) expect_that(length(grep('log message', raw.mine)) > 0, is_true()) }) context("logger hierarchy") test_that("Hierarchy is honored", { flog.threshold(ERROR) flog.threshold(TRACE, name='my') flog.threshold(DEBUG, name='my.package') raw.root <- capture.output(flog.info("log message")) raw.l1 <- capture.output(flog.trace("log message", name='my')) raw.l2 <- capture.output(flog.trace("log message", name='my.package')) expect_that(length(raw.root) == 0, is_true()) expect_that(length(grep('TRACE', raw.l1)) > 0, is_true()) expect_that(length(grep('log message', raw.l1)) > 0, is_true()) expect_that(length(raw.l2) == 0, is_true()) }) test_that("Hierarchy inheritance", { flog.remove('my.package') flog.remove('my') flog.threshold(ERROR) flog.threshold(TRACE, name='my') raw.root <- capture.output(flog.info("log message")) raw.l1 <- capture.output(flog.trace("log message", name='my')) raw.l2 <- capture.output(flog.trace("log message", name='my.package')) expect_that(length(raw.root) == 0, is_true()) expect_that(length(grep('TRACE', raw.l1)) > 0, is_true()) expect_that(length(grep('log message', raw.l1)) > 0, is_true()) expect_that(length(grep('TRACE', raw.l2)) > 0, is_true()) expect_that(length(grep('log message', raw.l2)) > 0, is_true()) }) # Can't test this since test_that calls suppressMessages #context("package loggers") #test_that("ftry captures warnings", { #raw <- capture.output(ftry(log(-1))) #cat("raw =",raw,'\n') #expect_that(length(grep('WARN', raw)) > 0, is_true()) #expect_that(length(grep('simpleWarning', raw)) > 0, is_true()) #}) context("carp") test_that("carp returns output", { expect_that(flog.carp(), is_false()) flog.carp(TRUE) flog.threshold(WARN) raw <- flog.debug("foo") expect_that(length(grep('DEBUG', raw)) > 0, is_true()) }) futile.logger/NAMESPACE0000644000176200001440000000017512740450476014253 0ustar liggesusers# Generated by roxygen2: do not edit by hand exportPattern("^[^\\.]") import(futile.options) import(lambda.r) import(utils) futile.logger/R/0000755000176200001440000000000012740450476013232 5ustar liggesusersfutile.logger/R/logger.R0000644000176200001440000003062612740450476014643 0ustar liggesusers#' Manage loggers #' #' Provides functions for writing log messages and managing loggers. Typically #' only the flog.[trace|debug|info|warn|error|fatal] functions need to be used #' in conjunction with flog.threshold to interactively change the log level. #' #' @section Usage: #' # Conditionally print a log statement at TRACE log level\cr #' flog.trace(msg, ..., name=flog.namespace(), capture=FALSE) #' #' # Conditionally print a log statement at DEBUG log level\cr #' flog.debug(msg, ..., name=flog.namespace(), capture=FALSE) #' #' # Conditionally print a log statement at INFO log level\cr #' flog.info(msg, ..., name=flog.namespace(), capture=FALSE) #' #' # Conditionally print a log statement at WARN log level\cr #' flog.warn(msg, ..., name=flog.namespace(), capture=FALSE) #' #' # Conditionally print a log statement at ERROR log level\cr #' flog.error(msg, ..., name=flog.namespace(), capture=FALSE) #' #' # Print a log statement at FATAL log level\cr #' flog.fatal(msg, ..., name=flog.namespace(), capture=FALSE) #' #' # Execute an expression and capture any warnings or errors #' ftry(expr, error=stop, finally=NULL) #' #' @section Additional Usage: #' These functions generally do not need to be called by an end user. #' #' # Get the ROOT logger\cr #' flog.logger() #' #' # Get the logger with the specified name\cr #' flog.logger(name) #' #' # Set options for the given logger\cr #' flog.logger(name, threshold=NULL, appender=NULL, layout=NULL, carp=NULL) #' #' @section Details: #' These functions represent the high level interface to futile.logger. #' #' The primary use case for futile.logger is to write out log messages. There #' are log writers associated with all the predefined log levels: TRACE, DEBUG, #' INFO, WARN, ERROR, FATAL. Log messages will only be written if the log level #' is equal to or more urgent than the current threshold. By default the ROOT #' logger is set to INFO. #' #' > flog.debug("This won't print") \cr #' > flog.info("But this \%s", 'will') \cr #' > flog.warn("As will \%s", 'this') #' #' Typically, the built in log level constants are used in the call, which #' conform to the log4j levels (from least severe to most severe): TRACE, #' DEBUG, INFO, WARN, ERROR, FATAL. It is not a strict requirement to use these #' constants (any numeric value will work), though most users should find this #' level of granularity sufficient. #' #' Loggers are hierarchical in the sense that any requested logger that is #' undefined will fall back to its most immediate defined parent logger. The #' absolute parent is ROOT, which is guaranteed to be defined for the system #' and cannot be deleted. This means that you can specify a new logger #' directly. #' #' > flog.info("This will fall back to 'my', then 'ROOT'", name='my.logger') #' #' You can also change the threshold or any other setting associated with a #' logger. This will create an explicit logger where any unspecified options #' are copied from the parent logger. #' #' > flog.appender(appender.file("foo.log"), name='my') \cr #' > flog.threshold(ERROR, name='my.logger') \cr #' > flog.info("This won't print", name='my.logger') \cr #' > flog.error("This %s print to a file", 'will', name='my.logger') \cr #' #' If you define a logger that you later want to remove, use flog.remove. #' #' The option 'capture' allows you to print out more complicated data #' structures without a lot of ceremony. This variant doesn't accept format #' strings and instead appends the value to the next line of output. Consider #' #' > m <- matrix(rnorm(12), nrow=3) \cr #' > flog.info("Matrix:",m, capture=TRUE) #' #' which preserves the formatting, whereas using capture=FALSE will have #' a cluttered output due to recycling. #' #' @name flog.logger #' @aliases flog.trace flog.debug flog.info flog.warn flog.error flog.fatal #' @param msg The message to log #' @param name The logger name to use #' @param capture Capture print output of variables instead of interpolate #' @param \dots Optional arguments to populate the format string #' @param expr An expression to evaluate #' @param finally An optional expression to evaluate at the end #' @author Brian Lee Yung Rowe #' @seealso \code{\link{flog.threshold}} \code{\link{flog.remove}} #' \code{\link{flog.carp}} \code{\link{flog.appender}} \code{\link{flog.layout}} #' @keywords data #' @examples #' #' flog.threshold(DEBUG) #' flog.debug("This debug message will print") #' #' flog.threshold(WARN) #' flog.debug("This one won't") #' #' m <- matrix(rnorm(12), nrow=3) #' flog.info("Matrix:",m, capture=TRUE) #' #' ftry(log(-1)) #' #' \dontrun{ #' s <- c('FCX','AAPL','JPM','AMZN') #' p <- TawnyPortfolio(s) #' #' flog.threshold(TRACE,'tawny') #' ws <- optimizePortfolio(p, RandomMatrixDenoiser()) #' z <- getIndexComposition() #' #' flog.threshold(WARN,'tawny') #' ws <- optimizePortfolio(p, RandomMatrixDenoiser()) #' z <- getIndexComposition() #' #' } NULL .log_level <- function(msg, ..., level, name, capture) { logger <- flog.logger(name) if (level > logger$threshold && (is.null(logger$carp) || !logger$carp)) { return(invisible()) } appender <- flog.appender(name) layout <- flog.layout(name) if (capture) { values <- paste(capture.output(print(...)), collapse='\n') message <- c(layout(level, msg), "\n", values, "\n") } else { message <- layout(level, msg, ...) } if (level <= logger$threshold) appender(message) invisible(message) } # Get the namespace that a function resides in. If no namespace exists, then # return NULL. # flog.namespace <- function(where=1) { s <- capture.output(str(topenv(environment(sys.function(where))), give.attr=FALSE)) if (length(grep('lambda.r',s)) > 0) s <- attr(sys.function(-5), 'topenv') if (length(grep('namespace', s)) < 1) return('ROOT') ns <- sub('.*namespace:([^>]+)>.*','\\1', s) ifelse(is.null(ns), 'ROOT', ns) } flog.trace <- function(msg, ..., name=flog.namespace(), capture=FALSE) { .log_level(msg, ..., level=TRACE,name=name, capture=capture) } flog.debug <- function(msg, ..., name=flog.namespace(), capture=FALSE) { .log_level(msg, ..., level=DEBUG,name=name, capture=capture) } flog.info <- function(msg, ..., name=flog.namespace(), capture=FALSE) { .log_level(msg, ..., level=INFO,name=name, capture=capture) } flog.warn <- function(msg, ..., name=flog.namespace(), capture=FALSE) { .log_level(msg, ..., level=WARN,name=name, capture=capture) } flog.error <- function(msg, ..., name=flog.namespace(), capture=FALSE) { .log_level(msg, ..., level=ERROR,name=name, capture=capture) } flog.fatal <- function(msg, ..., name=flog.namespace(), capture=FALSE) { .log_level(msg, ..., level=FATAL,name=name, capture=capture) } #' Wrap a try block in futile.logger #' #' This function integrates futile.logger with the error and warning system #' so problems can be caught both in the standard R warning system, while #' also being emitted via futile.logger. #' #' @name ftry #' @param expr The expression to evaluate in a try block #' @param error An error handler #' @param finally Pass-through to tryCatch finally #' @author Brian Lee Yung Rowe #' @keywords data #' @examples #' ftry(log(-1)) ftry <- function(expr, error=stop, finally=NULL) { w.handler <- function(e) flog.warn("%s", e) e.handler <- function(e) { flog.error("%s", e); error(e) } tryCatch(expr, warning=w.handler, error=e.handler, finally) } # By default, use the package namespace or use the 'ROOT' logger. flog.logger() %as% { flog.logger(flog.namespace()) } flog.logger(name) %as% { if (nchar(name) < 1) name <- 'ROOT' #cat(sprintf("Searching for logger %s\n", name)) key <- paste("logger", name, sep='.') # TODO: Search hierarchy os <- logger.options(key) if (! is.null(os)) return(os) if (name == 'ROOT') { logger <- list(name=name, threshold=INFO, appender=appender.console(), layout=layout.simple) logger.options(update=list(key, logger)) return(logger) } parts <- strsplit(name, '.', fixed=TRUE)[[1]] parent <- paste(parts[1:length(parts)-1], collapse='.') flog.logger(parent) } flog.logger(name, threshold=NULL, appender=NULL, layout=NULL, carp=NULL) %as% { logger <- flog.logger(name) if (!is.null(threshold)) logger$threshold <- threshold if (!is.null(appender)) logger$appender <- appender if (!is.null(layout)) logger$layout <- layout if (!is.null(carp)) logger$carp <- carp key <- paste("logger", name, sep='.') logger.options(update=list(key, logger)) invisible() } #' Remove a logger #' #' In the event that you no longer wish to have a logger registered, #' use this function to remove it. Then any references to this #' logger will inherit the next available logger in the hierarchy. #' #' @section Usage: #' # Remove a logger\cr #' flog.remove(name) #' #' @name flog.remove #' @param name The logger name to use #' @author Brian Lee Yung Rowe #' @keywords data #' @examples #' flog.threshold(ERROR, name='my.logger') #' flog.info("Won't print", name='my.logger') #' flog.remove('my.logger') #' flog.info("Will print", name='my.logger') flog.remove('ROOT') %as% { invisible() } flog.remove(name) %as% { key <- paste("logger", name, sep='.') logger.options(update=list(key, NULL)) invisible() } #' Get and set the threshold for a logger #' #' The threshold affects the visibility of a given logger. When a log #' statement is called, e.g. \code{flog.debug('foo')}, futile.logger #' compares the threshold of the logger with the level implied in the #' log command (in this case DEBUG). If the log level is at or higher #' in priority than the logger threshold, a message will print. #' Otherwise the command will silently return. #' #' @section Usage: #' # Get the threshold for the given logger\cr #' flog.threshold(name) \%::\% character : character \cr #' flog.threshold(name=ROOT) #' #' # Set the threshold for the given logger\cr #' flog.threshold(threshold, name=ROOT) #' #' @name flog.threshold #' @param threshold integer The new threshold for the given logger #' @param name character The name of the logger #' @author Brian Lee Yung Rowe #' @keywords data #' @examples #' flog.threshold(ERROR) #' flog.info("Won't print") #' flog.threshold(INFO) #' flog.info("Will print") # Set the threshold flog.threshold('TRACE', name='ROOT') %as% flog.threshold(TRACE, name) flog.threshold('trace', name='ROOT') %as% flog.threshold(TRACE, name) flog.threshold('DEBUG', name='ROOT') %as% flog.threshold(DEBUG, name) flog.threshold('debug', name='ROOT') %as% flog.threshold(DEBUG, name) flog.threshold('INFO', name='ROOT') %as% flog.threshold(INFO, name) flog.threshold('info', name='ROOT') %as% flog.threshold(INFO, name) flog.threshold('WARN', name='ROOT') %as% flog.threshold(WARN, name) flog.threshold('warn', name='ROOT') %as% flog.threshold(WARN, name) flog.threshold('ERROR', name='ROOT') %as% flog.threshold(ERROR, name) flog.threshold('error', name='ROOT') %as% flog.threshold(ERROR, name) flog.threshold('FATAL', name='ROOT') %as% flog.threshold(FATAL, name) flog.threshold('fatal', name='ROOT') %as% flog.threshold(FATAL, name) flog.threshold(threshold, name='ROOT') %as% { flog.logger(name, threshold=threshold) invisible() } # Get the threshold flog.threshold(name) %::% character : character flog.threshold(name='ROOT') %as% { logger <- flog.logger(name) names(logger$threshold) } #' Always return the log message #' #' Indicate whether the logger will always return the log message #' despite the threshold. #' #' This is a special option to allow the return value of the flog.* #' logging functions to return the generated log message even if #' the log level does not exceed the threshold. Note that this #' minorly impacts performance when enabled. This functionality #' is separate from the appender, which is still bound to the #' value of the logger threshold. #' #' @section Usage: #' # Indicate whether the given logger should carp\cr #' flog.carp(name=ROOT) #' #' # Set whether the given logger should carp\cr #' flog.carp(carp, name=ROOT) #' #' @name flog.carp #' @param carp logical Whether to carp output or not #' @param name character The name of the logger #' @author Brian Lee Yung Rowe #' @keywords data #' @examples #' flog.carp(TRUE) #' x <- flog.debug("Returns this message but won't print") #' flog.carp(FALSE) #' y <- flog.debug("Returns nothing and prints nothing") flog.carp(name) %::% character : logical flog.carp(name='ROOT') %as% { logger <- flog.logger(name) if (is.null(logger$carp)) FALSE else logger$carp } # Set whether to carp flog.carp(carp, name='ROOT') %as% { flog.logger(name, carp=carp) invisible() } futile.logger/R/layout.R0000644000176200001440000001415612740450476014701 0ustar liggesusers#' Manage layouts within the 'futile.logger' sub-system #' #' Provides functions for managing layouts. Typically 'flog.layout' is only #' used when manually creating a logging configuration. #' #' @section Usage: #' # Get the layout function for the given logger\cr #' flog.layout(name) \%::\% character : Function\cr #' flog.layout(name='ROOT') #' #' # Set the layout function for the given logger\cr #' flog.layout(fn, name='ROOT') #' #' # Decorate log messages with a standard format\cr #' layout.simple(level, msg, ...) #' #' # Generate log messages as JSON\cr #' layout.json(level, msg, ...) #' #' # Decorate log messages using a custom format\cr #' layout.format(format, datetime.fmt="%Y-%m-%d %H:%M:%S") #' #' # Show the value of a single variable #' layout.tracearg(level, msg, ...) #' #' @section Details: #' Layouts are responsible for formatting messages so they are human-readable. #' Similar to an appender, a layout is assigned to a logger by calling #' \code{flog.layout}. The \code{flog.layout} function is used internally #' to get the registered layout function. It is kept visible so #' user-level introspection is possible. #' #' \code{layout.simple} is a pre-defined layout function that #' prints messages in the following format:\cr #' LEVEL [timestamp] message #' #' This is the default layout for the ROOT logger. #' #' \code{layout.format} allows you to specify the format string to use #' in printing a message. The following tokens are available. #' \describe{ #' \item{~l}{Log level} #' \item{~t}{Timestamp} #' \item{~n}{Namespace} #' \item{~f}{The calling function} #' \item{~m}{The message} #' } #' #' \code{layout.json} converts the message and any additional objects provided #' to a JSON structure. E.g.: #' #' flog.info("Hello, world", cat='asdf') #' #' yields something like #' #' \{"level":"INFO","timestamp":"2015-03-06 19:16:02 EST","message":"Hello, world","func":"(shell)","cat":["asdf"]\} #' #' \code{layout.tracearg} is a special layout that takes a variable #' and prints its name and contents. #' #' @name flog.layout #' @aliases layout.simple layout.format layout.tracearg layout.json #' @param \dots Used internally by lambda.r #' @author Brian Lee Yung Rowe #' @seealso \code{\link{flog.logger}} \code{\link{flog.appender}} #' @keywords data #' @examples #' # Set the layout for 'my.package' #' flog.layout(layout.simple, name='my.package') #' #' # Update the ROOT logger to use a custom layout #' layout <- layout.format('[~l] [~t] [~n.~f] ~m') #' flog.layout(layout) #' #' # Create a custom logger to trace variables #' flog.layout(layout.tracearg, name='tracer') #' x <- 5 #' flog.info(x, name='tracer') NULL # Get the layout for the given logger flog.layout(name) %::% character : Function flog.layout(name='ROOT') %as% { logger <- flog.logger(name) logger$layout } # Set the layout flog.layout(fn, name='ROOT') %as% { flog.logger(name, layout=fn) invisible() } # This file provides some standard formatters # This prints out a string in the following format: # LEVEL [timestamp] message layout.simple <- function(level, msg, ...) { the.time <- format(Sys.time(), "%Y-%m-%d %H:%M:%S") if (length(list(...)) > 0) { parsed <- lapply(list(...), function(x) ifelse(is.null(x), 'NULL', x)) msg <- do.call(sprintf, c(msg, parsed)) } sprintf("%s [%s] %s\n", names(level),the.time, msg) } # Generates a list object, then converts it to JSON and outputs it layout.json <- function(level, msg, ...) { if (!requireNamespace("jsonlite", quietly=TRUE)) stop("layout.json requires jsonlite. Please install it.", call.=FALSE) where <- 1 # to avoid R CMD CHECK issue the.function <- tryCatch(deparse(sys.call(where)[[1]]), error=function(e) "(shell)") the.function <- ifelse( length(grep('flog\\.',the.function)) == 0, the.function, '(shell)') output_list <- list( level=jsonlite::unbox(names(level)), timestamp=jsonlite::unbox(format(Sys.time(), "%Y-%m-%d %H:%M:%S %z")), message=jsonlite::unbox(msg), func=jsonlite::unbox(the.function), additional=... ) jsonlite::toJSON(output_list, simplifyVector=TRUE) } # This parses and prints a user-defined format string. Available tokens are # ~l - Log level # ~t - Timestamp # ~n - Namespace # ~f - Calling function # ~m - Message # # layout <- layout.format('[~l] [~t] [~n.~f] ~m') # flog.layout(layout) layout.format <- function(format, datetime.fmt="%Y-%m-%d %H:%M:%S") { where <- 1 function(level, msg, ...) { if (! is.null(substitute(...))) msg <- sprintf(msg, ...) the.level <- names(level) the.time <- format(Sys.time(), datetime.fmt) the.namespace <- ifelse(flog.namespace() == 'futile.logger','ROOT',flog.namespace()) #print(sys.calls()) the.function <- tryCatch(deparse(sys.call(where)[[1]]), error=function(e) "(shell)") the.function <- ifelse(length(grep('flog\\.',the.function)) == 0, the.function, '(shell)') #pattern <- c('~l','~t','~n','~f','~m') #replace <- c(the.level, the.time, the.namespace, the.function, msg) message <- gsub('~l',the.level, format, fixed=TRUE) message <- gsub('~t',the.time, message, fixed=TRUE) message <- gsub('~n',the.namespace, message, fixed=TRUE) message <- gsub('~f',the.function, message, fixed=TRUE) message <- gsub('~m',msg, message, fixed=TRUE) sprintf("%s\n", message) } } layout.tracearg <- function(level, msg, ...) { the.time <- format(Sys.time(), "%Y-%m-%d %H:%M:%S") if (is.character(msg)) { if (! is.null(substitute(...))) msg <- sprintf(msg, ...) } else { external.call <- sys.call(-2) external.fn <- eval(external.call[[1]]) matched.call <- match.call(external.fn, external.call) matched.call <- matched.call[-1] matched.call.names <- names(matched.call) ## We are interested only in the msg and ... parameters, ## i.e. in msg and all parameters not explicitly declared ## with the function is.output.param <- matched.call.names == "msg" | !(matched.call.names %in% c(setdiff(names(formals(external.fn)), "..."))) label <- lapply(matched.call[is.output.param], deparse) msg <- sprintf("%s: %s", label, c(msg, list(...))) } sprintf("%s [%s] %s\n", names(level),the.time, msg) } futile.logger/R/appender.R0000644000176200001440000000560312740450476015157 0ustar liggesusers#' Manage appenders for loggers #' #' Provides functions for adding and removing appenders. #' #' @section Usage: #' # Get the appender for the given logger\cr #' flog.appender(name) \%::\% character : Function\cr #' flog.appender(name='ROOT') #' #' # Set the appender for the given logger\cr #' flog.appender(fn, name='ROOT') #' #' # Print log messages to the console\cr #' appender.console() #' #' # Write log messages to a file\cr #' appender.file(file) #' #' # Write log messages to console and a file\cr #' appender.tee(file) #' #' #' @section Details: #' Appenders do the actual work of writing log messages to some target. #' To use an appender in a logger, you must register it to a given logger. #' Use \code{flog.appender} to both access and set appenders. #' #' The ROOT logger by default uses \code{appender.console}. #' #' \code{appender.console} is a function that writes to the console. #' No additional arguments are necessary when registering the appender #' via flog.appender. #' #' #' \code{appender.file} writes to a file, so you must pass an additional file #' argument to the function. To change the file name, just call #' \code{flog.appender(appender.file(file))} again with a new file name. #' #' To use your own appender create a function that takes a single argument, #' which represents the log message. You need to pass a function reference to #' \code{flog.appender}. #' #' \code{appender.tee} writes to both the console and file. #' #' @section Value: #' When getting the appender, \code{flog.appender} returns the appender #' function. When setting an appender, \code{flog.appender} has no #' return value. #' #' @name flog.appender #' @aliases appender.console appender.file appender.tee #' @param \dots Used internally by lambda.r #' @author Brian Lee Yung Rowe #' @seealso \code{\link{flog.logger}} \code{\link{flog.layout}} #' @keywords data #' @examples #' \dontrun{ #' flog.appender(appender.console(), name='my.logger') #' #' # Set an appender to the logger named 'my.package'. Any log operations from #' # this package will now use this appender. #' flog.appender(appender.file('my.package.out'), 'my.package') #' } # Get appenders associated with the given logger flog.appender(name) %::% character : Function flog.appender(name='ROOT') %as% { logger <- flog.logger(name) logger$appender } # Set the appender for the given logger flog.appender(fn, name='ROOT') %as% { flog.logger(name, appender=fn) invisible() } # Some default handlers for use in futile.logger. All handlers need to conform # to the below signature: function(line) appender.console <- function() { function(line) cat(line, sep='') } # Write to a file. appender.file <- function(file) { function(line) cat(line, file=file, append=TRUE, sep='') } # Write to a file and to console appender.tee <- function(file){ function(line) { cat(line, sep='') cat(line, file=file, append=TRUE, sep='') } } futile.logger/R/options.R0000644000176200001440000000105212740450476015046 0ustar liggesusers#' Constants for 'futile.logger' #' #' Log level constants and the logger options. #' #' The logging configuration is managed by 'logger.options', a function #' generated by OptionsManager within 'futile.options'. #' #' @name logger.options #' @aliases FATAL ERROR WARN INFO DEBUG TRACE #' @usage logger.options(..., simplify = FALSE, update = list()) #' @param ... TODO #' @param simplify TODO #' @param update TODO #' @author Brian Lee Yung Rowe #' @seealso \code{futile.options} #' @keywords data logger.options <- OptionsManager('logger.options') futile.logger/R/futile.logger-package.R0000644000176200001440000001205012740450476017512 0ustar liggesusers#' A Logging Utility for R #' #' This package implements a logging system inspired by log4j. The basic idea #' of layouts, appenders, and loggers is faithful to log4j, while the #' implementation and idiom is all R. This means that support for hierarchical #' loggers, custom appenders, custom layouts is coupled with a simple and #' intuitive functional syntax. #' #' \tabular{ll}{ #' Package: \tab futile.logger\cr #' Type: \tab Package\cr #' Version: \tab 1.4.3\cr #' Date: \tab 2016-07-10\cr #' License: \tab LGPL-3\cr #' LazyLoad: \tab yes\cr #' } #' #' The latest version of futile.logger introduces zero-configuration semantics #' out of the box. This means that you can use the default configuration as is. #' It is also easy to interactively change the configuration of the ROOT #' logger, as well as create new loggers. Since loggers form a hierarchy based #' on their name, the ROOT logger is the starting point of the hierarchy and #' always exists. By default the ROOT logger is defined with a simple layout, #' printing to the console, with an INFO threshold. This means that writing to #' any logger with a threshold of INFO or higher will write to the console. #' #' All of the logging functions take a format string so it is easy to add #' arbitrary values to log messages. #' #' > flog.info("This song is just \%s words \%s", 7, "long") #' #' Thresholds range from most verbose to least verbose: TRACE, DEBUG, INFO, #' WARN, ERROR, FATAL. You can easily change the threshold of the ROOT logger #' by calling > flog.threshold(TRACE) which changes will print all log messages #' from every package. To suppress most logging by default but turn on all #' debugging for a logger 'my.logger', you would execute #' #' > flog.threshold(ERROR)\cr #' > flog.threshold(TRACE, name='my.logger') #' #' Any arbitrary logger can be defined simply by specifying it in any #' futile.logger write operation (futile.threshold, futile.appender, #' futile.layout). If the logger hasn't been defined, then it will be defined #' dynamically. Any unspecified options will be copied from the parent logger. #' #' When writing log messages, futile.logger will search the hierarchy based on #' the logger name. In our example, if 'my.logger' hasn't been defined then #' futile.logger will look for a logger named 'my' and finally the ROOT logger. #' #' Functions calling futile.logger from a package are automatically assigned a #' logger that has the name of the package. Suppose we have log messages in a #' package called 'my.package'. Then any function that calls futile.logger from #' within the package will automatically be assigned a default logger of #' 'my.package' instead of ROOT. This means that it is easy to change the log #' setting of any package that uses futile.logger for logging by just updating #' the logger for the given package. For instance suppose you want to output #' log message for my.package to a file instead. #' #' > flog.appender(appender.file('my.package.log'), name='my.package') #' #' Now all log statements in the package my.package will be written to a file #' instead of the console. All other log messages will continue to be written #' to the console. #' #' Appenders do the actual work of writing log messages to a writeable target, #' whether that is a console, a file, a URL, database, etc. When creating an #' appender, the implementation-specific options are passed to the appender at #' instantiation. The package defines two appender generator functions: #' #' \describe{ #' \item{appender.file}{Write to a file} #' \item{appender.console}{Write to the console} #' } #' #' Each of these functions returns the actual appender function, so be sure to #' actually call the function! #' #' Layouts are responsible for formatting messages. This operation usually #' consists of adding the log level, a timestamp, plus some pretty-printing to #' make the log messages easy on the eyes. The package supplies several layouts: #' #' \describe{ #' \item{layout.simple}{Writes messages with a default format} #' \item{layout.json}{Generates messages in a JSON format} #' \item{layout.format}{Define your own format} #' \item{layout.tracearg}{Print a variable name along with its value} #' } #' #' @name futile.logger-package #' @aliases futile.logger-package futile.logger flog.namespace #' @docType package #' @exportPattern "^[^\\.]" #' @import utils lambda.r futile.options #' @author Brian Lee Yung Rowe #' @seealso \code{\link{flog.logger}}, \code{\link{flog.threshold}}, #' \code{\link{flog.layout}}, \code{\link{flog.appender}} #' @keywords package attribute logic #' @examples #' #' flog.debug("This %s print", "won't") #' flog.warn("This %s print", "will") #' #' flog.info("This inherits from the ROOT logger", name='logger.a') #' flog.threshold(DEBUG, name='logger.a') #' flog.debug("logger.a has now been set to DEBUG", name='logger.a') #' flog.debug("But the ROOT logger is still at INFO (so this won't print)") #' #' \dontrun{ #' flog.appender(appender.file("other.log"), name='logger.b') #' flog.info("This writes to a %s", "file", name='logger.b') #' } #' NULL futile.logger/R/constants.R0000644000176200001440000000032412740450476015370 0ustar liggesusersFATAL <- 1L names(FATAL) <- "FATAL" ERROR <- 2L names(ERROR) <- "ERROR" WARN <- 4L names(WARN) <- "WARN" INFO <- 6L names(INFO) <- "INFO" DEBUG <- 8L names(DEBUG) <- "DEBUG" TRACE <- 9L names(TRACE) <- "TRACE" futile.logger/R/scat.R0000644000176200001440000000127012740450476014307 0ustar liggesusers#' Print formatted messages #' #' A replacement for \code{cat} that has built-in sprintf formatting #' #' Like \code{cat} but you can use format strings. #' #' @param format A format string passed to sprintf #' @param use.newline Whether to append a new line at the end #' @param \dots Arguments to pass to sprintf for dereferencing #' @return A formatted string printed to the console #' @author Brian Lee Yung Rowe #' @keywords data #' @examples #' #' apply(array(2:5),1, function(x) scat('This has happened %s times', x) ) #' scat <- function(format, ..., use.newline=TRUE) { if (use.newline) newline = '\n' else newline = '' cat(paste(sprintf(format, ...), newline, sep='')) } futile.logger/README.md0000644000176200001440000000763112740450476014317 0ustar liggesusers[![Build Status](https://travis-ci.org/zatonovo/futile.logger.png)](https://travis-ci.org/zatonovo/futile.logger) Overview ======== futile.logger is a logging utility for R. Originally built based on log4j, the latest version introduces a new API that is more consistent with R idioms. In practice this means an interface that works equally well in the shell for interactive use and also in scripts for system use. The underlying concepts of log4j still exist, e.g. loggers, appenders, and formatters. There continues to be a hierarchical system for logger. In addition, there is now automatic package scoping, which means that packages are given their own logger namespace so you can interactively turn on and off logging for specific packages. Also included is formatting logic to log list objects (which includes data.frames) in a smart way. Usage ===== Out of the box, the default `ROOT` logger logs to the console with threshold set to INFO. ```R flog.info("Hello, %s", "world") # This won't print by default flog.debug("Goodbye, %s", "world") # Change the log level to debug and try again flog.threshold(DEBUG) flog.debug("Goodbye, %s", "world") # Keep an alternate logger at WARN flog.threshold(WARN, name='quiet') # This won't print since it's using the logger named 'quiet'! flog.debug("Goodbye, %s", "world", name='quiet') ``` Loggers ------- A logger is simply a namespace bound to a threshold, an appender, and a formatter. Loggers are configured automatically whenever they are referenced (for example when changing the threshold) inheriting the settings of the root logger. To explicitly create a logger call `log.logger()`. ```R flog.logger("tawny", WARN, appender=appender.file('tawny.log')) ``` To remove a logger, use `log.remove()`. If no such logger exists, the command is safely ignored. ```R flog.remove("tawny") ``` Thresholds ---------- The logger threshold determines what will be logged for a given logger. Use this function to retrieve and also change this threshold. ```R # Get the logging threshold for the ROOT logger flog.threshold() ``` The default logger is ROOT. To change the threshold of a different logger, specify the logger argument with a string that represents the logger. Note that a log.(debug|info|warn|error) command running from a package will automatically be associated with a logger with the name of the package. This structure means you can change the log level for a specific package as necessary. ```R # Set root logger to DEBUG level to see all log messages flog.threshold(DEBUG) # Suppress log messages below WARN for logger 'quiet' flog.threshold(WARN, name="quiet") ``` Appenders --------- An appender defines where output is directed. Typically only one appender is used per logger, but multiple can be assigned. The package provides the following appenders: + `appender.console` + `appender.file` + `appender.tee` To change the appenders assigned to a logger, use `flog.appender()`: ```R # Change the 'quiet' logger to write to a file flog.appender(appender.file('quiet.log'), 'quiet') flog.warn("Goodbye, %s", "world", name='quiet') ``` You can create your own appender by defining a function that accepts a single character argument. It is up to you to define the behavior. For example, an appender that logs to a URL might look like the following. ```R url_appender.gen <- function(url) { conn <- url(url) function(line) { file.write() } } ``` flog.format("futile.matrix", fn) Layouts ------- A layout defines how a log message is printed. The default layout.simple prints log messages using the following format: LEVEL [datetime] Message The layouts included in the package are: + layout.simple - Use a default format + layout.format - Provide a customizable format string + layout.tracearg - Dump a variable with its name What's New ========== + Function to wrap a try/catch with logging (ftry) + Capture output for print statements (for more complex objects) + New layout.tracearg futile.logger/MD50000644000176200001440000000253112740461153013334 0ustar liggesusersd614067d8a6f9b6c793d9a3cdb6b0540 *DESCRIPTION d61ed2adf506a7b7909f59c852f347ed *NAMESPACE 1969ced6e73dcd0fa77a863596c898e5 *R/appender.R adc433b28d3a7d3af71dd39a1b9db833 *R/constants.R 0ea1553b425022a30df57b7c1e121fb5 *R/futile.logger-package.R c78a7f26382a3350abee1f71c9209a22 *R/layout.R 0f41f8079924d220d49c59e0e99b90e3 *R/logger.R eb3624430fe77aa17a803acf9e383c2f *R/options.R fce29de8b2fb63d0315b07bec6bd5e5c *R/scat.R 5af3301ce507c00309be63d5f2c8bad4 *README.md 6921ef82af355463a353c91dfc3f120d *man/flog.appender.Rd 746d421add2129919aea5cf3c7d6a369 *man/flog.carp.Rd a5d5136f9eb63de9e4387d9827beb70c *man/flog.layout.Rd f021d78c3269a680b6b89d0660507bb7 *man/flog.logger.Rd 8ff226e5e61a04f2d2d9b58e07c82cd4 *man/flog.remove.Rd 0c3e06e2c7ae3db202e5f35b9df70a3e *man/flog.threshold.Rd df02150ddb649d54e0f82ae9343edf84 *man/ftry.Rd 1213f19c2224957247267af28edbd73c *man/futile.logger-package.Rd b0c25a5578011d180f1766d8c0ba5904 *man/logger.options.Rd 89f313302a032d9ca6be6f422e509d6f *man/scat.Rd b2d6ad4073eda6f23f39f2faf921f2dc *tests/testthat.R 3962c833c285a6c1dca810336022f000 *tests/testthat/test_debug.R 25119c8e5a5da1695a9987e7a6ba31b9 *tests/testthat/test_json.R b77c63046eae36f5cb9928dc28d015b2 *tests/testthat/test_layout.R 32c7e9a789e9fc1a107817f03539c01d *tests/testthat/test_logger.R dd364c3763eb235f19ea637a7ed304b7 *tests/testthat/test_stringconfig.R futile.logger/DESCRIPTION0000644000176200001440000000143612740461153014535 0ustar liggesusersPackage: futile.logger Type: Package Title: A Logging Utility for R Version: 1.4.3 Date: 2016-07-10 Author: Brian Lee Yung Rowe Maintainer: Brian Lee Yung Rowe Depends: R (>= 3.0.0) Imports: utils, lambda.r (>= 1.1.0), futile.options Suggests: testthat, jsonlite Description: Provides a simple yet powerful logging utility. Based loosely on log4j, futile.logger takes advantage of R idioms to make logging a convenient and easy to use replacement for cat and print statements. License: LGPL-3 LazyLoad: yes NeedsCompilation: no ByteCompile: yes Collate: 'options.R' 'appender.R' 'constants.R' 'layout.R' 'logger.R' 'scat.R' 'futile.logger-package.R' RoxygenNote: 5.0.1 Packaged: 2016-07-10 13:44:30 UTC; brian Repository: CRAN Date/Publication: 2016-07-10 16:57:47 futile.logger/man/0000755000176200001440000000000012740450476013604 5ustar liggesusersfutile.logger/man/flog.threshold.Rd0000644000176200001440000000205712740450476017021 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/logger.R \name{flog.threshold} \alias{flog.threshold} \title{Get and set the threshold for a logger} \arguments{ \item{threshold}{integer The new threshold for the given logger} \item{name}{character The name of the logger} } \description{ The threshold affects the visibility of a given logger. When a log statement is called, e.g. \code{flog.debug('foo')}, futile.logger compares the threshold of the logger with the level implied in the log command (in this case DEBUG). If the log level is at or higher in priority than the logger threshold, a message will print. Otherwise the command will silently return. } \section{Usage}{ # Get the threshold for the given logger\cr flog.threshold(name) \%::\% character : character \cr flog.threshold(name=ROOT) # Set the threshold for the given logger\cr flog.threshold(threshold, name=ROOT) } \examples{ flog.threshold(ERROR) flog.info("Won't print") flog.threshold(INFO) flog.info("Will print") } \author{ Brian Lee Yung Rowe } \keyword{data} futile.logger/man/ftry.Rd0000644000176200001440000000116412740450476015061 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/logger.R \name{ftry} \alias{ftry} \title{Wrap a try block in futile.logger} \usage{ ftry(expr, error = stop, finally = NULL) } \arguments{ \item{expr}{The expression to evaluate in a try block} \item{error}{An error handler} \item{finally}{Pass-through to tryCatch finally} } \description{ This function integrates futile.logger with the error and warning system so problems can be caught both in the standard R warning system, while also being emitted via futile.logger. } \examples{ ftry(log(-1)) } \author{ Brian Lee Yung Rowe } \keyword{data} futile.logger/man/futile.logger-package.Rd0000644000176200001440000001151512740450476020235 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/futile.logger-package.R \docType{package} \name{futile.logger-package} \alias{flog.namespace} \alias{futile.logger} \alias{futile.logger-package} \title{A Logging Utility for R} \description{ This package implements a logging system inspired by log4j. The basic idea of layouts, appenders, and loggers is faithful to log4j, while the implementation and idiom is all R. This means that support for hierarchical loggers, custom appenders, custom layouts is coupled with a simple and intuitive functional syntax. } \details{ \tabular{ll}{ Package: \tab futile.logger\cr Type: \tab Package\cr Version: \tab 1.4.3\cr Date: \tab 2016-07-10\cr License: \tab LGPL-3\cr LazyLoad: \tab yes\cr } The latest version of futile.logger introduces zero-configuration semantics out of the box. This means that you can use the default configuration as is. It is also easy to interactively change the configuration of the ROOT logger, as well as create new loggers. Since loggers form a hierarchy based on their name, the ROOT logger is the starting point of the hierarchy and always exists. By default the ROOT logger is defined with a simple layout, printing to the console, with an INFO threshold. This means that writing to any logger with a threshold of INFO or higher will write to the console. All of the logging functions take a format string so it is easy to add arbitrary values to log messages. > flog.info("This song is just \%s words \%s", 7, "long") Thresholds range from most verbose to least verbose: TRACE, DEBUG, INFO, WARN, ERROR, FATAL. You can easily change the threshold of the ROOT logger by calling > flog.threshold(TRACE) which changes will print all log messages from every package. To suppress most logging by default but turn on all debugging for a logger 'my.logger', you would execute > flog.threshold(ERROR)\cr > flog.threshold(TRACE, name='my.logger') Any arbitrary logger can be defined simply by specifying it in any futile.logger write operation (futile.threshold, futile.appender, futile.layout). If the logger hasn't been defined, then it will be defined dynamically. Any unspecified options will be copied from the parent logger. When writing log messages, futile.logger will search the hierarchy based on the logger name. In our example, if 'my.logger' hasn't been defined then futile.logger will look for a logger named 'my' and finally the ROOT logger. Functions calling futile.logger from a package are automatically assigned a logger that has the name of the package. Suppose we have log messages in a package called 'my.package'. Then any function that calls futile.logger from within the package will automatically be assigned a default logger of 'my.package' instead of ROOT. This means that it is easy to change the log setting of any package that uses futile.logger for logging by just updating the logger for the given package. For instance suppose you want to output log message for my.package to a file instead. > flog.appender(appender.file('my.package.log'), name='my.package') Now all log statements in the package my.package will be written to a file instead of the console. All other log messages will continue to be written to the console. Appenders do the actual work of writing log messages to a writeable target, whether that is a console, a file, a URL, database, etc. When creating an appender, the implementation-specific options are passed to the appender at instantiation. The package defines two appender generator functions: \describe{ \item{appender.file}{Write to a file} \item{appender.console}{Write to the console} } Each of these functions returns the actual appender function, so be sure to actually call the function! Layouts are responsible for formatting messages. This operation usually consists of adding the log level, a timestamp, plus some pretty-printing to make the log messages easy on the eyes. The package supplies several layouts: \describe{ \item{layout.simple}{Writes messages with a default format} \item{layout.json}{Generates messages in a JSON format} \item{layout.format}{Define your own format} \item{layout.tracearg}{Print a variable name along with its value} } } \examples{ flog.debug("This \%s print", "won't") flog.warn("This \%s print", "will") flog.info("This inherits from the ROOT logger", name='logger.a') flog.threshold(DEBUG, name='logger.a') flog.debug("logger.a has now been set to DEBUG", name='logger.a') flog.debug("But the ROOT logger is still at INFO (so this won't print)") \dontrun{ flog.appender(appender.file("other.log"), name='logger.b') flog.info("This writes to a \%s", "file", name='logger.b') } } \author{ Brian Lee Yung Rowe } \seealso{ \code{\link{flog.logger}}, \code{\link{flog.threshold}}, \code{\link{flog.layout}}, \code{\link{flog.appender}} } \keyword{attribute} \keyword{logic} \keyword{package} futile.logger/man/flog.appender.Rd0000644000176200001440000000417312740450476016624 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/appender.R \name{flog.appender} \alias{appender.console} \alias{appender.file} \alias{appender.tee} \alias{flog.appender} \title{Manage appenders for loggers} \arguments{ \item{\dots}{Used internally by lambda.r} } \description{ Provides functions for adding and removing appenders. } \section{Usage}{ # Get the appender for the given logger\cr flog.appender(name) \%::\% character : Function\cr flog.appender(name='ROOT') # Set the appender for the given logger\cr flog.appender(fn, name='ROOT') # Print log messages to the console\cr appender.console() # Write log messages to a file\cr appender.file(file) # Write log messages to console and a file\cr appender.tee(file) } \section{Details}{ Appenders do the actual work of writing log messages to some target. To use an appender in a logger, you must register it to a given logger. Use \code{flog.appender} to both access and set appenders. The ROOT logger by default uses \code{appender.console}. \code{appender.console} is a function that writes to the console. No additional arguments are necessary when registering the appender via flog.appender. \code{appender.file} writes to a file, so you must pass an additional file argument to the function. To change the file name, just call \code{flog.appender(appender.file(file))} again with a new file name. To use your own appender create a function that takes a single argument, which represents the log message. You need to pass a function reference to \code{flog.appender}. \code{appender.tee} writes to both the console and file. } \section{Value}{ When getting the appender, \code{flog.appender} returns the appender function. When setting an appender, \code{flog.appender} has no return value. } \examples{ \dontrun{ flog.appender(appender.console(), name='my.logger') # Set an appender to the logger named 'my.package'. Any log operations from # this package will now use this appender. flog.appender(appender.file('my.package.out'), 'my.package') } } \author{ Brian Lee Yung Rowe } \seealso{ \code{\link{flog.logger}} \code{\link{flog.layout}} } \keyword{data} futile.logger/man/scat.Rd0000644000176200001440000000130412740450476015023 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/scat.R \name{scat} \alias{scat} \title{Print formatted messages} \usage{ scat(format, ..., use.newline = TRUE) } \arguments{ \item{format}{A format string passed to sprintf} \item{use.newline}{Whether to append a new line at the end} \item{\dots}{Arguments to pass to sprintf for dereferencing} } \value{ A formatted string printed to the console } \description{ A replacement for \code{cat} that has built-in sprintf formatting } \details{ Like \code{cat} but you can use format strings. } \examples{ apply(array(2:5),1, function(x) scat('This has happened \%s times', x) ) } \author{ Brian Lee Yung Rowe } \keyword{data} futile.logger/man/flog.carp.Rd0000644000176200001440000000210712740450476015746 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/logger.R \name{flog.carp} \alias{flog.carp} \title{Always return the log message} \arguments{ \item{carp}{logical Whether to carp output or not} \item{name}{character The name of the logger} } \description{ Indicate whether the logger will always return the log message despite the threshold. } \details{ This is a special option to allow the return value of the flog.* logging functions to return the generated log message even if the log level does not exceed the threshold. Note that this minorly impacts performance when enabled. This functionality is separate from the appender, which is still bound to the value of the logger threshold. } \section{Usage}{ # Indicate whether the given logger should carp\cr flog.carp(name=ROOT) # Set whether the given logger should carp\cr flog.carp(carp, name=ROOT) } \examples{ flog.carp(TRUE) x <- flog.debug("Returns this message but won't print") flog.carp(FALSE) y <- flog.debug("Returns nothing and prints nothing") } \author{ Brian Lee Yung Rowe } \keyword{data} futile.logger/man/flog.logger.Rd0000644000176200001440000001125012740450476016277 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/logger.R \name{flog.logger} \alias{flog.debug} \alias{flog.error} \alias{flog.fatal} \alias{flog.info} \alias{flog.logger} \alias{flog.trace} \alias{flog.warn} \title{Manage loggers} \arguments{ \item{msg}{The message to log} \item{name}{The logger name to use} \item{capture}{Capture print output of variables instead of interpolate} \item{\dots}{Optional arguments to populate the format string} \item{expr}{An expression to evaluate} \item{finally}{An optional expression to evaluate at the end} } \description{ Provides functions for writing log messages and managing loggers. Typically only the flog.[trace|debug|info|warn|error|fatal] functions need to be used in conjunction with flog.threshold to interactively change the log level. } \section{Usage}{ # Conditionally print a log statement at TRACE log level\cr flog.trace(msg, ..., name=flog.namespace(), capture=FALSE) # Conditionally print a log statement at DEBUG log level\cr flog.debug(msg, ..., name=flog.namespace(), capture=FALSE) # Conditionally print a log statement at INFO log level\cr flog.info(msg, ..., name=flog.namespace(), capture=FALSE) # Conditionally print a log statement at WARN log level\cr flog.warn(msg, ..., name=flog.namespace(), capture=FALSE) # Conditionally print a log statement at ERROR log level\cr flog.error(msg, ..., name=flog.namespace(), capture=FALSE) # Print a log statement at FATAL log level\cr flog.fatal(msg, ..., name=flog.namespace(), capture=FALSE) # Execute an expression and capture any warnings or errors ftry(expr, error=stop, finally=NULL) } \section{Additional Usage}{ These functions generally do not need to be called by an end user. # Get the ROOT logger\cr flog.logger() # Get the logger with the specified name\cr flog.logger(name) # Set options for the given logger\cr flog.logger(name, threshold=NULL, appender=NULL, layout=NULL, carp=NULL) } \section{Details}{ These functions represent the high level interface to futile.logger. The primary use case for futile.logger is to write out log messages. There are log writers associated with all the predefined log levels: TRACE, DEBUG, INFO, WARN, ERROR, FATAL. Log messages will only be written if the log level is equal to or more urgent than the current threshold. By default the ROOT logger is set to INFO. > flog.debug("This won't print") \cr > flog.info("But this \%s", 'will') \cr > flog.warn("As will \%s", 'this') Typically, the built in log level constants are used in the call, which conform to the log4j levels (from least severe to most severe): TRACE, DEBUG, INFO, WARN, ERROR, FATAL. It is not a strict requirement to use these constants (any numeric value will work), though most users should find this level of granularity sufficient. Loggers are hierarchical in the sense that any requested logger that is undefined will fall back to its most immediate defined parent logger. The absolute parent is ROOT, which is guaranteed to be defined for the system and cannot be deleted. This means that you can specify a new logger directly. > flog.info("This will fall back to 'my', then 'ROOT'", name='my.logger') You can also change the threshold or any other setting associated with a logger. This will create an explicit logger where any unspecified options are copied from the parent logger. > flog.appender(appender.file("foo.log"), name='my') \cr > flog.threshold(ERROR, name='my.logger') \cr > flog.info("This won't print", name='my.logger') \cr > flog.error("This %s print to a file", 'will', name='my.logger') \cr If you define a logger that you later want to remove, use flog.remove. The option 'capture' allows you to print out more complicated data structures without a lot of ceremony. This variant doesn't accept format strings and instead appends the value to the next line of output. Consider > m <- matrix(rnorm(12), nrow=3) \cr > flog.info("Matrix:",m, capture=TRUE) which preserves the formatting, whereas using capture=FALSE will have a cluttered output due to recycling. } \examples{ flog.threshold(DEBUG) flog.debug("This debug message will print") flog.threshold(WARN) flog.debug("This one won't") m <- matrix(rnorm(12), nrow=3) flog.info("Matrix:",m, capture=TRUE) ftry(log(-1)) \dontrun{ s <- c('FCX','AAPL','JPM','AMZN') p <- TawnyPortfolio(s) flog.threshold(TRACE,'tawny') ws <- optimizePortfolio(p, RandomMatrixDenoiser()) z <- getIndexComposition() flog.threshold(WARN,'tawny') ws <- optimizePortfolio(p, RandomMatrixDenoiser()) z <- getIndexComposition() } } \author{ Brian Lee Yung Rowe } \seealso{ \code{\link{flog.threshold}} \code{\link{flog.remove}} \code{\link{flog.carp}} \code{\link{flog.appender}} \code{\link{flog.layout}} } \keyword{data} futile.logger/man/flog.remove.Rd0000644000176200001440000000124312740450476016316 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/logger.R \name{flog.remove} \alias{flog.remove} \title{Remove a logger} \arguments{ \item{name}{The logger name to use} } \description{ In the event that you no longer wish to have a logger registered, use this function to remove it. Then any references to this logger will inherit the next available logger in the hierarchy. } \section{Usage}{ # Remove a logger\cr flog.remove(name) } \examples{ flog.threshold(ERROR, name='my.logger') flog.info("Won't print", name='my.logger') flog.remove('my.logger') flog.info("Will print", name='my.logger') } \author{ Brian Lee Yung Rowe } \keyword{data} futile.logger/man/logger.options.Rd0000644000176200001440000000123412740450476017044 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/options.R \name{logger.options} \alias{DEBUG} \alias{ERROR} \alias{FATAL} \alias{INFO} \alias{TRACE} \alias{WARN} \alias{logger.options} \title{Constants for 'futile.logger'} \usage{ logger.options(..., simplify = FALSE, update = list()) } \arguments{ \item{...}{TODO} \item{simplify}{TODO} \item{update}{TODO} } \description{ Log level constants and the logger options. } \details{ The logging configuration is managed by 'logger.options', a function generated by OptionsManager within 'futile.options'. } \author{ Brian Lee Yung Rowe } \seealso{ \code{futile.options} } \keyword{data} futile.logger/man/flog.layout.Rd0000644000176200001440000000511012740450476016333 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/layout.R \name{flog.layout} \alias{flog.layout} \alias{layout.format} \alias{layout.json} \alias{layout.simple} \alias{layout.tracearg} \title{Manage layouts within the 'futile.logger' sub-system} \arguments{ \item{\dots}{Used internally by lambda.r} } \description{ Provides functions for managing layouts. Typically 'flog.layout' is only used when manually creating a logging configuration. } \section{Usage}{ # Get the layout function for the given logger\cr flog.layout(name) \%::\% character : Function\cr flog.layout(name='ROOT') # Set the layout function for the given logger\cr flog.layout(fn, name='ROOT') # Decorate log messages with a standard format\cr layout.simple(level, msg, ...) # Generate log messages as JSON\cr layout.json(level, msg, ...) # Decorate log messages using a custom format\cr layout.format(format, datetime.fmt="%Y-%m-%d %H:%M:%S") # Show the value of a single variable layout.tracearg(level, msg, ...) } \section{Details}{ Layouts are responsible for formatting messages so they are human-readable. Similar to an appender, a layout is assigned to a logger by calling \code{flog.layout}. The \code{flog.layout} function is used internally to get the registered layout function. It is kept visible so user-level introspection is possible. \code{layout.simple} is a pre-defined layout function that prints messages in the following format:\cr LEVEL [timestamp] message This is the default layout for the ROOT logger. \code{layout.format} allows you to specify the format string to use in printing a message. The following tokens are available. \describe{ \item{~l}{Log level} \item{~t}{Timestamp} \item{~n}{Namespace} \item{~f}{The calling function} \item{~m}{The message} } \code{layout.json} converts the message and any additional objects provided to a JSON structure. E.g.: flog.info("Hello, world", cat='asdf') yields something like \{"level":"INFO","timestamp":"2015-03-06 19:16:02 EST","message":"Hello, world","func":"(shell)","cat":["asdf"]\} \code{layout.tracearg} is a special layout that takes a variable and prints its name and contents. } \examples{ # Set the layout for 'my.package' flog.layout(layout.simple, name='my.package') # Update the ROOT logger to use a custom layout layout <- layout.format('[~l] [~t] [~n.~f] ~m') flog.layout(layout) # Create a custom logger to trace variables flog.layout(layout.tracearg, name='tracer') x <- 5 flog.info(x, name='tracer') } \author{ Brian Lee Yung Rowe } \seealso{ \code{\link{flog.logger}} \code{\link{flog.appender}} } \keyword{data}