接上篇說道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_PROPERTY, Boolean.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~~