From 3a1977b586153d5ccc1680f30a11d9f57d6b6317 Mon Sep 17 00:00:00 2001 From: Neal Fultz Date: Wed, 25 Feb 2015 23:12:31 +0000 Subject: [PATCH] Writing documentation. --- NAMESPACE | 3 ++ R/autolog.R | 99 +++++++++++++++++++++++++++++++++--------- README.md | 16 ++++++- man/autolog-package.Rd | 8 +++- man/autolog.Rd | 33 +++++++++++--- man/wrap.Rd | 44 +++++++++++++++++++ 6 files changed, 174 insertions(+), 29 deletions(-) create mode 100644 man/wrap.Rd diff --git a/NAMESPACE b/NAMESPACE index 7438540..5024272 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,3 +1,6 @@ # Generated by roxygen2 (4.1.0): do not edit by hand export(autolog) +export(is.autologged) +export(unwrap) +export(wrap) diff --git a/R/autolog.R b/R/autolog.R index da329cf..b51b9c1 100644 --- a/R/autolog.R +++ b/R/autolog.R @@ -1,10 +1,14 @@ #' Automatic Logging #' +#' This package provides a one-liner way to add logging to an entire package or R session. +#' +#' @author Neal Fultz \email{njf@@zestfinance.com} #' @name autolog-package #' @docType package +#' @seealso \code{\link{autolog}}, \code{\link{wrap}} NULL -# Class name for wrapped fns +# Attribute name for wrapped fns .C = "autologger"; # Package variable, used for tabbing function calls over @@ -13,18 +17,38 @@ NULL #' Automatic Logging #' -#' @param e an environment to process; default to Global -#' @param logger a logging function accepting \code{...} -#' @param verbose verbose output of what functions are being instrumented +#' Call \code{\link{wrap}} on each function in an environment and assign the result back. +#' +#' @param e an environment to process; defaults to the \code{\link{.GlobalEnv}} +#' @param logger a logging function or name of function which accepts \code{...} +#' @param verbose logical, log which functions are detected and modified +#' +#' @section Options: +#' You can set the following default parameters using \code{\link{option}}: +#' \describe{ +#' \item{autolog.logger}{ name of a logging function} +#' \item{autolog.verbose}{ logical } +#' } #' +#' @section Logging a package: +#' +#' If you would like to add logging to an entire package, add the following to \code{R/zzz.R} in your package: +#' \preformatted{ +#' if(getOption("autologging", FALSE) && require(autolog)) autolog(environment()) +#' } +#' This will be run on package load and add logging to every function in the package, including +#' non-exported functions. To activate it, +#' \preformatted{ +#' options(autologging=TRUE) # Set *before* you load the pkg +#' library(mypkg) +#' } #' @export #' @examples #' f <- function(a,b) a / b #' zzz <- function(x,y) f(x,y) / f(y,x) #' autolog(environment(), verbose=TRUE) #' zzz(2,1) - -autolog <- function(e = .GlobalEnv, logger="message", verbose=getOption("autolog.verbose", FALSE)) { +autolog <- function(e = .GlobalEnv, logger=getOption("autolog.logger", "message"), verbose=getOption("autolog.verbose", FALSE)){ logger <- match.fun(logger); @@ -33,45 +57,78 @@ autolog <- function(e = .GlobalEnv, logger="message", verbose=getOption("autolog for(i in objNames) { x <- get(i, e); if(!is.function(x)) next; - if(class(x) == .C) { + if(is.autologged(x)) { if(verbose) logger("skipping\t", i); next } if(verbose) logger("wrapping\t", i); - assign(i, wrap(x, logger), e); + assign(i, wrap(x, logger), envir=e); } - e$.AUTOLOG <- TRUE; } +#' Wrap a function in logging code +#' +#' Create a logged copy of a function. Every time the new function is called, all three functions are called in order: +#' \enumerate{ +#' \item \code{pre} +#' \item \code{f} +#' \item \code{post} +#' } +#' +#' @param f a function to decorate +#' @param pre a function, to be called before \code{f} +#' @param post a function, to be called after \code{f} +#' +#' @details +#' +#' Wrapped functions carry an \dQuote{autologged} attribute, which can be tested for using \code{is.autologged}. The original function \code{f} can be extracted +#' using \code{unwrap}. +#' +#' +#' +#' @seealso \url{http://en.wikipedia.org/wiki/Decorator_pattern} and \code{\link[memoise]{memoise}} for another example of \dQuote{decorator} functions. +#' @export +#' @examples +#' f <- wrap(sum, message) +#' f(1:10) +#' is.autologged(f) +#' f <- unwrap(f) +#' f(1:10) +wrap <- function(f, pre, post=pre) { -wrap <- function(x, logger) { - - # Bug 1: make sure x is forced, R is too lazy, it will infinitely recur on the final function in the loop above if one function calls another. - force(x); - force(logger); + # Bug 1: make sure f is forced, R is too lazy, it will infinitely recur on the final function in the loop above if one function calls another. + force(f); + force(pre); + force(post); #using `class<-` to keep local environment clean - `class<-`( + `attr<-`( function(...) { txt <- deparse(sys.call()); .a$depth <- .a$depth + 1; - logger(Sys.time(), rep("\t", .a$depth), txt, " enter" ); + pre(Sys.time(), rep("\t", .a$depth), txt, " begin" ); on.exit( { - logger(Sys.time(), rep("\t", .a$depth), txt, " exit"); + post(Sys.time(), rep("\t", .a$depth), txt, " end"); .a$depth <- .a$depth - 1; }) - x(...); + f(...); }, - .C + .C, TRUE ) } -unwrap <- function(x) { - if(class(x) == .C) environment(x)$x else x +#' @rdname wrap +#' @export +is.autologged <- function(f) identical(attr(f, .C), TRUE) + +#' @rdname wrap +#' @export +unwrap <- function(f) { + if(is.autologged(f)) environment(f)$f else f } diff --git a/README.md b/README.md index 8d6ccec..5e46eed 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Autologger: Pain Free Logging -This package provides the `autolog` function, which instruments all functions in an environment to let you follow along with the execution. +This package provides the `autolog` function, which instruments all functions in a session or package to let you follow along with the execution. Here is an example: @@ -18,4 +18,16 @@ Here is an example: 2015-02-24 18:55:10 f(x, y) exit 2015-02-24 18:55:10 f(y, x) enter 2015-02-24 18:55:10 f(y, x) exit - 2015-02-24 18:55:10 zzz(2, 1) exit \ No newline at end of file + 2015-02-24 18:55:10 zzz(2, 1) exit + +## Logging a package + +If you would like to add logging to an entire package, add the following to `R/zzz.R`: + + if(getOption("autologging", FALSE) && require(autolog)) autolog(environment()) + +This will be run on package load and add logging to every function in the package, including +non-exported functions. To activate it, + + options(autologging=TRUE) # Set *before* you load the pkg + library(mypkg) \ No newline at end of file diff --git a/man/autolog-package.Rd b/man/autolog-package.Rd index d4156ab..1706cb3 100644 --- a/man/autolog-package.Rd +++ b/man/autolog-package.Rd @@ -5,6 +5,12 @@ \alias{autolog-package} \title{Automatic Logging} \description{ -Automatic Logging +This package provides a one-liner way to add logging to an entire package or R session. +} +\author{ +Neal Fultz \email{njf@zestfinance.com} +} +\seealso{ +\code{\link{autolog}}, \code{\link{wrap}} } diff --git a/man/autolog.Rd b/man/autolog.Rd index 413054c..f1269d3 100644 --- a/man/autolog.Rd +++ b/man/autolog.Rd @@ -4,18 +4,41 @@ \alias{autolog} \title{Automatic Logging} \usage{ -autolog(e = .GlobalEnv, logger = "message", +autolog(e = .GlobalEnv, logger = getOption("autolog.logger", "message"), verbose = getOption("autolog.verbose", FALSE)) } \arguments{ -\item{e}{an environment to process; default to Global} +\item{e}{an environment to process; defaults to the \code{\link{.GlobalEnv}}} -\item{logger}{a logging function accepting \code{...}} +\item{logger}{a logging function or name of function which accepts \code{...}} -\item{verbose}{verbose output of what functions are being instrumented} +\item{verbose}{logical, log which functions are detected and modified} } \description{ -Automatic Logging +Call \code{\link{wrap}} on each function in an environment and assign the result back. +} +\section{Options}{ + +You can set the following default parameters using \code{\link{option}}: +\describe{ +\item{autolog.logger}{ name of a logging function} +\item{autolog.verbose}{ logical } +} +} + +\section{Logging a package}{ + + +If you would like to add logging to an entire package, add the following to \code{R/zzz.R} in your package: +\preformatted{ + if(getOption("autologging", FALSE) && require(autolog)) autolog(environment()) +} +This will be run on package load and add logging to every function in the package, including +non-exported functions. To activate it, +\preformatted{ + options(autologging=TRUE) # Set *before* you load the pkg + library(mypkg) +} } \examples{ f <- function(a,b) a / b diff --git a/man/wrap.Rd b/man/wrap.Rd new file mode 100644 index 0000000..ab113f1 --- /dev/null +++ b/man/wrap.Rd @@ -0,0 +1,44 @@ +% Generated by roxygen2 (4.1.0): do not edit by hand +% Please edit documentation in R/autolog.R +\name{wrap} +\alias{is.autologged} +\alias{unwrap} +\alias{wrap} +\title{Wrap a function in logging code} +\usage{ +wrap(f, pre, post = pre) + +is.autologged(f) + +unwrap(f) +} +\arguments{ +\item{f}{a function to decorate} + +\item{pre}{a function, to be called before \code{f}} + +\item{post}{a function, to be called after \code{f}} +} +\description{ +Create a logged copy of a function. Every time the new function is called, all three functions are called in order: +\enumerate{ +\item \code{pre} +\item \code{f} +\item \code{post} +} +} +\details{ +Wrapped functions carry an \dQuote{autologged} attribute, which can be tested for using \code{is.autologged}. The original function \code{f} can be extracted +using \code{unwrap}. +} +\examples{ +f <- wrap(sum, message) +f(1:10) +is.autologged(f) +f <- unwrap(f) +f(1:10) +} +\seealso{ +\url{http://en.wikipedia.org/wiki/Decorator_pattern} and \code{\link[memoise]{memoise}} for another example of \dQuote{decorator} functions. +} +