slf4j 介绍

slf4j实现了对各种日志框架的一个门面模式(Facade), 即抽象出各种日志框架的共同特征, 通过接口提供一个简单易用的界面. 支持大部分的日志框架例如log4j, logback等.

SLF4J为各种loging APIs提供一个简单统一的接口,从而使得最终用户能够在部署的时候配置自己希望的loging APIs实现.

The Simple Logging Facade for Java or (SLF4J) serves as a simple facade or abstraction for various logging frameworks, e.g. java.util.logging, log4j and logback, allowing the end user to plug in the desired logging framework at deployment time.

在项目中使用slf4j的话, 那么具体的日志框架系统是可以替换的, 例如之前用log4j, 不需要改动一行代码就可以替换为logback. logback和log4j是同一帮人写的, 但是logback性能上有提升.

下面进入slf4j的源代码看一看其实现. 这里先看slf4j 1.7. 下载http://www.slf4j.org/dist/slf4j-1.7.4.tar.gz , 内容如下

slf4j-1.7.4.tar.gz

核心代码都在slf4j-api-1.7.4-sources.jar里面, 其他都是适配具体的日志框架的bridge, 某些日志框架提供了这个桥接, 例如logback. 在MyEclipse中新建项目导入slf4j-api-1.7.4-sources.jar中的代码.

slf4j-api-1.7.4-sources.jar

Logger类提供了记录日志需要的所有方法, LoggerFactory为我们生成logger的实例.

代码包中的impl目录不应该添加到项目中, 这些只是一些dummy类, 应该被具体的桥接类代替. 通过slf的maven配置文件可以看出, 生成class的jar包时, 会删除impl目录.

下面写一个test类:

 
 
package test;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Test {
    private static Logger log = LoggerFactory.getLogger(Test.class);
 
    public static void main ( String [] args ) {
 
        log.debug("this is a debugger info");
        log.info("this is a info");
    }
}
 

重点类是LoggerFactory, 负责绑定具体的日志系统. bridge必须提供一个StaticLoggerBinder类, 在classpath或者文件系统中应该只有一个StaticLoggerBinder类, LoggerFactory会进行一个扫描, 搜索这个类.

 
  // We need to use the name of the StaticLoggerBinder class, but we can't reference
  // the class itself.
  private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";
 
  private static Set findPossibleStaticLoggerBinderPathSet() {
    // use Set instead of list in order to deal with  bug #138
    // LinkedHashSet appropriate here because it preserves insertion order during iteration
    Set staticLoggerBinderPathSet = new LinkedHashSet();
    try {
      ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
      Enumeration paths;
      if (loggerFactoryClassLoader == null) {
        paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
      } else {
        paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
      }
 
      // paths应该只包含一个成员.
      while (paths.hasMoreElements()) {
        URL path = (URL) paths.nextElement();
        staticLoggerBinderPathSet.add(path);
      }
    } catch (IOException ioe) {
      Util.report("Error getting resources from path", ioe);
    }
    return staticLoggerBinderPathSet;
  }
 

org/slf4j/impl/StaticLoggerBinder.class是相对路径, 匹配所有同名的类, 例如org/slf4j/impl/StaticLoggerBinder.class或者logback-classic-1.0.10.jar!/org/slf4j/impl/StaticLoggerBinder.class . getResources会扫描classpath中所有的jar包或者文件夹.

这样具体使用哪个日志框架就取决于StaticLoggerBinder的实现. 如果要替换日志框架, 就替换StaticLoggerBinder的实现即可.

简单来说就是LoggerFactory负责找到StaticLoggerBinder的实现, 而StaticLoggerBinder负责绑定到具体的日志框架.如果一个日志框架需要支持slf4j那么久提供一个实现, slf4j会找到这个实现并执行绑定.

相比之下slf4j 1.5则简单的多, 没有搜索binder类的代码, 而是直接import StaticLoggerBinder类.

 
  static {
    try { 
      loggerFactory = StaticLoggerBinder.SINGLETON.getLoggerFactory();
    } catch(NoClassDefFoundError ncde) {
      String msg = ncde.getMessage();
      if(msg != null && msg.indexOf("org/slf4j/impl/StaticLoggerBinder") != -1) {
        Util.reportFailure("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
        Util.reportFailure("See "+NO_STATICLOGGERBINDER_URL+" for further details.");
 
      } 
      throw ncde;
    } catch (Exception e) {
      // we should never get here
      Util.reportFailure("Failed to instantiate logger ["
          + StaticLoggerBinder.SINGLETON.getLoggerFactoryClassStr() + "]", e);
    }
  }
 

如果没有就报错NoClassDefFound, 如果有就使用碰到的第一个.