SLF4J源码解析-LoggerFactory(一)

slf4j的含义为Simple logging facade for Java,其为简单的为java实现的日志打印工具,本文则对其源码进行简单的分析

JAVA调用SLF4J

public class Test{
	private static fianl Logger log = LoggerFactory.getLogger(Test.class) ;
	public static void main(String[] args){
		log.debug();
		log.info();
		log.error();
		log.fatal();
	}
}

注意调用slf4j接口用的是LoggerFactory.getLogger()方法,与log4j调用的LogManager.getLogger()有点区别

Logger接口

内部属性概览

final public String ROOR_LOGGER_NAME = "ROOT" ;

public boolean isTraceEnabled();

public void trace(String msg);

....
....

简单的看也就是定义了相应的级别输出方法,比如trace()/info()/error()/debug()等

LoggerFactory内部属性

其为final类型的class。罗列部分属性,具体如下

  //代表日志工具的初始化状态
  static final int UNINITIALIZED = 0;
  static final int ONGOING_INITILIZATION = 1;
  static final int FAILED_INITILIZATION = 2;
  static final int SUCCESSFUL_INITILIZATION = 3;
  static final int NOP_FALLBACK_INITILIZATION = 4;

  //返回的均为NOPLogger类,即不打印任何日志信息
  static SubstituteLoggerFactory TEMP_FACTORY = new SubstituteLoggerFactory();
  static NOPLoggerFactory NOP_FALLBACK_FACTORY = new NOPLoggerFactory();

LoggerFactory#getLogger()-获取日志对象

其为静态方法,源码如下

  public static Logger getLogger(String name) {
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    return iLoggerFactory.getLogger(name);
  }

继续查看下LoggerFactory#getILoggerFactory()方法

LoggerFactory#getILoggerFactory()-获取真实的日志工厂

源码如下

  public static ILoggerFactory getILoggerFactory() {
    //初次获取,初始化状态为0
    if (INITIALIZATION_STATE == UNINITIALIZED) {
	  //状态置为1
      INITIALIZATION_STATE = ONGOING_INITILIZATION;
      //进行初始化
      performInitialization();

    }
    switch (INITIALIZATION_STATE) {
    case SUCCESSFUL_INITILIZATION:
      //返回初始化成功的日志对象
      return StaticLoggerBinder.getSingleton().getLoggerFactory();
    case NOP_FALLBACK_INITILIZATION:
      return NOP_FALLBACK_FACTORY;
    case FAILED_INITILIZATION:
      throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
    case ONGOING_INITILIZATION:
      // support re-entrant behavior.
      // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
      return TEMP_FACTORY;
    }
    throw new IllegalStateException("Unreachable code");
  }

继续观察LoggerFactory#performInitialization()方法

LoggerFactory#performInitialization()-初始化日志操作

  private final static void performInitialization() {
    //检查项目部署的环境即classpath下有无StaticLoggerBinder.class
    singleImplementationSanityCheck();
    //有则开始绑定
    bind();
    if (INITIALIZATION_STATE == SUCCESSFUL_INITILIZATION) {
      //对slf4j支持的版本进行确认
      versionSanityCheck();
   
    }
  }

分别看下LoggerFactory#singleImplementationSanityCheck()方法和LoggerFactory#bind()方法

LoggerFactory#singleImplementationSanityCheck()-特定类存在判断

特定类指的是org/slf4j/impl/StaticLoggerBinder.class。具体源码如下

  private static void singleImplementationSanityCheck() {
    try {
      //获取类加载器
      ClassLoader loggerFactoryClassLoader = LoggerFactory.class
          .getClassLoader();
      //存放特定类的个数,当无相应的资源返回为空集合,但不为null
      Enumeration paths;
      if (loggerFactoryClassLoader == null) {
        paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
      } else {
        paths = loggerFactoryClassLoader
            .getResources(STATIC_LOGGER_BINDER_PATH);
      }
      List implementationList = new ArrayList();
      //对获取org/slf4j/impl/StaticLoggerBinder.class的资源进行遍历
      while (paths.hasMoreElements()) {
        URL path = (URL) paths.nextElement();
        implementationList.add(path);
      }
      //打印成功日志
      if (implementationList.size() > 1) {
        Util.report("Class path contains multiple SLF4J bindings.");
        for (int i = 0; i < implementationList.size(); i++) {
          Util.report("Found binding in [" + implementationList.get(i) + "]");
        }
        Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
      }
    } catch (IOException ioe) {
      Util.report("Error getting resources from path", ioe);
    }
  }

主要任务是判断是否存在org/slf4j/impl/StaticLoggerBinder.class,其在为bind()方法作预备调查,此方法目的是打印一些帮助信息。注意此处不建议拥有多个StaticLoggerBinder类,一般要求只有单个StaticLoggerBinder存在,不然则会导致日志输出不了

LoggerFactory#bind()-绑定获取真实的日志处理类

bind()方法的处理逻辑显得就很有意思了

  private final static void bind() {
    try {
      //获取StaticLoggerBinder单例
      // the next line does the binding
      StaticLoggerBinder.getSingleton();
      INITIALIZATION_STATE = SUCCESSFUL_INITILIZATION;
      emitSubstituteLoggerWarning();
    } catch (NoClassDefFoundError ncde) {
      //对无此类的异常处理
      String msg = ncde.getMessage();
      //如果错误信息含有org/slf4j/impl/StaticLoggerBinder信息则设置初始化状态为4
      if (msg != null && msg.indexOf("org/slf4j/impl/StaticLoggerBinder") != -1) {
        INITIALIZATION_STATE = NOP_FALLBACK_INITILIZATION;
        Util
            .report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
        Util.report("Defaulting to no-operation (NOP) logger implementation");
        Util.report("See " + NO_STATICLOGGERBINDER_URL
            + " for further details.");
      } else {
        //状态为2,并抛出异常
        failedBinding(ncde);
        throw ncde;
      }
    } catch(java.lang.NoSuchMethodError nsme) {
      //对StaticLoggerBinder类无getSingleton()方法做处理
      String msg = nsme.getMessage();
      if (msg != null && msg.indexOf("org.slf4j.impl.StaticLoggerBinder.getSingleton()") != -1) {
        INITIALIZATION_STATE = FAILED_INITILIZATION;
        Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
        Util.report("Your binding is version 1.5.5 or earlier.");
        Util.report("Upgrade your binding to version 1.6.x. or 2.0.x");
      }
      throw nsme;
    } catch (Exception e) {
      failedBinding(e);
      throw new IllegalStateException("Unexpected initialization failure", e);
    }
  }

bind()方法对StaticLoggerBinder#getSingleton()方法做了异常捕获处理,处理逻辑如下:

  1. 对不存在StaticLoggerBinder类的NoClassDefFoundError异常,如果错误信息含有org/slf4j/impl/StaticLoggerBinder信息则不抛出异常,但设置状态为NOP_FALLBACK_INITILIZATION(4);反之则直接抛出异常,并设置状态为FAILED_INITILIZATION(2)

  2. 对存在StaticLoggerBinder类但不存在getSingleton()方法的NoSuchMethodError异常,均抛出异常,并设置状态为FAILED_INITILIZATION(2)

LoggerFactory#versionSanityCheck()-日志版本要求验证

源码如下

  private final static void versionSanityCheck() {
    try {
      //此处为1.6
      String requested = StaticLoggerBinder.REQUESTED_API_VERSION;

      //判断是否与API的版本一致,此处为true
      boolean match = false;
      for (int i = 0; i < API_COMPATIBILITY_LIST.length; i++) {
        if (requested.startsWith(API_COMPATIBILITY_LIST[i])) {
          match = true;
        }
      }
      if (!match) {
        Util.report("The requested version " + requested
            + " by your slf4j binding is not compatible with "
            + Arrays.asList(API_COMPATIBILITY_LIST).toString());
        Util.report("See " + VERSION_MISMATCH + " for further details.");
      }
    } catch (java.lang.NoSuchFieldError nsfe) {
      // given our large user base and SLF4J's commitment to backward
      // compatibility, we cannot cry here. Only for implementations
      // which willingly declare a REQUESTED_API_VERSION field do we
      // emit compatibility warnings.
    } catch (Throwable e) {
      // we should never reach here
      Util.report("Unexpected problem occured during version sanity check", e);
    }
  }

目前的slf4j版本为1.6

小结

slf4j相当于是一个抽象接口,其会判断classpath下是否存在StaticLoggerBinder类,并针对此类进行相应的逻辑处理,于此我们可以判断出,其可以很好的被其他日志API接入,比如logback等

下节内容预告

针对返回状态为SUCCESSFUL_INITILIZATIONNOP_FALLBACK_INITILIZATIONFAILED_INITILIZATIONONGOING_INITILIZATION时,创建的为何种ILoggerFactory,详情见SLF4J源码解析-LoggerFactory(二)

posted @ 2017-08-24 20:14  南柯问天  阅读(3076)  评论(0编辑  收藏  举报