slf4j在日志框架中起的关键作用及源码轻度分析

SLF4J,即简单日志门面(Simple Logging Facade for Java)。从设计模式的角度考虑,它是用来在log和代码层之间起到门面的作用。 对应用来说只要使用slf4j提供的接口,即可隐藏日志的具体实现。这与jdbc和相似。使用jdbc也就避免了不同的具体数据库。使用了slf4j可以对客户端应用解耦。因为当我们在代码实现中引入log日志的时候,用的是接口,所以可以实时的更具情况来调换具体的日志实现类。这就是slf4j的作用。

SLF4J所提供的核心API是一些接口以及一个LoggerFactory的工厂类。SLF4J提供了统一的记录日志的接口,只要按照其提供的方法记录即可,最终日志的格式、记录级别、输出方式等通过具体日志系统的配置来实现,因此可以在应用中灵活切换日志系统。

 

配置SLF4J是非常简单的一件事,只要将与你打算使用的日志系统对应的jar包加入到项目中,SLF4J就会自动选择使用你加入的日志系统。

 

日志系统绑定原理:

下面说的是slf4j+logback组合示例:  

    我们在应用slf4j日志门面的时候都是先在类中声明Logger

private static final Logger logger = LoggerFactory.getLogger(Test.class); 

Logger是slf4j中的一个接口,slf4j又是日志的门面,我想大家都有一个疑惑,Logger怎么会根据你导入项目的log4j或者logback去调用不同项目中的日志框架,我们进LoggerFactory的源码包里一探究竟,其实so easy! 我这里是slf4j-1.7.5.jar

  public static Logger getLogger(Class clazz) {
    return getLogger(clazz.getName());
  }

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

  public static ILoggerFactory getILoggerFactory() {
    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://bugzilla.slf4j.org/show_bug.cgi?id=106
        return TEMP_FACTORY;
    }
    throw new IllegalStateException("Unreachable code");
  }

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

  private final static void bind() {
      Set staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
      reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
      // the next line does the binding
      StaticLoggerBinder.getSingleton();
      INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
      reportActualBinding(staticLoggerBinderPathSet);
      emitSubstituteLoggerWarning();
   
  }

  private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class";

  private static Set findPossibleStaticLoggerBinderPathSet() {
    Set staticLoggerBinderPathSet = new LinkedHashSet();
    try {
      ClassLoader loggerFactoryClassLoader = LoggerFactory.class
              .getClassLoader();
      Enumeration paths;
      if (loggerFactoryClassLoader == null) {
        paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
      } else {
        paths = loggerFactoryClassLoader
                .getResources(STATIC_LOGGER_BINDER_PATH);
      }
      while (paths.hasMoreElements()) {
        URL path = (URL) paths.nextElement();
        staticLoggerBinderPathSet.add(path);
      }
    } catch (IOException ioe) {
      Util.report("Error getting resources from path", ioe);
    }
    return staticLoggerBinderPathSet;
  }

上面这一步是非常关键的一步,其中 loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH); 意思是查找所有给定名称的资源 ,详见JDK参考文档,意思是查找项目根路径下 org/slf4j/impl/StaticLoggerBinder.class 此文件,然后你可以查看logback或者slf4j-log4j的jar包,会发现这两个包都有这个目录和文件,说明,如果项目导入这两个其中一个文件的话,getResources就会找到这个实现类

现在我们回过头来再看之前的方法,据需往下走  performInitialization()方法执行完后 INITIALIZATION_STATE现在是成功状态,会进入StaticLoggerBinder.getSingleton().getLoggerFactory();方法。此时应该注意,这个方法会最终返回Logger实现类。但是此时又是在slf4j包里,怎么能返回slf4j或者log4j的实现类呢,我一开始以为用的是反射,因为我们已经获取到其他包里的StaticLoggerBinder ,但是其实不是,我自己找过之后才发现slf4j.jar里根本没有StaticLoggerBinder.class。此时用 StaticLoggerBinder调用getSingleton().方法,其实这个StaticLoggerBinder已经是其他日志实现框架中的类了。(slf4j的源码中是有StaticLoggerBinder类的)





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值