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体系外实现解决这个问题。。。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章