Sunday, April 22, 2012

A common challenge in Java is dealing with various logging frameworks. Various different libraries depend on different logging implementations and things can messy very fast.

The issues are compounded by the various inconsistencies with logging conventions, names of things and configurations.

Lately I've been working on a simple ORM and one of the requirements I decided early on would be that this new library would have no external dependencies.  However, I did want to be able to log things if logging was available.

My solution was to write a simple wrapper class that would hide the run-time dependencies of the various loggers.

It's used in a similar way to other loggers for example:

class SomeClass {
   private static final Log log = Log.getLogger( SomeClass.class);

   void someMethod() {
        try {
            log.info("hello");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
   }
}

The Log class detects the presence of SLF4J or LOG4J and uses common method conventions, debug, info, warn, error for logging. If it doesn't find either it falls back to the java.util.logging library.

You can copy this class into your own projects using your own package convention and then you won't need to have SLF4J or LOG4J imports in your Java source files.

Here's the source:


package <your package goes here>;


import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * Logging wrapper to avoid runtime dependencies.
 * It will use slf4j if available or log4j if available otherwise it falls back to JUL (java.util.logging).
 * 
 * JUL will use the default java logging.properties
 * log4j will use log4j.properties
 * slf4j will use logback.xml
 * 
 * log4j DEBUG maps to JUL FINE
 * log4j ERROR maps to JUL SEVERE
 * 
 * The class includes an overloaded error method to log stack traces as well as the message.
 *
 * @author Dan Howard
 * @since 4/21/12 6:47 AM
 */
public class Log {


    private Log() {
    }


    enum LogMode {
        SLF4J, LOG4J, JUL;
    }


    private static LogMode logMode;


    static {
        try {
            Class.forName("org.slf4j.Logger");
            logMode = LogMode.SLF4J;
        } catch (ClassNotFoundException e) {
        }


        if (logMode == null) {
            try {
                Class.forName("org.apache.log4j.Logger");
                logMode = LogMode.LOG4J;
            } catch (ClassNotFoundException e) {
                logMode = LogMode.JUL;
            }
        }


        assert logMode != null;
    }


    private static final Map<String, Log> loggers = new ConcurrentHashMap<String, Log>(12);


    private String logName;


    public Log(String logName) {
        this.logName = logName;
    }


    public static Log getLogger(Class logName) {
        return getLogger(logName.getName());
    }


    public static Log getLogger(String logName) {
        if (loggers.containsKey(logName)) {
            return loggers.get(logName);
        }
        Log log = new Log(logName);
        loggers.put(logName, log);
        return log;
    }




    public boolean isDebugEnabled() {
        switch (logMode) {
            case SLF4J:
                return org.slf4j.LoggerFactory.getLogger(logName).isDebugEnabled();
            case LOG4J:
                return org.apache.log4j.Logger.getLogger(logName).isDebugEnabled();
            default:
                return java.util.logging.Logger.getLogger(logName).isLoggable(java.util.logging.Level.FINE);
        }
    }


    public void debug(Object message) {
        switch (logMode) {
            case SLF4J:
                org.slf4j.LoggerFactory.getLogger(logName).debug("" + message);
                break;
            case LOG4J:
                org.apache.log4j.Logger.getLogger(logName).debug(message);
                break;
            case JUL:
                java.util.logging.Logger.getLogger(logName).fine("" + message);
        }
    }


    public void info(Object message) {
        switch (logMode) {
            case SLF4J:
                org.slf4j.LoggerFactory.getLogger(logName).info("" + message);
                break;
            case LOG4J:
                org.apache.log4j.Logger.getLogger(logName).info(message);
                break;
            case JUL:
                java.util.logging.Logger.getLogger(logName).info("" + message);
        }
    }


    public void warn(Object message) {
        switch (logMode) {
            case SLF4J:
                org.slf4j.LoggerFactory.getLogger(logName).warn("" + message);
                break;
            case LOG4J:
                org.apache.log4j.Logger.getLogger(logName).warn(message);
                break;
            case JUL:
                java.util.logging.Logger.getLogger(logName).warning("" + message);
        }
    }


    public void error(Object message) {
        switch (logMode) {
            case SLF4J:
                org.slf4j.LoggerFactory.getLogger(logName).error("" + message);
                break;
            case LOG4J:
                org.apache.log4j.Logger.getLogger(logName).error(message);
                break;
            case JUL:
                java.util.logging.Logger.getLogger(logName).severe("" + message);
                break;
        }
    }


    public void error(Object message, Throwable t) {
        switch (logMode) {
            case SLF4J:
                org.slf4j.LoggerFactory.getLogger(logName).error("" + message, t);
                break;
            case LOG4J:
                org.apache.log4j.Logger.getLogger(logName).error(message, t);
                break;
            case JUL:
                java.util.logging.Logger.getLogger(logName).log(java.util.logging.Level.SEVERE, "" + message, t);
        }
    }
}