Spring Boot 启动过程分析
项目中突然启动时开始打印 DEBUG 的日志,而日志级别配置的都是 INFO. 尽管投产比很低, 出于洁癖, 花了两个小时研究了一下问题。
想要调查清楚: 为什么会启动时打印 DEBUG 日志,稍后就按照配置的级别(INFO)进行打印.
日志是怎么打印的
倒叙而言
- 通过
logger的debug/info等方法进行打印 (实际的打印过程暂且忽略) - 获取到的
logger有日志级别,相应的 loggerContext 中也有对应的配置spring-boot中, 我们配置了logback作为打印的框架, 以上表述仅针对logback的实现
logger于LoggerFactory中获取Slf4jLogfactory会获取StaticLoggerBinder中的LogFactory单例
LogFactory单例 会被LogBackLoggingSystem初始化LogBackLoggingSystem会通过LoggingApplicationListener初始化LoggingApplicationListener会在SpringApplication的相关生命周期事件回调时被触发spring-boot以及spring-cloud启动时,均会创建SpringApplication- 简单而言,
spring-cloud的启动即是通过先启动一个bootstrap的SpringApplication来拉起真正的SpringApplication。
- 简单而言,
排查过程
- debug 打印的过程
- 发现启动时对应的
loggercontext和RootLogger的级别均为DEBUG - 正常运行时打印日志的 logger, 对应的
loggercontext和RootLogger的级别均为INFO
- 发现启动时对应的
- 找到设置 level 的代码,debug 设值和变化的过程
- 发现
LogBackLoggingSystem的初始化会被调用两次, 第一次为DEBUG级别, 第二次为INFO级别
- 发现
- 分析为什么会初始化两次
SpringApplication.run方法触发后, 会通过ApplicationListener回调- 回调的
listner中, 有一个BootstrapApplicationListener, 用于启动spring-cloud BootstrapApplicationListener会创建一个用于bootstrap的SpringApplication, 创建的过程中也会触发ApplicationListener, 其中同样包含LoggingApplicationListenerspring-boot的ApplicationListener会触发的回调包含BootstrapApplicationListener,LoggingSystemShutdownListener,LoggingApplicationListener. 由提前设置好的 order 决定触发顺序,所以会先创建用于bootstrap的loggingsystem, 而后销毁,而后创建应用运行时使用的loggingsystem
解决方法
配置 bootstrap.yml, 其中设置 logging.path 以及 logging.level, 即可指定 bootstrap 期间的 logger 的行为。
感想
这个问题本身解决的价值和带来的经验/知识积累一般。 不过查问题挺爽, 逃避一下严肃的思考, 另外工程的集成度真高, 这一个小问题里没追究下去的细节还有很多,程序员不容易,哈哈