logging/0000755000176200001440000000000013512631413011677 5ustar liggesuserslogging/tests/0000755000176200001440000000000013512626061013044 5ustar liggesuserslogging/tests/run_tests.R0000644000176200001440000000026413512626061015217 0ustar liggesuserslibrary(testthat) test_results <- data.frame(testthat::test_dir("."), stringsAsFactors = FALSE) if (any(test_results$failed) || any(test_results$error)) { q(status = 1) } logging/tests/testthat.R0000644000176200001440000000007613512626061015032 0ustar liggesuserslibrary(testthat) library(logging) test_check("logging") logging/tests/testthat/0000755000176200001440000000000013512631413014701 5ustar liggesuserslogging/tests/testthat/test.msg-composer.R0000644000176200001440000000376413512626061020432 0ustar liggesuserslibrary(testthat) library(logging) context("Testing message composer functionality [test.msg-composer]") test_setup <- function() { test_env <- new.env(parent = emptyenv()) test_env$logged <- NULL mock_action <- function(msg, handler, ...) { if (length(list(...)) && "dry" %in% names(list(...))) return(TRUE) test_env$logged <- c(test_env$logged, msg) } mock_formatter <- function(record) { paste(record$levelname, record$logger, record$msg, sep = ":") } logReset() addHandler(mock_action, formatter = mock_formatter) return(test_env) } ## test functions test_that("msgcomposer/basic_funcionality", { env <- test_setup() setMsgComposer(function(msg, ...) { paste(msg, "comp") }) loginfo("test") expect_equal(env$logged, "INFO::test comp") }) test_that("msgcomposer/composer_signature_validation", { env <- test_setup() expect_error({ setMsgComposer(function(msgX, ...) { paste(msg, "comp") }) }) }) test_that("msgcomposer/reset_composer", { env <- test_setup() setMsgComposer(function(msg, ...) { paste(msg, "comp") }) resetMsgComposer() loginfo("test") expect_equal(env$logged, "INFO::test") }) test_that("msgcomposer/set_sublogger_composer", { env <- test_setup() setMsgComposer(function(msg, ...) { paste(msg, "comp") }, container = "named") loginfo("test") loginfo("test", logger = "named") expect_equal(env$logged, c("INFO::test", "INFO:named:test comp")) }) test_that("msgcomposer/set_sublogger_composer_directly", { env <- test_setup() getLogger("named")$setMsgComposer(function(msg, ...) { paste(msg, "comp") }) loginfo("test") loginfo("test", logger = "named") expect_equal(env$logged, c("INFO::test", "INFO:named:test comp")) }) test_that("msgcomposer/composer_signature_validation_directly", { env <- test_setup() expect_error({ getLogger("named")$setMsgComposer(function(msgX, ...) { paste(msg, "comp") }) }) }) logging/tests/testthat/test.data.interaction.R0000644000176200001440000001417613512626061021245 0ustar liggesuserslibrary(testthat) library(logging) context("Testing data interactions [test.data.interaction]") test_that("000/getLoggerWithoutInitializingDoesNotCrash", { root_logger <- getLogger("") succeed() }) test_that("001/defaultLoggingLevelIsINFO", { basicConfig() root_logger <- getLogger("") expect <- logging:::loglevels["INFO"] expect_equal(root_logger[["level"]], expect) }) test_that("002/canInitializeTwice", { basicConfig() basicConfig() root_logger <- getLogger("") expect <- logging:::loglevels["INFO"] expect_equal(root_logger[["level"]], expect) }) test_that("003/sameNameMeansSameObject", { basicConfig() root1 <- getLogger("abc") root2 <- getLogger("abc") expect_identical(root1, root2) }) test_that("004/noNameMeansRoot", { root_logger1 <- getLogger("") root_logger2 <- getLogger() expect_identical(root_logger1, root_logger2) }) test_that("500.basicConfigSetsLevelOfHandler", { logReset() basicConfig("DEBUG") root_logger <- getLogger("") expect <- logging:::loglevels["NOTSET"] current <- root_logger$getHandler("basic.stdout")[["level"]] expect_equal(current, expect) logReset() basicConfig("ERROR") root_logger <- getLogger("") ## needed: `logReset` unlinked the old one expect <- logging:::loglevels["NOTSET"] current <- root_logger$getHandler("basic.stdout")[["level"]] expect_equal(current, expect) }) test_that("canFindLoggingLevels", { expect_equal(logging:::loglevels[["NOTSET"]], 0) expect_equal(logging:::loglevels[["DEBUG"]], 10) expect_equal(logging:::loglevels[["INFO"]], 20) expect_equal(logging:::loglevels[["WARN"]], 30) expect_equal(logging:::loglevels[["ERROR"]], 40) expect_equal(logging:::loglevels[["FATAL"]], 50) }) test_that("fineLevelsAreOrdered", { expect_true(logging:::loglevels[["FINEST"]] < logging:::loglevels[["FINER"]]) expect_true(logging:::loglevels[["FINER"]] < logging:::loglevels[["FINE"]]) expect_true(logging:::loglevels[["FINE"]] < logging:::loglevels[["DEBUG"]]) }) test_that("canSetLoggerLevelByNamedValue", { logReset() basicConfig() setLevel(logging:::loglevels["DEBUG"], "") root_logger <- getLogger("") expect <- logging:::loglevels["DEBUG"] expect_equal(root_logger[["level"]], expect) }) test_that("canSetLoggerLevelByName", { logReset() basicConfig() setLevel("DEBUG", "") root_logger <- getLogger("") expect <- logging:::loglevels["DEBUG"] expect_equal(root_logger[["level"]], expect) }) test_setup <- function(...) { test_env <- new.env(parent = emptyenv()) test_env$logged <- NULL mock_action <- function(msg, handler, ...) { if (length(list(...)) && "dry" %in% names(list(...))) return(TRUE) test_env$logged <- c(test_env$logged, msg) } mock_formatter <- function(record) { msg <- trimws(record$msg) paste(record$levelname, record$logger, msg, sep = ":") } logReset() addHandler(mock_action, formatter = mock_formatter, ...) return(test_env) } test_that("recordIsEmitted/rootToRoot", { env <- test_setup() logfinest("test") logfiner("test") logfine("test") logdebug("test") loginfo("test") logerror("test") expect_equal(length(env$logged), 2) }) test_that("recordIsEmitted/tooDeep", { env <- test_setup(logger = "too.deep") logfinest("test") logfiner("test") logfine("test") logdebug("test") loginfo("test") logerror("test") expect_equal(length(env$logged), 0) }) test_that("recordIsEmitted/unrelated", { env <- test_setup(logger = "too.deep") logdebug("test", logger = "other.branch") loginfo("test", logger = "other.branch") logerror("test", logger = "other.branch") expect_equal(length(env$logged), 0) }) test_that("recordIsEmitted/deepToRoot", { env <- test_setup(logger = "") logdebug("test", logger = "other.branch") loginfo("test", logger = "other.branch") logerror("test", logger = "other.branch") expect_equal(length(env$logged), 2) }) test_that("recordIsEmitted/deepToRoot.DI.dropped", { env <- test_setup(level = "DEBUG", logger = "") setLevel("INFO", "other.branch") logdebug("test", logger = "other.branch") loginfo("test", logger = "other.branch") logerror("test", logger = "other.branch") expect_equal(length(env$logged), 2) }) test_that("recordIsEmitted/deepToRoot.DD.passed", { env <- test_setup(level = "DEBUG", logger = "") setLevel("DEBUG", "other.branch") setLevel("DEBUG", "") logdebug("test", logger = "other.branch") loginfo("test", logger = "other.branch") logerror("test", logger = "other.branch") expect_equal(length(env$logged), 3) }) test_that("formattingRecord/lengthZero", { env <- test_setup(level = "DEBUG", logger = "") loginfo("test '%s'", numeric(0)) expect_equal(env$logged, "INFO::test ''") }) test_that("formattingRecord/lengthOne", { env <- test_setup(level = "DEBUG", logger = "") loginfo("test '%s'", 12) expect_equal(env$logged, "INFO::test '12'") }) test_that("formattingRecord/lengthMore", { env <- test_setup(level = "DEBUG", logger = "") loginfo("test '%s'", c(0, 1, 2)) expect_equal(env$logged, "INFO::test '0,1,2'") }) test_that("formattingRecord/moreArguments", { env <- test_setup(level = "DEBUG", logger = "") loginfo("%s: %d", "name", 123) expect_equal(env$logged, "INFO::name: 123") env$logged <- NULL loginfo("%s: %0.2f", "name", 123.0) expect_equal(env$logged, "INFO::name: 123.00") }) test_that("formattingRecord/moreArguments.lengthMore", { env <- test_setup(level = "DEBUG", logger = "") loginfo("%s '%s'", "name", c(0, 1, 2)) expect_equal(env$logged, "INFO::name '0,1,2'") }) test_that("formattingRecord/strips.whitespace", { env <- test_setup(level = "DEBUG", logger = "") loginfo("a string with trailing whitespace \n") expect_equal(env$logged, "INFO::a string with trailing whitespace") env$logged <- NULL loginfo(" this string has also leading whitespace ") expect_equal(env$logged, "INFO::this string has also leading whitespace") }) logging/tests/testthat/test.format-limitations.R0000644000176200001440000000541613512626061021635 0ustar liggesuserslibrary(testthat) library(logging) context("Testing formatting limitation handling [test.format-limitations]") test_setup <- function() { test_env <- new.env(parent = emptyenv()) test_env$logged <- NULL mock_action <- function(msg, handler, ...) { if (length(list(...)) && "dry" %in% names(list(...))) return(TRUE) test_env$logged <- c(test_env$logged, msg) } mock_formatter <- function(record) { paste(record$levelname, record$logger, record$msg, sep = ":") } logReset() addHandler(mock_action, level = "DEBUG", formatter = mock_formatter) return(test_env) } ## test functions test_that("formatlimits/up_to_limit_w_formating", { env <- test_setup() msg0_9 <- paste(0:9, collapse = "") msg8190 <- paste(rep(msg0_9, 819), collapse = "") stopifnot(nchar(msg8190) == 8190) msg8190_s <- paste0(msg8190, "%s") stopifnot(nchar(msg8190_s) == 8192) loginfo(msg8190_s, paste0(".", msg0_9)) expect_equal(env$logged, paste0("INFO::", msg8190, ".", msg0_9)) }) test_that("formatlimits/over_limit_w_formatting_fails", { env <- test_setup() msg0_9 <- paste(0:9, collapse = "") msg8190 <- paste(rep(msg0_9, 819), collapse = "") stopifnot(nchar(msg8190) == 8190) msg8190_sX <- paste0(msg8190, "%s", "X") stopifnot(nchar(msg8190_sX) == 8193) expect_error({ loginfo(msg8190_sX, paste0(".", msg0_9)) }, regexp = "'msg' length exceeds maximal format length 8192") }) test_that("formatlimits/over_limit_wo_format_args_passes", { env <- test_setup() msg0_9 <- paste(0:9, collapse = "") msg8190 <- paste(rep(msg0_9, 819), collapse = "") stopifnot(nchar(msg8190) == 8190) msg8190_sX <- paste0(msg8190, "%s", "X") stopifnot(nchar(msg8190_sX) == 8193) loginfo(msg8190_sX) expect_equal(env$logged, paste0("INFO::", msg8190_sX)) }) test_that("formatlimits/over_limit_wo_formatting", { env <- test_setup() msg0_9 <- paste(0:9, collapse = "") msg8190 <- paste(rep(msg0_9, 819), collapse = "") stopifnot(nchar(msg8190) == 8190) # long message without formatting msg8190_x2 <- paste0(msg8190, ".", msg8190) stopifnot(nchar(msg8190_x2) == 2 * 8190 + 1) loginfo(msg8190_x2) expect_equal(env$logged, paste0("INFO::", msg8190_x2)) env$logged <- c() # long message with %% at end msg8190_m_perc <- paste0(msg8190, "%%", "X") stopifnot(nchar(msg8190_m_perc) == 8193) loginfo(msg8190_m_perc) expect_equal(env$logged, paste0("INFO::", msg8190_m_perc)) env$logged <- c() # long message with %% at start msg8190_perc_m <- paste0("%%", "X", msg8190) stopifnot(nchar(msg8190_perc_m) == 8193) loginfo(msg8190_perc_m) expect_equal(env$logged, paste0("INFO::", msg8190_perc_m)) }) logging/tests/testthat/test.handlers.R0000644000176200001440000001136013512626061017606 0ustar liggesuserslibrary(testthat) library(logging) context("Testing handlers management [test.handlers]") test_that("looksForHandlersInRootLogger", { logReset() basicConfig() expect_identical(getHandler("basic.stdout"), getLogger()[["handlers"]][[1]]) }) test_that("lookingForHandlersInObject", { logReset() basicConfig() expect_identical(getLogger()$getHandler("basic.stdout"), getLogger()[["handlers"]][[1]]) }) test_that("addingANewHandler", { logReset() basicConfig() addHandler(writeToConsole) expect_equal(length(with(getLogger(), names(handlers))), 2) expect_true("writeToConsole" %in% with(getLogger(), names(handlers))) }) test_that("gettingHandler", { logReset() basicConfig() addHandler(writeToConsole) expect_true(!is.null(getHandler("writeToConsole"))) expect_true(!is.null(getHandler(writeToConsole))) }) test_that("gettingHandler/oo", { logReset() basicConfig() log <- getLogger("testLogger") log$addHandler(writeToConsole) expect_true(!is.null(log$getHandler("writeToConsole"))) expect_true(!is.null(log$getHandler(writeToConsole))) }) test_that("removingOneHandlerByName", { logReset() basicConfig() addHandler(writeToConsole) removeHandler("writeToConsole") expect_equal(length(with(getLogger(), names(handlers))), 1) expect_false("writeToConsole" %in% with(getLogger(), names(handlers))) }) test_that("removingOneHandlerByAction", { logReset() basicConfig() addHandler(writeToConsole) expect_equal(length(with(getLogger(), names(handlers))), 2) removeHandler(writeToConsole) expect_equal(length(with(getLogger(), names(handlers))), 1) expect_false("writeToConsole" %in% with(getLogger(), names(handlers))) }) test_that("removingOneHandlerByAction/oo", { logReset() basicConfig() log <- getLogger("testLogger") log$addHandler(writeToConsole) expect_equal(length(with(log, names(handlers))), 1) log$removeHandler(writeToConsole) expect_equal(length(with(log, names(handlers))), 0) expect_false("writeToConsole" %in% with(log, names(handlers))) }) test_that("setLevel verifies if container is not NULL", { logReset() basicConfig() expect_error(setLevel("INFO", NULL), "NULL container provided: cannot set level for NULL container", fixed = TRUE) }) test_that("addHandler verifies if action provided", { logReset() basicConfig() expect_error(addHandler("handlerName"), "No action for the handler provided", fixed = TRUE) }) test_that("setLevel verifies level value", { logReset() setLevel(150) # invalid level expect_equal(getLogger()$getLevel(), loglevels["NOTSET"]) logReset() setLevel(TRUE) # invalid level expect_equal(getLogger()$getLevel(), loglevels["NOTSET"]) logReset() setLevel("INVALID") # invalid level expect_equal(getLogger()$getLevel(), loglevels["NOTSET"]) }) test_that("setLevel verifies level value/oo", { logReset() log <- getLogger("testLogger") log$setLevel(150) # invalid level expect_equal(log$getLevel(), loglevels["NOTSET"]) log$setLevel(TRUE) # invalid level expect_equal(log$getLevel(), loglevels["NOTSET"]) log$setLevel("INVALID") # invalid level expect_equal(log$getLevel(), loglevels["NOTSET"]) }) test_that("setting level with updateOptions", { logReset() basicConfig() updateOptions("", level = "WARN") expect_equal(getLogger()$getLevel(), loglevels["WARN"]) }) test_file_name <- file.path(tempdir(), c("1", "2", "3")) teardown({ unlink(test_file_name, force = TRUE) }) test_that("loggingToConsole", { logReset() basicConfig() logdebug("test %d", 2) loginfo("test %d", 2) succeed() }) test_that("loggingToFile", { logReset() unlink(test_file_name, force = TRUE) getLogger()$setLevel("FINEST") addHandler(writeToFile, file = test_file_name[[1]], level = "DEBUG") expect_equal(with(getLogger(), names(handlers)), c("writeToFile")) logerror("test %d", 1) logwarn("test %d", 3) loginfo("test %d", 1) logdebug("test %d", 2) logfinest("test %d", 4) logfiner("test %d", 4) logfine("test %d", 4) succeed() }) test_that("loggingToFile.oo", { logReset() unlink(test_file_name, force = TRUE) log <- getLogger() log$setLevel("FINEST") log$addHandler(writeToFile, file = test_file_name[[1]], level = "DEBUG") expect_equal(with(log, names(handlers)), c("writeToFile")) log$error("test %d", 1) log$warn("test %d", 3) log$info("test %d", 1) log$debug("test %d", 2) log$finest("test %d", 4) log$finer("test %d", 4) log$fine("test %d", 4) succeed() }) logging/tests/testthat/test.automatic-format.R0000644000176200001440000000340513512626061021263 0ustar liggesuserslibrary(testthat) library(logging) context("Testing formatting of mess and exprs [test.automatic-format]") test_setup <- function() { test_env <- new.env(parent = emptyenv()) test_env$logged <- NULL mock_action <- function(msg, handler, ...) { if (length(list(...)) && "dry" %in% names(list(...))) return(TRUE) test_env$logged <- c(test_env$logged, msg) } mock_formatter <- function(record) { paste(record$levelname, record$logger, record$msg, sep = ":") } logReset() addHandler(mock_action, level = "DEBUG", formatter = mock_formatter) return(test_env) } ## test functions test_that("autoformat/one_numeric_literal", { env <- test_setup() loginfo(12) expect_equal(env$logged, "INFO::12: 12") }) test_that("autoformat/more_numeric_literals", { env <- test_setup() loginfo(12, 1 + 1, 2 * 2) expect_equal(env$logged, c("INFO::12: 12", "INFO::1 + 1: 2", "INFO::2 * 2: 4")) }) test_that("autoformat/one_numeric_variable", { env <- test_setup() a <- 1 loginfo(a) expect_equal(env$logged, "INFO::a: 1") }) test_that("autoformat/one_numeric_expression", { env <- test_setup() loginfo(3 + 5) expect_equal(env$logged, "INFO::3 + 5: 8") }) test_that("autoformat/explicit_msg_parameter", { env <- test_setup() loginfo(logger = getLogger(), msg = 3 + 5) expect_equal(env$logged, "INFO::3 + 5: 8") }) test_that("autoformat/shifted_msg_parameter", { env <- test_setup() loginfo(logger = getLogger(), 3 + 5) expect_equal(env$logged, "INFO::3 + 5: 8") }) test_that("autoformat/levellog", { env <- test_setup() levellog("INFO", 3 + 5) expect_equal(env$logged, "INFO::3 + 5: 8") }) logging/tests/testthat/test.level-inheritance.R0000644000176200001440000000616513512626061021413 0ustar liggesuserslibrary(testthat) library(logging) context("Testing loglevel inheritance [test.level-inheritance]") test_setup <- function() { test_env <- new.env(parent = emptyenv()) test_env$logged <- NULL mock_action <- function(msg, handler, ...) { if (length(list(...)) && "dry" %in% names(list(...))) return(TRUE) test_env$logged <- c(test_env$logged, msg) } mock_formatter <- function(record) { paste(record$levelname, record$logger, record$msg, sep = ":") } logReset() addHandler(mock_action, formatter = mock_formatter) test_env$mock_action <- mock_action return(test_env) } test_that("leveliherit/sublogger_default_to_inherit_parent_level", { env <- test_setup() loginfo("test", logger = "named") logdebug("test", logger = "named") expect_equal(env$logged, "INFO:named:test") env$logged <- c() setLevel("DEBUG") loginfo("test", logger = "named") logdebug("test", logger = "named") expect_equal(env$logged, c("INFO:named:test", "DEBUG:named:test")) }) test_that("leveliherit/sublogger_enforced_level_used", { env <- test_setup() setLevel("DEBUG") setLevel("INFO", container = "named") loginfo("test", logger = "named") logdebug("test", logger = "named") expect_equal(env$logged, c("INFO:named:test")) }) test_that("leveliherit/handler_default_to_inherit_parent_level", { env <- test_setup() mock_action2 <- env$mock_action addHandler(mock_action2, formatter = function(record) { paste(record$levelname, record$logger, record$msg, sep = "|") }, logger = "named") loginfo("test", logger = "named") logdebug("test", logger = "named") expect_equal(env$logged, c("INFO|named|test", "INFO:named:test")) env$logged <- c() setLevel("DEBUG") loginfo("test", logger = "named") logdebug("test", logger = "named") expect_equal(env$logged, c("INFO|named|test", "INFO:named:test", "DEBUG|named|test", "DEBUG:named:test")) }) test_that("leveliherit/handler_inherit_enforced_sublogger_level", { env <- test_setup() setLevel("DEBUG") setLevel("INFO", container = "named") mock_action2 <- env$mock_action addHandler(mock_action2, formatter = function(record) { paste(record$levelname, record$logger, record$msg, sep = "|") }, logger = "named") loginfo("test", logger = "named") logdebug("test", logger = "named") expect_equal(env$logged, c("INFO|named|test", "INFO:named:test")) env$logged <- c() setLevel("INFO") setLevel("DEBUG", container = "named") logdebug("test", logger = "named") expect_equal(env$logged, c("DEBUG|named|test")) }) test_that("leveliherit/handler_enforced_level_used", { env <- test_setup() setLevel("DEBUG") mock_action2 <- env$mock_action addHandler(mock_action2, formatter = function(record) { paste(record$levelname, record$logger, record$msg, sep = "|") }, logger = "named", level = "INFO") loginfo("test", logger = "named") logdebug("test", logger = "named") expect_equal(env$logged, c("INFO|named|test", "INFO:named:test", "DEBUG:named:test")) }) logging/NAMESPACE0000644000176200001440000000111313512626061013115 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method(updateOptions,Logger) S3method(updateOptions,character) S3method(updateOptions,environment) export(addHandler) export(basicConfig) export(getHandler) export(getLogger) export(levellog) export(logReset) export(logdebug) export(logerror) export(logfine) export(logfiner) export(logfinest) export(loginfo) export(loglevels) export(logwarn) export(removeHandler) export(resetMsgComposer) export(setLevel) export(setMsgComposer) export(updateOptions) export(writeToConsole) export(writeToFile) import(methods) logging/NEWS.md0000644000176200001440000000254213512626061013003 0ustar liggesusers# logging 0.10-108 (2019-07-14) * issue #4: logged do not raise exception if formatting message contains %F markers and not formatting arguments passed * sub-loggers are created by default with loglevel inheritance from parent * issue #4: message composer can be set directly for logger object # logging 0.9-107 (2019-02-09) * issue #4: added possibility to set custom message composer for logger e.g. based on glue formating. # logging 0.9-106 (2019-01-23) * issue #2 fixed: handling of sprintf limitation to 8192 characters in fmt. If no formatting parameters provided and fmt does not contain markers it logs message just as it is. If formatting required on msg over 8192 chars it raises error. # logging 0.9-105 (2019-01-16) * Documentation generation migrated to roxygen2 * Tests migrated to testthat, test coverage enhanced * Most issues reported by goodpractice fixed * If crayon is available console messages are in color * #1 fixed. * Basic handlers are created with NOTSET level (if not specified) so they pass log records to main logger. * Root logger initialization changed: basicConfig is not required any more for loginfo to work. If you need clear logger without default handlers use logReset. # logging 0.8-104 (2018-11-29) * Maintener changed to Walerian Sokolowski logging/R/0000755000176200001440000000000013512626061012103 5ustar liggesuserslogging/R/utils.R0000644000176200001440000001524213512626061013372 0ustar liggesusers## ## this is part of the logging package. the logging package is free ## software: you can redistribute it as well as modify it under the terms of ## the GNU General Public License as published by the Free Software ## Foundation, either version 3 of the License, or (at your option) any later ## version. ## ## this program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with the nens libraray. If not, see http://www.gnu.org/licenses/. ## ## Copyright (c) 2009..2013 by Mario Frasca ## #' #' Predefined(sample) handler actions #' #' When you define a handler, you specify its name and the associated action. #' A few predefined actions described below are provided. #' #' A handler action is a function that accepts a formated message and handler #' configuration. #' #' Messages passed are filtered already regarding loglevel. #' #' \dots parameters are used by logging system to interact with the action. \dots can #' contain \var{dry} key to inform action that it meant to initialize itself. In the case #' action should return TRUE if initialization succeded. #' #' If it's not a dry run \dots contain the whole preformated \var{logging.record}. #' A \var{logging.record} is a named list and has following structure: #' \describe{ #' \item{msg}{contains the real formatted message} #' \item{level}{message level as numeric} #' \item{levelname}{message level name} #' \item{logger}{name of the logger that generated it} #' \item{timestamp}{formatted message timestamp} #' } #' #' @param msg A formated message to handle. #' @param handler The handler environment containing its options. You can #' register the same action to handlers with different properties. #' @param ... parameters provided by logger system to interact with the action. #' #' @examples #' ## define your own function and register it with a handler. #' ## author is planning a sentry client function. please send #' ## any interesting function you may have written! #' #' @name inbuilt-actions NULL #' @rdname inbuilt-actions #' #' @details #' \code{writeToConsole} detects if crayon package is available and uses it #' to color messages. The coloring can be switched off by means of configuring #' the handler with \var{color_output} option set to FALSE. #' #' @export #' writeToConsole <- function(msg, handler, ...) { if (length(list(...)) && "dry" %in% names(list(...))) { if (!is.null(handler$color_output) && handler$color_output == FALSE) { handler$color_msg <- function(msg, level_name) msg } else { handler$color_msg <- .build_msg_coloring() } return(TRUE) } stopifnot(length(list(...)) > 0) level_name <- list(...)[[1]]$levelname msg <- handler$color_msg(msg, level_name) cat(paste0(msg, "\n")) } .build_msg_coloring <- function() { crayon_env <- tryCatch(asNamespace("crayon"), error = function(e) NULL) default_color_msg <- function(msg, level_name) msg if (is.null(crayon_env)) { return(default_color_msg) } if (is.null(crayon_env$make_style) || is.null(crayon_env$combine_styles) || is.null(crayon_env$reset)) { return(default_color_msg) } color_msg <- function(msg, level_name) { style <- switch(level_name, "FINEST" = crayon_env$make_style("gray80"), "FINER" = crayon_env$make_style("gray60"), "FINE" = crayon_env$make_style("gray60"), "DEBUG" = crayon_env$make_style("deepskyblue4"), "INFO" = crayon_env$reset, "WARNING" = crayon_env$make_style("darkorange"), "ERROR" = crayon_env$make_style("red4"), "CRITICAL" = crayon_env$combine_styles(crayon_env$bold, crayon_env$make_style("red1")), crayon_env$make_style("gray100")) res <- paste0(style(msg), crayon_env$reset("")) return(res) } return(color_msg) } #' @rdname inbuilt-actions #' #' @details \code{writeToFile} action expects file path to write to under #' \var{file} key in handler options. #' #' @export #' writeToFile <- function(msg, handler, ...) { if (length(list(...)) && "dry" %in% names(list(...))) return(exists("file", envir = handler)) cat(paste0(msg, "\n"), file = with(handler, file), append = TRUE) } ## the single predefined formatter defaultFormat <- function(record) { ## strip leading and trailing whitespace from the final message. msg <- trimws(record$msg) text <- paste(record$timestamp, paste(record$levelname, record$logger, msg, sep = ":")) return(text) } ## default way of composing msg with parameters defaultMsgCompose <- function(msg, ...) { optargs <- list(...) if (is.character(msg)) { ## invoked as ("printf format", arguments_for_format) if (length(optargs) > 0) { optargs <- lapply(optargs, function(x) { if (length(x) != 1) x <- paste(x, collapse = ",") x }) } # 8192 is limitation on fmt in sprintf if (any(nchar(msg) > 8192)) { if (length(optargs) > 0) { stop("'msg' length exceeds maximal format length 8192") } # else msg must not change in any way return(msg) } if (length(optargs) > 0) { msg <- do.call("sprintf", c(msg, optargs)) } return(msg) } ## invoked as list of expressions ## this assumes that the function the user calls is two levels up, e.g.: ## loginfo -> .levellog -> logger$log -> .default_msg_composer ## levellog -> .levellog -> logger$log -> .default_msg_composer external_call <- sys.call(-3) 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 formal_names <- names(formals(external_fn)) is_output_param <- matched_call_names == "msg" | !(matched_call_names %in% c(setdiff(formal_names, "..."))) label <- lapply(matched_call[is_output_param], deparse) msg <- sprintf("%s: %s", label, c(msg, optargs)) return(msg) } logging/R/logger.R0000644000176200001440000003053113512626061013507 0ustar liggesusers## ## this is part of the logging package. the logging package is free ## software: you can redistribute it as well as modify it under the terms of ## the GNU General Public License as published by the Free Software ## Foundation, either version 3 of the License, or (at your option) any later ## version. ## ## this program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with the nens libraray. If not, see http://www.gnu.org/licenses/. ## ## Copyright (c) 2009..2013 by Mario Frasca ## #' #' Entry points for logging actions #' #' Generate a log record and pass it to the logging system.\cr #' #' A log record gets timestamped and will be independently formatted by each #' of the handlers handling it.\cr #' #' Leading and trailing whitespace is stripped from the final message. #' #' @param msg the textual message to be output, or the format for the \dots #' arguments #' @param ... if present, msg is interpreted as a format and the \dots values #' are passed to it to form the actual message. #' @param logger the name of the logger to which we pass the record #' #' @examples #' logReset() #' addHandler(writeToConsole) #' loginfo('this goes to console') #' logdebug('this stays silent') #' #' @name logging-entrypoints #' NULL #' @rdname logging-entrypoints #' @export #' logdebug <- function(msg, ..., logger = "") { .levellog(loglevels["DEBUG"], msg, ..., logger = logger) } #' @rdname logging-entrypoints #' @export #' logfinest <- function(msg, ..., logger = "") { .levellog(loglevels["FINEST"], msg, ..., logger = logger) } #' @rdname logging-entrypoints #' @export #' logfiner <- function(msg, ..., logger = "") { .levellog(loglevels["FINER"], msg, ..., logger = logger) } #' @rdname logging-entrypoints #' @export #' logfine <- function(msg, ..., logger = "") { .levellog(loglevels["FINE"], msg, ..., logger = logger) } #' @rdname logging-entrypoints #' @export #' loginfo <- function(msg, ..., logger = "") { .levellog(loglevels["INFO"], msg, ..., logger = logger) } #' @rdname logging-entrypoints #' @export #' logwarn <- function(msg, ..., logger = "") { .levellog(loglevels["WARN"], msg, ..., logger = logger) } #' @rdname logging-entrypoints #' @export #' logerror <- function(msg, ..., logger = "") { .levellog(loglevels["ERROR"], msg, ..., logger = logger) } #' @rdname logging-entrypoints #' #' @param level The logging level #' @export levellog <- function(level, msg, ..., logger = "") { # just calling .levellog # do not simplify it as call sequence sould be same # as for other logX functions .levellog(level, msg, ..., logger = logger) } .levellog <- function(level, msg, ..., logger = "") { if (is.character(logger)) { logger <- getLogger(logger) } logger$log(level, msg, ...) } #' #' Set defaults and get the named logger. #' #' Make sure a logger with a specific name exists and return it as a #' \var{Logger} S4 object. if not yet present, the logger will be created and #' given the values specified in the \dots arguments. #' #' @param name The name of the logger #' @param ... Any properties you may want to set in the newly created #' logger. These have no effect if the logger is already present. #' #' @return The logger retrieved or registered. #' #' @examples #' getLogger() # returns the root logger #' getLogger('test.sub') # constructs a new logger and returns it #' getLogger('test.sub') # returns it again #' #' @export #' getLogger <- function(name = "", ...) { if (name == "") { fullname <- "logging.ROOT" } else { fullname <- paste("logging.ROOT", name, sep = ".") } if (!exists(fullname, envir = logging.options)) { logger <- Logger$new(name = name, handlers = list(), level = namedLevel("NOTSET")) updateOptions.environment(logger, ...) logging.options[[fullname]] <- logger if (fullname == "logging.ROOT") { .basic_config(logger) } } logging.options[[fullname]] } #' #' Bootstrapping the logging package. #' #' \code{basicConfig} and \code{logReset} provide a way to put the logging package #' in a know initial state. #' #' @examples #' basicConfig() #' logdebug("not shown, basic is INFO") #' logwarn("shown and timestamped") #' logReset() #' logwarn("not shown, as no handlers are present after a reset") #' #' @name bootstrapping NULL #' @rdname bootstrapping #' #' @details #' \code{basicConfig} creates the root logger, attaches a console handler(by #' \var{basic.stdout} name) to it and sets the level of the handler to #' \code{level}. You must not call \code{basicConfig} to for logger to work any more: #' then root logger is created it gets initialized by default the same way as #' \code{basicConfig} does. If you need clear logger to fill with you own handlers #' use \code{logReset} to remove all default handlers. #' #' @param level The logging level of the root logger. Defaults to INFO. Please do notice that #' this has no effect on the handling level of the handler that basicConfig attaches to the #' root logger. #' #' @export #' basicConfig <- function(level = 20) { root_logger <- getLogger() updateOptions(root_logger, level = namedLevel(level)) .basic_config(root_logger) invisible() } #' Called from basicConfig and while creating rootLogger. #' @noRd .basic_config <- function(root_logger) { stopifnot(root_logger$name == "") root_logger$addHandler("basic.stdout", writeToConsole) } #' @rdname bootstrapping #' #' @details #' \code{logReset} reinitializes the whole logging system as if the package had just been #' loaded except it also removes all default handlers. Typically, you would want to call #' \code{basicConfig} immediately after a call to \code{logReset}. #' #' @export #' logReset <- function() { ## reinizialize the whole logging system ## remove all content from the logging environment rm(list = ls(logging.options), envir = logging.options) root_logger <- getLogger(level = "INFO") root_logger$removeHandler("basic.stdout") invisible() } #' #' Add a handler to or remove one from a logger. #' #' Use this function to maintain the list of handlers attached to a logger.\cr #' \cr #' \code{addHandler} and \code{removeHandler} are also offered as methods of the #' \var{Logger} S4 class. #' #' @details #' Handlers are implemented as environments. Within a logger a handler is #' identified by its \var{name} and all handlers define at least the #' three variables: #' \describe{ #' \item{level}{all records at level lower than this are skipped.} #' \item{formatter}{a function getting a record and returning a string} #' \item{\code{action(msg, handler)}}{a function accepting two parameters: a #' formatted log record and the handler itself. making the handler a #' parameter of the action allows us to have reusable action functions.} #' } #' #' Being an environment, a handler may define as many variables as you #' think you need. keep in mind the handler is passed to the action #' function, which can check for existence and can use all variables that #' the handler defines. #' #' @param handler The name of the handler, or its action #' @param logger the name of the logger to which to attach the new handler, #' defaults to the root logger. #' #' @examples #' logReset() #' addHandler(writeToConsole) #' names(getLogger()[["handlers"]]) #' loginfo("test") #' removeHandler("writeToConsole") #' logwarn("test") #' #' @name handlers-management NULL #' @rdname handlers-management #' #' @param ... Extra parameters, to be stored in the handler list #' #' \dots may contain extra parameters that will be passed to the handler #' action. Some elements in the \dots will be interpreted here. #' #' @export #' addHandler <- function(handler, ..., logger = "") { if (is.character(logger)) { logger <- getLogger(logger) } ## this part has to be repeated here otherwise the called function ## will deparse the argument to 'handler', the formal name given ## here to the parameter if (is.character(handler)) { logger$addHandler(handler, ...) } else { handler_name <- deparse(substitute(handler)) logger$addHandler(handler = handler_name, action = handler, ...) } } #' @rdname handlers-management #' @export #' removeHandler <- function(handler, logger = "") { if (is.character(logger)) { logger <- getLogger(logger) } if (!is.character(handler)) { # handler was passed as its action handler <- deparse(substitute(handler)) } logger$removeHandler(handler) } #' #' Retrieves a handler from a logger. #' #' @description #' Handlers are not uniquely identified by their name. Only within the logger to which #' they are attached is their name unique. This function is here to allow you grab a #' handler from a logger so you can examine and alter it. #' #' @description #' Typical use of this function is in \code{setLevel(newLevel, getHandler(...))}. #' #' @param handler The name of the handler, or its action. #' @param logger Optional: the name of the logger. Defaults to the root logger. #' #' @return The retrieved handler object. It returns NULL if handler is not registerd. #' #' @examples #' logReset() #' addHandler(writeToConsole) #' getHandler("basic.stdout") #' #' @export #' getHandler <- function(handler, logger = "") { if (is.character(logger)) { logger <- getLogger(logger) } if (!is.character(handler)) { # handler was passed as its action handler <- deparse(substitute(handler)) } logger$getHandler(handler) } #' #' Set \var{logging.level} for the object. #' #' Alter an existing logger or handler, setting its \var{logging.level} to a new #' value. You can access loggers by name, while you must use \code{getHandler} to #' get a handler. #' #' @param level The new level for this object. Can be numeric or character. #' @param container a logger, its name or a handler. Default is root logger. #' #' @examples #' basicConfig() #' setLevel("FINEST") #' setLevel("DEBUG", getHandler("basic.stdout")) #' #' @export #' setLevel <- function(level, container = "") { if (is.null(container)) { stop("NULL container provided: cannot set level for NULL container") } if (is.character(container)) { container <- getLogger(container) } assign("level", namedLevel(level), container) } #' #' Sets message composer for logger. #' #' Message composer is used to compose log message out of formating string and arguments. #' It is function with signature \code{function(msg, ...)}. Formating message is passed under msg #' and formating arguments are passed as \code{...}. #' #' If message composer is not set default is in use (realized with \code{sprintf}). If message #' composer is not set for sub-logger, parent's message composer will be used. #' #' @param composer_f message composer function (type: function(msg, ...)) #' @param container name of logger to reser message composer for (type: character) #' #' @examples #' setMsgComposer(function(msg, ...) paste0("s-", msg, "-e")) #' loginfo("a message") # will log ' INFO::s-a message-e' #' resetMsgComposer() #' loginfo("a message") # will log ' INFO::a message' #' #' @export #' setMsgComposer <- function(composer_f, container = "") { if (is.null(container)) { stop("NULL container provided: cannot set message composer for NULL container") } if (is.character(container)) { container <- getLogger(container) } container$setMsgComposer(composer_f) assign("msg_composer", composer_f, container) } #' #' Resets previously set message composer. #' #' @param container name of logger to reser message composer for (type: character) #' #' @export #' resetMsgComposer <- function(container = "") { if (is.null(container)) { stop("NULL container provided: cannot resset message composer for NULL container") } if (is.character(container)) { container <- getLogger(container) } assign("msg_composer", function() NULL, container) } logging/R/updateOptions.R0000644000176200001440000000405013512626061015063 0ustar liggesusers## ## this is part of the logging package. the logging package is free ## software: you can redistribute it as well as modify it under the terms of ## the GNU General Public License as published by the Free Software ## Foundation, either version 3 of the License, or (at your option) any later ## version. ## ## this program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with the nens libraray. If not, see http://www.gnu.org/licenses/. ## ## Copyright (c) 2009..2013 by Mario Frasca ## #' #' Changes settings of logger or handler. #' #' @param container a logger, its name or a handler. #' @param ... options to set for the container. #' #' @export #' updateOptions <- function(container, ...) UseMethod("updateOptions") #' @describeIn updateOptions Update options for logger identified #' by name. #' @export #' updateOptions.character <- function(container, ...) { ## container is really just the name of the container updateOptions(getLogger(container), ...) } #' @describeIn updateOptions Update options of logger or handler #' passed by reference. #' @export #' updateOptions.environment <- function(container, ...) { ## the container is a logger config <- list(...) if ("level" %in% names(config)) { config$level <- namedLevel(config$level) } else if (get("name", container) == "") { # root logger config$level <- loglevels["INFO"] } else { config$level <- loglevels["NOTSET"] } for (key in names(config)) { if (key != "") { assign(key, config[[key]], container) } } invisible() } #' @describeIn updateOptions Update options of logger or handler #' passed by reference. #' @export #' updateOptions.Logger <- function(container, ...) { updateOptions.environment(container, ...) } logging/R/namedLevels.R0000644000176200001440000000340713512626061014471 0ustar liggesusers## ## this is part of the logging package. the logging package is free ## software: you can redistribute it as well as modify it under the terms of ## the GNU General Public License as published by the Free Software ## Foundation, either version 3 of the License, or (at your option) any later ## version. ## ## this program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with the nens libraray. If not, see http://www.gnu.org/licenses/. ## ## Copyright (c) 2009..2013 by Mario Frasca ## ## TODO: these constants must be documented #' #' The logging levels, names and values #' #' This list associates names to values and vice versa.\cr #' Names and values are the same as in the python standard logging module. #' #' @export #' loglevels <- c(NOTSET = 0, FINEST = 1, FINER = 4, FINE = 7, DEBUG = 10, INFO = 20, WARNING = 30, WARN = 30, ERROR = 40, CRITICAL = 50, FATAL = 50) namedLevel <- function(value) UseMethod("namedLevel") namedLevel.default <- function(value) { loglevels[1] } namedLevel.character <- function(value) { position <- which(names(loglevels) == value) if (length(position) == 0) { position <- 1 } loglevels[position][1] } namedLevel.numeric <- function(value) { position <- which(loglevels == value) if (length(position) == 0) { position <- 1 } loglevels[position][1] } logging/R/oo.R0000644000176200001440000001340313512626061012644 0ustar liggesusers## ## this is part of the logging package. the logging package is free ## software: you can redistribute it as well as modify it under the terms of ## the GNU General Public License as published by the Free Software ## Foundation, either version 3 of the License, or (at your option) any later ## version. ## ## this program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with the nens libraray. If not, see http://www.gnu.org/licenses/. ## ## Copyright (c) 2009..2013 by Mario Frasca ## Logger <- setRefClass( "Logger", fields = list( name = "character", handlers = "list", level = "numeric", msg_composer = "function"), methods = list( getParent = function() { # split the name on the '.' parts <- unlist(strsplit(name, ".", fixed = TRUE)) removed <- parts[-length(parts)] # except the last item parent_name <- paste(removed, collapse = ".") return(getLogger(parent_name)) }, getMsgComposer = function() { if (!is.null(msg_composer) && !is.null(functionBody(msg_composer))) { return(msg_composer) } if (name != "") { parent_logger <- getParent() return(parent_logger$getMsgComposer()) } return(defaultMsgCompose) }, setMsgComposer = function(composer_f) { if (!is.function(composer_f) || paste(formalArgs(composer_f), collapse = ", ") != "msg, ...") { stop(paste("message composer(passed as composer_f) must be function", " with signature function(msg, ...)")) } msg_composer <<- composer_f }, .deducelevel = function(initial_level = loglevels[["NOTSET"]]) { if (initial_level != loglevels[["NOTSET"]]) { # it's proper level (set: Not for inheritance) return(initial_level) } if (level != loglevels[["NOTSET"]]) { return(level) } if (name == "") { # assume it's FINEST, as root logger cannot inherit return(loglevels[["FINEST"]]) } # ask parent for level parent_logger <- getParent() return(parent_logger$.deducelevel()) }, .logrecord = function(record) { logger_level <- .deducelevel(level) if (record$level >= logger_level) { for (handler in handlers) { handler_level <- .deducelevel(with(handler, level)) if (record$level >= handler_level) { action <- with(handler, action) formatter <- with(handler, formatter) action(formatter(record), handler, record) } } } if (name != "") { parent_logger <- getParent() parent_logger$.logrecord(record) } invisible(TRUE) }, log = function(msglevel, msg, ...) { msglevel <- namedLevel(msglevel) if (msglevel < level) { return(invisible(FALSE)) } ## fine, we create the record and pass it to all handlers attached to the ## loggers from here up to the root. record <- list() composer_f <- getMsgComposer() record$msg <- composer_f(msg, ...) record$timestamp <- sprintf("%s", Sys.time()) record$logger <- name record$level <- msglevel record$levelname <- names(which(loglevels == record$level)[1]) ## cascade action in private method. .logrecord(record) }, setLevel = function(new_level) { new_level <- namedLevel(new_level) level <<- new_level }, getLevel = function() level, getHandler = function(handler) { if (!is.character(handler)) handler <- deparse(substitute(handler)) handlers[[handler]] }, removeHandler = function(handler) { if (!is.character(handler)) # handler was passed as its action handler <- deparse(substitute(handler)) handlers <<- handlers[!(names(handlers) == handler)] }, addHandler = function(handler, ..., level = 0, formatter = defaultFormat) { handler_env <- new.env() if (is.character(handler)) { ## first parameter is handler name handler_name <- handler ## and hopefully action is in the dots params <- list(...) if ("action" %in% names(params)) { the_action <- params[["action"]] } else if (length(params) > 0 && is.null(names(params)[[1]])) { the_action <- params[[1]] } else { stop("No action for the handler provided") } assign("action", the_action, handler_env) } else { ## first parameter is handler action, from which we extract the name updateOptions.environment(handler_env, action = handler) handler_name <- deparse(substitute(handler)) } updateOptions.environment(handler_env, ...) assign("level", namedLevel(level), handler_env) assign("formatter", formatter, handler_env) removeHandler(handler_name) if (with(handler_env, action)(NA, handler_env, dry = TRUE) == TRUE) { handlers[[handler_name]] <<- handler_env } }, finest = function(...) log(loglevels["FINEST"], ...), finer = function(...) log(loglevels["FINER"], ...), fine = function(...) log(loglevels["FINE"], ...), debug = function(...) log(loglevels["DEBUG"], ...), info = function(...) log(loglevels["INFO"], ...), warn = function(...) log(loglevels["WARN"], ...), error = function(...) log(loglevels["ERROR"], ...) ) # methods ) # setRefClass logging/R/zzz.R0000644000176200001440000000567613512626061013101 0ustar liggesusers## ## this is part of the logging package. the logging package is free ## software: you can redistribute it as well as modify it under the terms of ## the GNU General Public License as published by the Free Software ## Foundation, either version 3 of the License, or (at your option) any later ## version. ## ## this program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ## General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with the nens libraray. If not, see http://www.gnu.org/licenses/. ## ## Copyright (c) 2009..2013 by Mario Frasca ## #' @import methods NULL #' A tentative logging package. #' #' @description #' A logging package emulating the Python logging package. #' #' @description #' What you find here behaves similarly to what you also find in Python. #' This includes hierarchic loggers, multiple handlers at each logger, #' the possibility to specify a formatter for each handler (one default #' formatter is given), same levels (names and numeric values) as Python's #' logging package, a simple logging.BasicConfig function to quickly put #' yourself in a usable situation... #' #' This package owes a lot to my employer, r-forge, the stackoverflow community, #' Brian Lee Yung Rowe's futile package (v1.1) and the documentation of #' the Python logging package. #' #' @details #' Index: #' \describe{ #' \item{\code{basicConfig}}{bootstrapping the logging package} #' \item{\code{addHandler}}{add a handler to a logger} #' \item{\code{getLogger}}{set defaults and get current values for a logger} #' \item{\code{removeHandler}}{remove a handler from a logger} #' \item{\code{setLevel}}{set logging.level for a logger} #' } #' #' To use this package, include logging instructions in your code, possibly #' like this: #' #' \code{library(logging)}\cr #' \code{basicConfig()}\cr #' \code{addHandler(writeToFile, logger="company", file="sample.log")}\cr #' \code{loginfo("hello world", logger="")}\cr #' \code{logwarn("hello company", logger="company.module")} #' #' The \code{basicConfig} function adds a console handler to the root logger, #' while the explicitly called \code{addHandler} adds a file handler to the #' 'company' logger. the effect of the above example is two lines on the #' console and just one line in the \code{sample.log} file. #' #' The web pages for this package on r-forge are kept decently up to date #' and contain a usable tutorial. Check the references. #' #' @references #' the python logging module: \url{http://docs.python.org/library/logging.html}\cr #' this package at github: \url{https://github.com/WLOGSolutions/r-logging/} #' #' @docType package #' @name logging-package #' @keywords package NULL ## create the logging environment logging.options <- new.env() logging/MD50000644000176200001440000000310213512631413012203 0ustar liggesusersa8d1146e0f751539a31b108f059b5954 *DESCRIPTION a80dce85e3d9704d266e58f0f4a074c5 *NAMESPACE 60a41839c06c596bee9f0637aa9f4feb *NEWS.md bf16b69f726cafe89cc216371917cca9 *R/logger.R e49f76f00ca6411a6e02df8df2d0506c *R/namedLevels.R 8b19a1a59016ccb6da83d8f8c0ab0d60 *R/oo.R 70bc803dfba9a33f23d6542a26dbf55d *R/updateOptions.R 98c27392cc3e2dd0447ce0f828e33295 *R/utils.R ca505851d5c0649dd9f14a1fc3b756bf *R/zzz.R 092bbc3a9b38edd9071bbfa0ba321590 *man/bootstrapping.Rd 363b650f0bf8979e764e83fdfe3d78ba *man/getHandler.Rd 32e67bb3bdacaf88d3964ad6fe95d07f *man/getLogger.Rd 2a4612aaba896c5895a3211a00f4f009 *man/handlers-management.Rd c671356b9ede8b516bd345588796a730 *man/inbuilt-actions.Rd aeb822d8f2b792408822fa0143528d63 *man/logging-entrypoints.Rd 40602824c726ef3f2b2d26c5733b012e *man/logging-package.Rd 9148058d0fc592892a4d02895a57a803 *man/loglevels.Rd a51604309104de96673615502c912d97 *man/resetMsgComposer.Rd 5f97dd813745ecaf5a71121e7b362871 *man/setLevel.Rd 42284bf7bd86d954e4629e6ac4919c13 *man/setMsgComposer.Rd c89e7939b75d470386bbfa7d492b4b71 *man/updateOptions.Rd 100cc5d5a65b62f237c1167743b6b5f6 *tests/run_tests.R efc0db024732ee9cbbf0bc8fcbd83d60 *tests/testthat.R c1a4499985819751ca10e95c9a13502a *tests/testthat/test.automatic-format.R 6524e9b0c5267cc66441a19cc48cf02e *tests/testthat/test.data.interaction.R b20183077adb74d9ebb9bb28041ab64a *tests/testthat/test.format-limitations.R 1ba29522bec7fe30e1cf38b556fad607 *tests/testthat/test.handlers.R c25d9265fc73c3611b3688b84c43a8f3 *tests/testthat/test.level-inheritance.R cf2bafbcc813c74fc00804fa72a9fca8 *tests/testthat/test.msg-composer.R logging/DESCRIPTION0000644000176200001440000000165713512631413013416 0ustar liggesusersPackage: logging Version: 0.10-108 Title: R Logging Package Authors@R: c( person("Mario", "Frasca", email = "mariotomo@gmail.com", role = c("aut")), person("Walerian", "Sokolowski", email = "r-logging@wlogsolutions.com", role = c("cre")) ) Description: Pure R implementation of the ubiquitous log4j package. It offers hierarchic loggers, multiple handlers per logger, level based filtering, space handling in messages and custom formatting. URL: https://github.com/WLOGSolutions/r-logging BugReports: https://github.com/WLOGSolutions/r-logging/issues License: GPL-3 Encoding: UTF-8 Language: en-US Depends: R (>= 3.2.0) Imports: methods Suggests: testthat, crayon RoxygenNote: 6.1.1 NeedsCompilation: no Packaged: 2019-07-14 13:20:49 UTC; ws171913 Author: Mario Frasca [aut], Walerian Sokolowski [cre] Maintainer: Walerian Sokolowski Repository: CRAN Date/Publication: 2019-07-14 13:50:03 UTC logging/man/0000755000176200001440000000000013512626061012455 5ustar liggesuserslogging/man/loglevels.Rd0000644000176200001440000000067213512626061014745 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/namedLevels.R \docType{data} \name{loglevels} \alias{loglevels} \title{The logging levels, names and values} \format{An object of class \code{numeric} of length 11.} \usage{ loglevels } \description{ This list associates names to values and vice versa.\cr Names and values are the same as in the python standard logging module. } \keyword{datasets} logging/man/setLevel.Rd0000644000176200001440000000124713512626061014533 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/logger.R \name{setLevel} \alias{setLevel} \title{Set \var{logging.level} for the object.} \usage{ setLevel(level, container = "") } \arguments{ \item{level}{The new level for this object. Can be numeric or character.} \item{container}{a logger, its name or a handler. Default is root logger.} } \description{ Alter an existing logger or handler, setting its \var{logging.level} to a new value. You can access loggers by name, while you must use \code{getHandler} to get a handler. } \examples{ basicConfig() setLevel("FINEST") setLevel("DEBUG", getHandler("basic.stdout")) } logging/man/setMsgComposer.Rd0000644000176200001440000000204213512626061015714 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/logger.R \name{setMsgComposer} \alias{setMsgComposer} \title{Sets message composer for logger.} \usage{ setMsgComposer(composer_f, container = "") } \arguments{ \item{composer_f}{message composer function (type: function(msg, ...))} \item{container}{name of logger to reser message composer for (type: character)} } \description{ Message composer is used to compose log message out of formating string and arguments. It is function with signature \code{function(msg, ...)}. Formating message is passed under msg and formating arguments are passed as \code{...}. } \details{ If message composer is not set default is in use (realized with \code{sprintf}). If message composer is not set for sub-logger, parent's message composer will be used. } \examples{ setMsgComposer(function(msg, ...) paste0("s-", msg, "-e")) loginfo("a message") # will log ' INFO::s-a message-e' resetMsgComposer() loginfo("a message") # will log ' INFO::a message' } logging/man/updateOptions.Rd0000644000176200001440000000167013512626061015606 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/updateOptions.R \name{updateOptions} \alias{updateOptions} \alias{updateOptions.character} \alias{updateOptions.environment} \alias{updateOptions.Logger} \title{Changes settings of logger or handler.} \usage{ updateOptions(container, ...) \method{updateOptions}{character}(container, ...) \method{updateOptions}{environment}(container, ...) \method{updateOptions}{Logger}(container, ...) } \arguments{ \item{container}{a logger, its name or a handler.} \item{...}{options to set for the container.} } \description{ Changes settings of logger or handler. } \section{Methods (by class)}{ \itemize{ \item \code{character}: Update options for logger identified by name. \item \code{environment}: Update options of logger or handler passed by reference. \item \code{Logger}: Update options of logger or handler passed by reference. }} logging/man/logging-package.Rd0000644000176200001440000000406513512626061015770 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/zzz.R \docType{package} \name{logging-package} \alias{logging-package} \title{A tentative logging package.} \description{ A logging package emulating the Python logging package. What you find here behaves similarly to what you also find in Python. This includes hierarchic loggers, multiple handlers at each logger, the possibility to specify a formatter for each handler (one default formatter is given), same levels (names and numeric values) as Python's logging package, a simple logging.BasicConfig function to quickly put yourself in a usable situation... This package owes a lot to my employer, r-forge, the stackoverflow community, Brian Lee Yung Rowe's futile package (v1.1) and the documentation of the Python logging package. } \details{ Index: \describe{ \item{\code{basicConfig}}{bootstrapping the logging package} \item{\code{addHandler}}{add a handler to a logger} \item{\code{getLogger}}{set defaults and get current values for a logger} \item{\code{removeHandler}}{remove a handler from a logger} \item{\code{setLevel}}{set logging.level for a logger} } To use this package, include logging instructions in your code, possibly like this: \code{library(logging)}\cr \code{basicConfig()}\cr \code{addHandler(writeToFile, logger="company", file="sample.log")}\cr \code{loginfo("hello world", logger="")}\cr \code{logwarn("hello company", logger="company.module")} The \code{basicConfig} function adds a console handler to the root logger, while the explicitly called \code{addHandler} adds a file handler to the 'company' logger. the effect of the above example is two lines on the console and just one line in the \code{sample.log} file. The web pages for this package on r-forge are kept decently up to date and contain a usable tutorial. Check the references. } \references{ the python logging module: \url{http://docs.python.org/library/logging.html}\cr this package at github: \url{https://github.com/WLOGSolutions/r-logging/} } \keyword{package} logging/man/resetMsgComposer.Rd0000644000176200001440000000061013512626061016242 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/logger.R \name{resetMsgComposer} \alias{resetMsgComposer} \title{Resets previously set message composer.} \usage{ resetMsgComposer(container = "") } \arguments{ \item{container}{name of logger to reser message composer for (type: character)} } \description{ Resets previously set message composer. } logging/man/logging-entrypoints.Rd0000644000176200001440000000250513512626061016770 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/logger.R \name{logging-entrypoints} \alias{logging-entrypoints} \alias{logdebug} \alias{logfinest} \alias{logfiner} \alias{logfine} \alias{loginfo} \alias{logwarn} \alias{logerror} \alias{levellog} \title{Entry points for logging actions} \usage{ logdebug(msg, ..., logger = "") logfinest(msg, ..., logger = "") logfiner(msg, ..., logger = "") logfine(msg, ..., logger = "") loginfo(msg, ..., logger = "") logwarn(msg, ..., logger = "") logerror(msg, ..., logger = "") levellog(level, msg, ..., logger = "") } \arguments{ \item{msg}{the textual message to be output, or the format for the \dots arguments} \item{...}{if present, msg is interpreted as a format and the \dots values are passed to it to form the actual message.} \item{logger}{the name of the logger to which we pass the record} \item{level}{The logging level} } \description{ Generate a log record and pass it to the logging system.\cr } \details{ A log record gets timestamped and will be independently formatted by each of the handlers handling it.\cr Leading and trailing whitespace is stripped from the final message. } \examples{ logReset() addHandler(writeToConsole) loginfo('this goes to console') logdebug('this stays silent') } logging/man/inbuilt-actions.Rd0000644000176200001440000000377013512626061016057 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R \name{inbuilt-actions} \alias{inbuilt-actions} \alias{writeToConsole} \alias{writeToFile} \title{Predefined(sample) handler actions} \usage{ writeToConsole(msg, handler, ...) writeToFile(msg, handler, ...) } \arguments{ \item{msg}{A formated message to handle.} \item{handler}{The handler environment containing its options. You can register the same action to handlers with different properties.} \item{...}{parameters provided by logger system to interact with the action.} } \description{ When you define a handler, you specify its name and the associated action. A few predefined actions described below are provided. } \details{ A handler action is a function that accepts a formated message and handler configuration. Messages passed are filtered already regarding loglevel. \dots parameters are used by logging system to interact with the action. \dots can contain \var{dry} key to inform action that it meant to initialize itself. In the case action should return TRUE if initialization succeded. If it's not a dry run \dots contain the whole preformated \var{logging.record}. A \var{logging.record} is a named list and has following structure: \describe{ \item{msg}{contains the real formatted message} \item{level}{message level as numeric} \item{levelname}{message level name} \item{logger}{name of the logger that generated it} \item{timestamp}{formatted message timestamp} } \code{writeToConsole} detects if crayon package is available and uses it to color messages. The coloring can be switched off by means of configuring the handler with \var{color_output} option set to FALSE. \code{writeToFile} action expects file path to write to under \var{file} key in handler options. } \examples{ ## define your own function and register it with a handler. ## author is planning a sentry client function. please send ## any interesting function you may have written! } logging/man/getLogger.Rd0000644000176200001440000000147513512626061014672 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/logger.R \name{getLogger} \alias{getLogger} \title{Set defaults and get the named logger.} \usage{ getLogger(name = "", ...) } \arguments{ \item{name}{The name of the logger} \item{...}{Any properties you may want to set in the newly created logger. These have no effect if the logger is already present.} } \value{ The logger retrieved or registered. } \description{ Make sure a logger with a specific name exists and return it as a \var{Logger} S4 object. if not yet present, the logger will be created and given the values specified in the \dots arguments. } \examples{ getLogger() # returns the root logger getLogger('test.sub') # constructs a new logger and returns it getLogger('test.sub') # returns it again } logging/man/getHandler.Rd0000644000176200001440000000154513512626061015026 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/logger.R \name{getHandler} \alias{getHandler} \title{Retrieves a handler from a logger.} \usage{ getHandler(handler, logger = "") } \arguments{ \item{handler}{The name of the handler, or its action.} \item{logger}{Optional: the name of the logger. Defaults to the root logger.} } \value{ The retrieved handler object. It returns NULL if handler is not registerd. } \description{ Handlers are not uniquely identified by their name. Only within the logger to which they are attached is their name unique. This function is here to allow you grab a handler from a logger so you can examine and alter it. Typical use of this function is in \code{setLevel(newLevel, getHandler(...))}. } \examples{ logReset() addHandler(writeToConsole) getHandler("basic.stdout") } logging/man/handlers-management.Rd0000644000176200001440000000350113512626061016655 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/logger.R \name{handlers-management} \alias{handlers-management} \alias{addHandler} \alias{removeHandler} \title{Add a handler to or remove one from a logger.} \usage{ addHandler(handler, ..., logger = "") removeHandler(handler, logger = "") } \arguments{ \item{handler}{The name of the handler, or its action} \item{...}{Extra parameters, to be stored in the handler list \dots may contain extra parameters that will be passed to the handler action. Some elements in the \dots will be interpreted here.} \item{logger}{the name of the logger to which to attach the new handler, defaults to the root logger.} } \description{ Use this function to maintain the list of handlers attached to a logger.\cr \cr \code{addHandler} and \code{removeHandler} are also offered as methods of the \var{Logger} S4 class. } \details{ Handlers are implemented as environments. Within a logger a handler is identified by its \var{name} and all handlers define at least the three variables: \describe{ \item{level}{all records at level lower than this are skipped.} \item{formatter}{a function getting a record and returning a string} \item{\code{action(msg, handler)}}{a function accepting two parameters: a formatted log record and the handler itself. making the handler a parameter of the action allows us to have reusable action functions.} } Being an environment, a handler may define as many variables as you think you need. keep in mind the handler is passed to the action function, which can check for existence and can use all variables that the handler defines. } \examples{ logReset() addHandler(writeToConsole) names(getLogger()[["handlers"]]) loginfo("test") removeHandler("writeToConsole") logwarn("test") } logging/man/bootstrapping.Rd0000644000176200001440000000271013512626061015637 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/logger.R \name{bootstrapping} \alias{bootstrapping} \alias{basicConfig} \alias{logReset} \title{Bootstrapping the logging package.} \usage{ basicConfig(level = 20) logReset() } \arguments{ \item{level}{The logging level of the root logger. Defaults to INFO. Please do notice that this has no effect on the handling level of the handler that basicConfig attaches to the root logger.} } \description{ \code{basicConfig} and \code{logReset} provide a way to put the logging package in a know initial state. } \details{ \code{basicConfig} creates the root logger, attaches a console handler(by \var{basic.stdout} name) to it and sets the level of the handler to \code{level}. You must not call \code{basicConfig} to for logger to work any more: then root logger is created it gets initialized by default the same way as \code{basicConfig} does. If you need clear logger to fill with you own handlers use \code{logReset} to remove all default handlers. \code{logReset} reinitializes the whole logging system as if the package had just been loaded except it also removes all default handlers. Typically, you would want to call \code{basicConfig} immediately after a call to \code{logReset}. } \examples{ basicConfig() logdebug("not shown, basic is INFO") logwarn("shown and timestamped") logReset() logwarn("not shown, as no handlers are present after a reset") }