接上篇说道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~~
GitHub 加速计划 / sp / spring-boot
40
11
下载
spring-projects/spring-boot: 是一个用于简化Spring应用开发的框架。适合用于需要快速开发企业级Java应用的项目。特点是可以提供自动配置、独立运行和内置的Tomcat服务器,简化Spring应用的构建和部署。
最近提交(Master分支:4 个月前 )
f8e5f467 * gh-48872: Add spring.data.rest.return-body-on-delete configuration property Closes gh-48872 12 小时前
1e458eff This commit adds the missing returnBodyOnDelete property to DataRestProperties, completing the symmetric configuration for controlling response body behavior across all CRUD operations. The property maps to RepositoryRestConfiguration.setReturnBodyOnDelete() which was added in Spring Data REST 4.1. Signed-off-by: paullee714 <woolba714@kakao.com> See gh-48872 12 小时前
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐