spring-boot使用logback的坑2

接上篇說道spring-boot在使用logback的坑之後又發現一個坑,自定義的appender以及原生的appender的stop()方法不會調用,導致部分清理工作無法完成。
原因:上篇說到去掉了LoggingApplicationListener使spring-boot不再管理logback,導致沒有add一個shutdownhook
首先我們去掉上篇的方法,讓spring-boot依舊管理logback,這樣理論上就可以調用stop做清理工作了,但是最後發現還是不行,debug跟蹤代碼,如下:
private void registerShutdownHookIfNecessary(Environment environment,
      LoggingSystem loggingSystem) {
   boolean registerShutdownHook = new RelaxedPropertyResolver(environment)
         .getProperty(REGISTER_SHUTDOWN_HOOK_PROPERTYBoolean.class, false);
   if (registerShutdownHook) {
      Runnable shutdownHandler = loggingSystem.getShutdownHandler();
      if (shutdownHandler != null
            && shutdownHookRegistered.compareAndSet(false, true)) {
         registerShutdownHook(new Thread(shutdownHandler));
      }
   }
}
     
registerShutdownHook這個變量的值默認是false,所以不會加hook,查看REGISTER_SHUTDOWN_HOOK_PROPERTY,發現該值爲:logging.register-shutdown-hook,那麼這個應該是spring-boot中的application.properties中的配置項,將該值配置爲true
結果發現果真可以調用stop。
那麼這樣又會引入之前上篇說到的初始化兩次的問題,其實spring-boot在logback初始化之後會reset一次,自己再重新初始化,對程序沒有影響,但是自定義的appender的log會打印兩次,總是覺得不舒服。所以該問題應該從logback入手,讓logback加一個hook去做清理工作
我們模仿spring-boot中的方法,看它如何實現,上面代碼第6行調用getShutdownHandler(),我們進入該方法,LogbackLoggingSystem中:

@Override
public Runnable getShutdownHandler() {
   return new ShutdownHandler();
}


private final class ShutdownHandler implements Runnable {

   @Override
   public void run() {
      getLoggerContext().stop();
   }

}

一直到現在的代碼還是包在spring-boot中,我們不想引入spring的包,所以繼續往下看,

private LoggerContext getLoggerContext() {
   ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory();
   Assert.isInstanceOf(LoggerContext.class, factory,
         String.format(
               "LoggerFactory is not a Logback LoggerContext but Logback is on "
                     "the classpath. Either remove Logback or the competing "
                     "implementation (%s loaded from %s). If you are using "
                     "WebLogic you will need to add 'org.slf4j' to "
                     "prefer-application-packages in WEB-INF/weblogic.xml",
               factory.getClass()getLocation(factory)));
   return (LoggerContext) factory;
}

終於LoggerContext是logback中的類,我們只要拿到這個context,然後調用它的stop方法就可以了。
偶然發現logback-core包中有一個hook的package,那麼更簡單了,直接註冊這個hook裏面的某個類應該更簡單,那麼可以在自定義的appender中的start方法加入如下代碼:

DelayingShutdownHook shutdownHook = new DelayingShutdownHook();
Runtime.getRuntime().addShutdownHook(new Thread(shutdownHook));

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