zoukankan      html  css  js  c++  java
  • 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(二)

  • 相关阅读:
    Windows 命令行
    建议博客园提供 BT 资源发布功能——由分享 VS2005 Beta2 的历程想到的
    成功 自信 快乐(转载)
    轻松一下,加菲猫语录
    COmega 概述
    对 RBAC 几种模型的理解及初步实现构想
    DHTML 中滚动条的设置
    求婚
    终于有了自己的blog!
    VS2005 已发布!
  • 原文地址:https://www.cnblogs.com/question-sky/p/7425069.html
Copyright © 2011-2022 走看看