# Logging using function as decorator in Guava

I needed to add a logger into my flow based on FluentIterable from the Guava library. I have a lot of functions and in some cases I would like to add logging when in other cases (for the same function) I would not.

Of course the natural solution for this problem is to use the decorator pattern, so I created something like this:

/**
* Decorator function that add logging into function passed as parameter.
*
* @param <I> input type
* @param <O> output type.
*/
public class DecoratorLoggerInfoFunc<I, O> implements Function<I, O> {

/**
* Original function
*/
private final Function<I, O> function;

/**
* Current logger.
*/
private final Logger logger;

/**
* "Builder" that takes function and return function decorated by logger.
*
* @param function function to decorate.
* @param <I>      input type
* @param <O>      output type
* @return function with logger.
*/
public static <I, O> Function<I, O> of(Function<I, O> function) {
checkNotNull(function, "Function to decorate cannot be null");
return new DecoratorLoggerInfoFunc<I, O>(function);
}

/**
* Initialize logger!
*
* @param function original function.
*/
private DecoratorLoggerInfoFunc(Function<I, O> function) {
this.function = function;
this.logger = LoggerFactory.getLogger(function.getClass());
}

/**
* Flow:
* <ul>
* <li>Call original function</li>
* <li>Return output of original function</li>
* </ul>
*
* @param input
* @return
*/
@Override
public O apply(I input) {
O output = function.apply(input);
logger.info("Input is {} Output is {}", input, output);
return output;
}
}


Is this code a good way to solve my problem? Is there a better way to do this?

The concept seems fine to me. The problem is that you will still need to decide whether to decorate, or not decorate the function, and that is done outside the function. That makes it a once-per-create decision, which is OK, but you may still end up with a lot of conditions in your code:

Function f = logFunction() ? new DecoratorLoggerInfoFunc(f) : f;


The actual apply method is not very robust though. I would include logging of any potential exceptions that are not declared to be thrown on the function itself...:

public O apply(I input) {
try {
O output = function.apply(input);
logger.info("Input is {} Output is {}", input, output);
return output;
} catch (RuntimeException e) {
logger.warn("Exception running with input {}", input, e);
throw e;
}
}