The Simple Logging Facade for Java or (SLF4J) is a simple facade for various logging frameworks. It decouples the logging framework from the application. The deployer can choose which logging framework to use. For every popular logging framework, an adapter Jar file is available. Deploy this file together with the core SLF4J Jar file, and the logging automagically works.
In my current work situation, a financial application I developed has to be integrated into an existing server application. This server application uses its own logging framework. Luckily, I've been using SLF4J, so logging integration is just a matter of writing a custom SLF4J adapter and replace the current adapter (SLF4J-Log4J) with it.
Writing an adapter is very easy. You only need to create three files:
- Create an implementation of the logger interface: org.slf4j.Logger
- Create a factory for the new logger using this interface: org.slf4j.ILoggerFactory
- Create your own binder class, so SLF4J knows which factory to use to get loggers: org.slf4j.spi.LoggerFactoryBinder
Firstly, we create the class that implements the SLF4J Logger interface. The implementation of this interface is used by SLF4J to delegate the logging. It's an interface with a large number of methods, so let the IDE generate the empty methods for you. Next is to implement the bodies of the methods to do the actual logging. In my case, I call the custom logging framework.
package org.slf4j.impl;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.helpers.MessageFormatter;
public class MyLoggerAdapter implements Logger {
// Bunch of inherited methods here. Let your IDE generate this.
// Implement these methods to do your own logging.
}
Now, we create a factory implementation for the Logger class. This class is used to retrieve Logger objects used by the application. Make sure it implements the ILoggerFactory interface.
package org.slf4j.impl;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
import java.util.HashMap;
import java.util.Map;
public class MyLoggerFactory implements ILoggerFactory {
private Map<String, MyLoggerAdapter> loggerMap;
public MyLoggerFactory() {
loggerMap = new HashMap<String, MyLoggerAdapter>();
}
@Override
public Logger getLogger(String name) {
synchronized (loggerMap) {
if (!loggerMap.containsKey(name)) {
loggerMap.put(name, new MyLoggerAdapter(name));
}
return loggerMap.get(name);
}
}
}
Finally, we need a way to register or bind the logger factory to SLF4J. To do that, we have to customize the StaticLoggerBinder class. You can almost use the same code provided by other adapters. I shamelessly ripped my code from the Log4J adapter.
package org.slf4j.impl;
import org.slf4j.ILoggerFactory;
import org.slf4j.spi.LoggerFactoryBinder;
public class StaticLoggerBinder implements LoggerFactoryBinder {
/**
* The unique instance of this class.
*/
private static final StaticLoggerBinder SINGLETON
= new StaticLoggerBinder();
/**
* Return the singleton of this class.
*
* @return the StaticLoggerBinder singleton
*/
public static final StaticLoggerBinder getSingleton() {
return SINGLETON;
}
/**
* Declare the version of the SLF4J API this implementation is
* compiled against. The value of this field is usually modified
* with each release.
*/
// To avoid constant folding by the compiler,
// this field must *not* be final
public static String REQUESTED_API_VERSION = "1.6"; // !final
private static final String loggerFactoryClassStr
= MyLoggerFactory.class.getName();
/**
* The ILoggerFactory instance returned by the
* {@link #getLoggerFactory} method should always be the same
* object.
*/
private final ILoggerFactory loggerFactory;
private StaticLoggerBinder() {
loggerFactory = new MyLoggerFactory();
}
public ILoggerFactory getLoggerFactory() {
return loggerFactory;
}
public String getLoggerFactoryClassStr() {
return loggerFactoryClassStr;
}
}
Compile and deploy this code together with the SLF4J jar, and logging will be handled by the our new adapter.
Links: