Skip to content

Commit

Permalink
Writing documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
Neal Fultz committed Feb 25, 2015
1 parent 80c954e commit 3a1977b
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 29 deletions.
3 changes: 3 additions & 0 deletions 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)
99 changes: 78 additions & 21 deletions 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
Expand All @@ -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);

Expand All @@ -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
}
16 changes: 14 additions & 2 deletions 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:

Expand All @@ -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
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)
8 changes: 7 additions & 1 deletion man/autolog-package.Rd
Expand Up @@ -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}}
}

33 changes: 28 additions & 5 deletions man/autolog.Rd
Expand Up @@ -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
Expand Down
44 changes: 44 additions & 0 deletions 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.
}

0 comments on commit 3a1977b

Please sign in to comment.