No appenders present in context [default] for logger [xxxxx]

使用logback时,由于配置缘故出现No appenders present in context [default] for logger [xxxxx]的问题,导致通过getLogger(class)获取到的logger没有打印日志。问题部分配置如下:STDOUT是ConsoleAppender省略了

	<logger name="tom.vertx" level="debug" additivity="false">
		<appender-ref ref="STDOUT"/>
	</logger>

	<root level="info">
		<appender-ref ref="STDOUT" />
	</root>

先说结论:

该问题出现的原因是我配置了tom.vertx包下面的类对应的logger,然后该包下的类通过getLogger(class)获取logger时,获取到的logger是logger名称为tom.vertx的子孙,但是由于additivity是false,所以不会使用上级logger的appender,而通过getLogger(class)获取到的logger默认在初始化创建时并没有指定appender,所以就出现No appenders...的问题,解决方式就是additivity设置为true或者getLogger(name)方式直接通过logger名字获取已配置的logger。

Slf4j-Logback执行流程

结合实际使用简单说一下执行流程。在通过LoggerFactory.getLogger(...)时,第一次时会有以下执行流程对logback进行初始化操作。(slf4j-api 2.0.0-alpha1  logback-core/classic 1.3.0-alpha5)

  • (LoggerFactory) getILoggerFactory() 获取logger工厂类
    • (LoggerFactory) getProvider() 同步初始化
      • (LoggerFactory) performInitialization() 初始化操作
        • (LoggerFactory) bind()  查找并绑定provider
          • (LoggerFactory) findServiceProviders() 通过jdk spi技术 ServiceLoader加载位于META-INF/services/下的接口定义文件,获取SLF4JServiceProvider实实现类,logback是LogbackServiceProvider
          • (LoggerFactory) 调用provider.initialize()方法初始化LoggerContext等信息
            • (LogbackServiceProvider) initializeLoggerContext() 初始化logback配置
              • (ContextInitializer) new ContextInitializer(context).autoConfig() 扫描配置文件,如果-Dlogback.configurationFile=xx.xml/groovy配置i文件直接使用配置文件,否则查找classpath下logback.groovy、logback.xml、logback-test.xml配置文件,如果都没有找到,使用spi技术查找Configurator的配置实现类,如果还没有则使用BasicConfigurator初始化基本配置,设置默认root logger的appender为ConsoleAppender并设置loggerContext。
                • (ContextInitializer) configureByResource(url) 如果找到配置文件使用该方法读取解析配置文件,对于xml使用JoranConfigurator生成配置类,并初始化配置
                  • (JoranConfigruator) doConfigure(url),初始化配置,实际上调用的父类GenericConfigurator的doConfigure(url)方法
                    • (GenericConfigurator) playEventsAndProcessModel(event), 处理生成配置 、规则
                    • (GenericConfigurator) processModel(), 生成配置模型Model(xml中的每个标签都对应一个模型类),注册模型处理器(通过JoranConfigurator.buildDefaultProcessor方法), 并通过模型对应的handler.handle方法处理模型生成模型处理类如appender、logger等。
                      • (DefaultProcessor) process(), 模型处理,实例化模型handler,并通过handler.handle和handler.postHandle处理model模型并初始化。
  • (LoggerFactory) getLogger(class) 通过LoggerFactory获取logger,此时的LoggerFactory是LoggerContext对象
    • (LoggerContext) getLogger(name) 通过class.getName的类名调用getLogger(name)获取logger
      • 首先判断name==ROOT,如果是直接返回,否则继续
      • 通过loggerCache获取name对应的logger,如果有直接返回(loggerCache是个Map),否则继续
      • 循环通过( . 或者 $ )符号分割解析name,从root logger开始,通过logger.getChildByName获取子logger,如果没有则创建并加入到当前logger的孩子列表。例如:
      • 
        # 假如传入的类为tom.vertx.util.MainRunnerTest,则最终生成的logger如下:
        ROOT :parent=> null
        tom  :parent=> Logger[ROOT]
        tom.vertx :parent=> Logger[tom]
        tom.vertx.util :parent=> Logger[tom.vertx]
        tom.vertx.util.MainRunnerTest :parent=> Logger[tom.vertx.util]
        # 通过getLogger返回的logger为最后生成的logger,name是tom.vertx.util.MainRunnerTest

         

      • name解析完成返回传入name对应的logger对象。

  • (Logger) log.debug/info/warn/error/fatal 方法调用,最终会将传入的参数包装为一个LoggingEvent对象传给所有logger

    • (Logger) callAppenders(event) 事件分发,根据additive属性判断是否事件向上传递。true是,false否。

    •     public void callAppenders(ILoggingEvent event) {
              int writes = 0;
              //循环从当前logger获取其上级logger,并调用该链条上logger的appendLoopOnAppenders方法
              for (Logger l = this; l != null; l = l.parent) {
                  //循环将事件发给logger内的所有appender,每次调用返回值就是有多少个appender处理了该事件
                  //如果返回0,证明该logger没有配置appender
                  writes += l.appendLoopOnAppenders(event);
                  //如果logger的additive属性是false,则事件不会再往上传递,跳出循环
                  if (!l.additive) {
                      break;
                  }
              }
              // No appenders in hierarchy
              // 如果writes=0,说明log事件没有被任何一个appender处理,就导致了No appenders pres...
              if (writes == 0) {
                  loggerContext.noAppenderDefinedWarning(this);
              }
          }
      
         private int appendLoopOnAppenders(ILoggingEvent event) {
              //aai这个就是一个持有当前logger内所有appender的对象
              //如果当前logger配置了appender则aai不为空,否则为空
              //通过class类名获取时,没有配置class类对应的logger时,aai总是为空
              //所以如果没配置class对应的logger并且additive=false,则该logger aai总是null
              //由于additive为false,不会再向上传递事件,就会导致日志没打印,有WARN: No appenders..问题
              if (aai != null) {
                  return aai.appendLoopOnAppenders(event);
              } else {
                  return 0;
              }
          }

       

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章