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(二)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM