SLF4J整合其他日誌框架

1. 整合需要的Jar包

1.1 代碼使用SLF4J-API時

使用SLF4J-API的時候整合其他日誌框架時,一般需要加上slf4j-jcl.jar等等

1.2 代碼使用其他API時

使用其他API的時候,一般就是加上jcl-over-slf4j.jar這樣類似得jar包

1.3 一個樣板例子

Spring全家桶使用得commons-logging API,當我們自己代碼想要使用或者切換到SLF4J時;可以使用下面得插件

<!--目前存在commons-logging 及 slf4j兩套log api-->
<dependency>
   <groupId>org.slf4j</groupId><!--jcl這個插件也是利用了commons-logging載入具體實現時利用得SPI機制來搞得-->
   <artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-api</artifactId>
</dependency>
<dependency><!--logback 實現了SLF4J-API接口-->
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

2. SLF4J源代碼分析(bind的過程及涉及到的JVM機制)

2.1 ver-1.7.25---ClassLoader檢測

起始:

 public static Logger getLogger(Class<?> clazz) {
    Logger logger = getLogger(clazz.getName());
    if (DETECT_LOGGER_NAME_MISMATCH) {
       Class<?> autoComputedCallingClass = Util.getCallingClass();
       if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)){
          Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),autoComputedCallingClass.getName()));
          Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
        }
     }
    return logger;
}

//從類命獲取

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

//獲取日誌工廠

public static ILoggerFactory getILoggerFactory() {
  if (INITIALIZATION_STATE == UNINITIALIZED) {
     synchronized (LoggerFactory.class) {
        if (INITIALIZATION_STATE == UNINITIALIZED) {
            INITIALIZATION_STATE = ONGOING_INITIALIZATION;
            performInitialization();
        }
      }
   }
   switch (INITIALIZATION_STATE) {
   case SUCCESSFUL_INITIALIZATION:
     return StaticLoggerBinder.getSingleton().getLoggerFactory();
   case NOP_FALLBACK_INITIALIZATION:
     return NOP_FALLBACK_FACTORY;
   case FAILED_INITIALIZATION:
     throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
   case ONGOING_INITIALIZATION:
     // support re-entrant behavior.
     // See also http://jira.qos.ch/browse/SLF4J-97
     return SUBST_FACTORY;
    }
    throw new IllegalStateException("Unreachable code");
}

//日誌工廠初始化

private final static void performInitialization() {
  bind();
  if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
     versionSanityCheck();
  }
}

//具體實現綁定

private final static void bind() {
  try {
      Set<URL> staticLoggerBinderPathSet = null;
      if (!isAndroid()) {//不是安卓系統得時候,需要檢查是否有多個org/slf4j/impl/StaticLoggerBinder.class文件;保證SLF4J-API下沒有多個兼容插件(1.8後以SPI機制替換)
         staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
         reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
      }
      // 真正執行綁定,StaticLoggerBinder不同兼容包實現不一樣,但目的一樣這個類是單例實例持有一個LoggerFactory得引用;而這個loggerfatory裏面都是具體框架logger得適配器類實例
      StaticLoggerBinder.getSingleton();
      INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
      reportActualBinding(staticLoggerBinderPathSet);
      fixSubstituteLoggers();
      replayEvents();
      // release all resources in SUBST_FACTORY
      SUBST_FACTORY.clear();
   } catch (NoClassDefFoundError ncde) {
     String msg = ncde.getMessage();
     if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
        INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
        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 {
         failedBinding(ncde);
         throw ncde;
      }
    } catch (java.lang.NoSuchMethodError nsme) {
      String msg = nsme.getMessage();
      if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
         INITIALIZATION_STATE = FAILED_INITIALIZATION;
         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.");
      }
      throw nsme;
     } catch (Exception e) {
       failedBinding(e);
       throw new IllegalStateException("Unexpected initialization failure", e);
     }
}

//獲取

2.2 ver-1.8 SPI機制

起始:

public static Logger getLogger(Class<?> clazz) {
  Logger logger = getLogger(clazz.getName());
  if (DETECT_LOGGER_NAME_MISMATCH) {
     Class<?> autoComputedCallingClass = Util.getCallingClass();
     if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
         Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(), autoComputedCallingClass.getName()));
         Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
     }
   }
   return logger;
 }

//轉從name獲取

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

//先獲取具體得實現

public static ILoggerFactory getILoggerFactory() {
  return getProvider().getLoggerFactory();
}

//先check當前LoggerFactory得狀態;如果是未初始化則進行初始化

static SLF4JServiceProvider getProvider() {
   if (INITIALIZATION_STATE == UNINITIALIZED) {
      synchronized (LoggerFactory.class) {
         if (INITIALIZATION_STATE == UNINITIALIZED) {
             INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                    performInitialization();
             }
          }
        }
    switch (INITIALIZATION_STATE) {
    case SUCCESSFUL_INITIALIZATION:
        return PROVIDER;
    case NOP_FALLBACK_INITIALIZATION:
        return NOP_FALLBACK_FACTORY;
    case FAILED_INITIALIZATION:
        throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
    case ONGOING_INITIALIZATION:
       // support re-entrant behavior.
       // See also http://jira.qos.ch/browse/SLF4J-97
       return SUBST_PROVIDER;
    }
    throw new IllegalStateException("Unreachable code");
}

//封裝一下免得難看

private final static void performInitialization() {
  bind();
  if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
     versionSanityCheck();
  }
}

//接口實現者的綁定

private final static void bind() {
   try {
       List<SLF4JServiceProvider> providersList = findServiceProviders();//SPI機制
       reportMultipleBindingAmbiguity(providersList);//如果有多個實現類,則BugReport
       if (providersList != null && !providersList.isEmpty()) {
          PROVIDER = providersList.get(0);//獲取第一個實現類,注意Linux和windows都是獲取磁盤node值最小的Jar裏面的實現類
          PROVIDER.initialize();//接口初始化,不同日誌框架的兼容包實現不一樣Log4J或者logback或者jul等等,
          INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
          reportActualBinding(providersList);
          fixSubstituteLoggers();
          replayEvents();
          // release all resources in SUBST_FACTORY
          SUBST_PROVIDER.getSubstituteLoggerFactory().clear();
        } else {
           INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
           Util.report("No SLF4J providers were found.");
           Util.report("Defaulting to no-operation (NOP) logger implementation");
           Util.report("See " + NO_PROVIDERS_URL + " for further details.");
           Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
           reportIgnoredStaticLoggerBinders(staticLoggerBinderPathSet);
        }
      } catch (Exception e) {
            failedBinding(e);
            throw new IllegalStateException("Unexpected initialization failure", e);
      }
}

//獲取實現類List,SPI機制

private static List<SLF4JServiceProvider> findServiceProviders() {
   //SPI機制,主要由ServiceLoader這個工具類負責實施
   //主要工作流程:從每個Jar包的META-INFO目錄下的services文件夾下面獲取指定接口的實現類
   //要去文件名和接口的全類名一致;
   //返回值是所有的Jar包內的所有的實現類Class實例;
   ServiceLoader<SLF4JServiceProvider> serviceLoader=ServiceLoader.load(SLF4JServiceProvider.class);
   List<SLF4JServiceProvider> providerList = new ArrayList<SLF4JServiceProvider>();
   for (SLF4JServiceProvider provider : serviceLoader) {
      providerList.add(provider);
   }
   return providerList;
}

3 吐槽

  1. Java技術生態裏最讓人蛋疼得一點是,JDK制定得行業標準時不但慢而且還不一定好使;
  2. 比如日誌標準(先有了Log4J等等JDK纔出日誌得API還不好使),比如JavaEE標準(被Spring懟成了傻子)等等;
  3. 等JDK標準出來了,別人已經寫了一些框架佔了相當大部分市場;
  4. 而且同一個標準或者工具有很多種實現,不同實現還要加上自己得特定API。。。
  5. 還有JDK也缺少包管理機制,只有靠maven和gradle是JDK體系外實現解決這個問題。。。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章