SLF4J源码解析-LoggerFactory(二)

承接前文SLF4J源码解析-LoggerFactory(一),本文则主要针对获取ILoggerFactory对象作下简单的分析

LoggerFactory#getILoggerFactory()

源码如下

  public static ILoggerFactory getILoggerFactory() {
   ***
   ***
    
    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");
  }

根据前文的分析,SL4J获取Logger对象需要classpath下拥有StaticLoggerBinder类,本文则选取logback API来作解析

StaticLoggerBinder.getSingleton().getLoggerFactory()-成功初始化状态下的获取ILoggerFactory()

StaticLoggerBinder.getSingleton()-获取StaticLoggerBinder实例

此处我们只需要分析下StaticLoggerBinder的静态初始化块

  static {
    SINGLETON.init();
  }

紧接着看下init()方法

  void init() {
    try {
      try {
        //此处的defaultLoggerContext为LoggerContext
        //此处也会去加载配置文件
        new ContextInitializer(defaultLoggerContext).autoConfig();
      } catch (JoranException je) {
        Util.report("Failed to auto configure default logger context", je);
      }
      // logback-292
      //检查是否defaultLoggerContext注册了StatusListener
      if(!StatusUtil.contextHasStatusListener(defaultLoggerContext)) {
        StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);
      }
      //内部调用
      contextSelectorBinder.init(defaultLoggerContext, KEY);
      initialized = true;
    } catch (Throwable t) {
      // we should never get here
      Util.report("Failed to instantiate [" + LoggerContext.class.getName()
          + "]", t);
    }
  }

看下ContextInitializer如何加载相应的配置文件

ContextInitializer#autoConfig()

直接看源码

  public void autoConfig() throws JoranException {
	//判断系统变量logback.statusListenerClass是否设定,设定了则注册
	//StatusListener
    StatusListenerConfigHelper.installIfAsked(loggerContext);
    //查找相应的配置文件,我们可以简单的看下
    URL url = findURLOfDefaultConfigurationFile(true);
    if (url != null) {
      //加载相应的配置文件
      configureByResource(url);
    } else {
	  //没有则创建名为console的ConsoleAppender
      BasicConfigurator.configure(loggerContext);
    }
  }

我们看下ContextInitializer如何查找配置文件

ContextInitializer#findURLOfDefaultConfigurationFile()

直接阅读源码

  public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {
    ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
    //首先查找系统变量logback.configurationFile指定的配置文件
    URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);
    if (url != null) {
      return url;
    }
    //反之则查找classpath下是否存在名为logback.groovy配置文件
    url = getResource(GROOVY_AUTOCONFIG_FILE, myClassLoader, updateStatus);
    if (url != null) {
      return url;
    }
    //反之则查找classpath下是否存在名为logback-test.xml配置文件
    url = getResource(TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus);
    if (url != null) {
      return url;
    }
	//最后则查找classpath下是否存在名为logback.xml的配置文件
    return getResource(AUTOCONFIG_FILE, myClassLoader, updateStatus);
  }

根据源码整理下logback加载配置文件的顺序

  1. 查找系统变量logback.configurationFile指定的配置文件

  2. 上述不存在则查找classpath下是否存在名为logback.groovy配置文件

  3. 上述不存在则查找classpath下是否存在名为logback-test.xml配置文件

  4. 最后查找classpath下是否存在名为logback.xml的配置文件

注意:此处的classpath特指根目录下,web环境下一般指WEB-INF/classes根目录

ContextInitializer#configureByResource()-加载指定的配置文件

具体源码如下

  public void configureByResource(URL url) throws JoranException {
    //指定的文件查找不到则抛出异常
    if (url == null) {
      throw new IllegalArgumentException("URL argument cannot be null");
    }
    //针对结尾为groovy的配置文件进行解析
    if (url.toString().endsWith("groovy")) {
      if (EnvUtil.isGroovyAvailable()) {
        // avoid directly referring to GafferConfigurator so as to avoid
        // loading  groovy.lang.GroovyObject . See also http://jira.qos.ch/browse/LBCLASSIC-214
        GafferUtil.runGafferConfiguratorOn(loggerContext, this, url);
      } else {
        StatusManager sm = loggerContext.getStatusManager();
        sm.add(new ErrorStatus("Groovy classes are not available on the class path. ABORTING INITIALIZATION.",
                loggerContext));
      }
    }
    //对结尾为xml的文件进行解析
    if (url.toString().endsWith("xml")) {
      JoranConfigurator configurator = new JoranConfigurator();
      configurator.setContext(loggerContext);
      configurator.doConfigure(url);
    }
  }

具体的解析步骤就不讲解了,读者有兴趣可自行去分析

NOPLoggerFactory#NOP_FALLBACK_INITILIZATION状态下生成的ILoggerFactory

此日志工厂主要生成不打印任何日志的Logger对象

小结

通过分析源码我们可以得知其他的API是如何结合SL4J来实现Logger原理的。本文是以logback为例的,具体的逻辑可见本文的详细内容

posted @ 2017-08-25 20:17  南柯问天  阅读(2156)  评论(0编辑  收藏  举报