Monday, December 13, 2010

Correct SLF4J logger wrapping

The problem

As much as the idea pains me, I find it necessary to wrap the well-known logger wrapping framework SLF4J. Why?

The framework author goes to lengths to support JDK1.4 in the API, a reasonable goal for a broadly used API. The result is signatures such as this:

public interface Logger {
    /* ... */

    void info(String msg);

    void info(String format, Object arg);

    void info(String format, Object arg1, Object arg2);

    void info(String format, Object[] args);

    void info(String format, Throwable t);

    /* ... */
}

That's it. You get 0-2 parameters to put into your formatted mesage. If you have more than two parameters you are out of luck. An no mixing exceptions with formatted messages. However, were SLF4J coded against JDK5 the API might become:

public interface Logger {
    /* ... */

    void info(String msg, Object... args);

    /* NB - ellipsis parameter must be last. */
    void info(Throwable t, String msg, Object... args);

    /* ... */
}

Yes, both simpler and more functional. But not a an option without maintaining two versions of the code base or losing backwards compatibility.

A solution

In the past I worked around this with some pretty wild solutions, but today I ran across Wrapping the slf4j API, posted in August.

I can fix SLF4J for JDK5 myself:

public class LoggerBase {
    private static final String FQCN = LoggerBase.class.getName();

    private final Logger logger;

    public LoggerBase(Class thisClass) {
        this.logger = getLogger(thisClass);
    }

    public LoggerBase(String loggerName) {
        this.logger = getLogger(loggerName);
    }

    /* ... */

    public void info(String format, Object... args) {
        info(null, format, args);
    }

    public void info(Throwable thrown, String format, Object... args) {
        if (!logger.isInfoEnabled())
            return;

        if (logger instanceof LocationAwareLogger)
            ((LocationAwareLogger) logger)
                    .log(null, FQCN, INFO_INT, format, args, thrown);
        else
            logger.info(arrayFormat(format, args).getMessage(), thrown);
    }

    /* ... */
}

Hopefully SLF4J presents a solution for this (perhaps a separate JDK5 jar) so I can drop my warped logger wrapper wrapping class.

No comments: