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;
              }
          }

       

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