本文记录 SpringBoot 与 Logback 是如何工作的,即观察 SpringBoot 中 Logback 是怎么一步一步初始化的。用以测试的 SpringBoot 版本是 1.5.16, 而非最新的 SpringBoot 2。关于 SpringBoot 日志的官方文档在 Logging, 但不太详细或透彻。本文也不承诺说就理解得更有深度,只是为官方文档提供更多方面的参考。
SpringBoot 默认使用 Slf4J + Logback 来记录日志,对于一个基本的依赖于
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
的 Spring Boot 项目,上面组件依赖了 spring-boot-starter-logging 组件,而该组件又引入了以下几个依赖
- logback-classic: 依赖了 Slf4J
- jcl-over-slf4j
- jul-to-slf4j
- log4j-over-slf4j
相当于把其他的日志框架全桥接到了 Slf4J + Logback 上去了。
那么 Spring Boot Web 项目是怎么样子的呢?spring-boot-starter-web 依赖于 spring-boot-starter,所以日志框架选用上就没有一点区别了。
Spring Boot 应用默认日志输出
从一个最简单的 Spring Boot 应用程序来感受它的配置日志输出,一个 Maven 项目,最基本的配置是
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.16.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> </dependencies> |
注意:spring-boot-starter-parent 的 pom.xml 文件值得瞧一瞧的。
当 application.properties 文件内容为空,并且没有任何的 Logback 配置文件在 resources 目录中时,
来个最简单的程序
1 2 3 4 5 6 7 8 9 10 |
@SpringBootApplication public class Application { private static final Logger logger = LoggerFactory.getLogger(Application.class); public static void main(String[] args) { logger.info("aaa"); SpringApplication.run(Application.class, args); logger.info("bbb"); } } |
在 SpringApplication.run(...) 前后各调用 logger.info(...) 输出信息
前面 logger.info("aaa") 和 logger.info("bbb") 的输出用红线标示出来了,可以非常感性的认识到
- 它们输出样式不同,所以使用了不同的日志配置
- logger.info("aaa") 发生在 Spring 上下文初始化之前,以 Logback 的默认行为初始化的 LoggerFactory
- logger.info("bbb") 发生在 Spring 上下文初始化之后,因此 Spring Boot 又重新配置了 Logback 的 LoggerFactory
- #2 不是我们这里探讨的范畴,它和普通 Java 应用使用 Logback 是一致的,主要看 #3 怎么来的,并且默认的 Log 是怎么样的配置
Spring Boot 是如何初始化 Logback 的
在 Logging 一文中提到了 Spring Boot 有一个 LoggingSystem
抽象来负责配置日志系统,并且 Logback 是首选。LoggingSystem
是一个抽象类,它的实现层次如下
既然说 Logback 是首选,那么 Spring Boot 最终是要用到 LogbackLoggingSystem
这个类的,那我们从源代码跟踪一下 Spring Boot 的 Spring 上下文是如何与 Logback 衔接起来的。
能与 Spring 上下文进行交互的一般来说是 ApplicationEvent, 这里是 org.springframework.boot.logging.LoggingApplicationListener
, 它实现了 ApplicationListener
, 看 LoggingApplicationListener
的事件方法
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationStartingEvent) { onApplicationStartingEvent((ApplicationStartingEvent) event); // #1 } else if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent( // #2 (ApplicationEnvironmentPreparedEvent) event); } else if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent((ApplicationPreparedEvent) event); // #3 } ...... |
#1 找到 LoggingSystem
的实现类,在 LoggingSystem.get(classloader)
方法中,如果配置了系统属性 org.springframework.boot.logging.LoggingSystem
对应的实现类的话,就用它,指定为 none
值就用 NoOpLogginSystem
实现,即没有任何日志输出。如果没有指定 org.springframework.boot.logging.LoggingSystem
系统属性,LoggingSystem
则尝试以下的顺序找实现类
- ch.qos.logback.core.Appender => org.springframework.boot.logging.logback.LogbackLoggingSystem
- ora.apache.logging.log4j.core.impl.Log4jContextFactory => org.springframework.boot.logging.log4j2.Log4J2LoggingSystem
- java.util.logging.LogManager => org.springframework.boot.logging.java.JavaLoggingSystem
而显然 LogbackLoggingSystem 对应的类 ch.qos.logback.core.Appender
是存在于 springboot starter 中的,所以在 #1 中可以确定是用 LogbackLoggingSystem
实现
#3 先说这最后一步,如果初始化好,把 LoggingSystem
的实例(此处为 LogbackLoggingSystem 实例) 注册名为 springBootLogginSystem
的 Spring Bean
#2 对日志进行配置,具体实现在 LoggingApplicationListener.initialize(environment, classLoader)
和 LogbackLoggingSystem.initialize(...)
方法中。不列出实际代码来了,只解翻译一下过程
- 试图从 Spring 属性(包括配置在 Spring 属性文件,--logging.file= 或 -Dlogging.file= 这样的配置)读出配置的
logging.file
和logging.path
值,如果有的话,分别映射为LOG_FILE
和LOG_PATH
系统属性值,这可以在 logback.xml 的配置中用 ${LOG_FILE} 引用到 - 初始日志级别的设置,如果 Spring 属性中配置了
debug=true
则为 LogLevel.DEBUG, 如果配置了trace=true
则为 LogLevel.TRACE。后面还会专为某些包预设一些日志级别,并且最后的日志级别可在 Spring 属性中用logging.level.logger_name=DEBUG
来一一配置,如logging.level.org.springframework=DEBUG
- 如果用 Spring 属性
logging.config
指定了配置文件,则使用该配置文件初始化 Logback 的 LoggerFactory,否则LogbackLoggingSystem
将会以下面的顺序来查找 Logback 配置文件logback-test.groovy, logback-test.xml, logback.groovy, logback.xml
logback-test-spring.xml, logback-test-spring.xml, logback-spring.groovy, logback-spring.xml (AbstractLoggingSystem.getSpringConfigLocations() 方法)注意:SpringBoot 会忽略掉普通 Logback 应用的系统属性
logback.configurationFile
设定配置文件的方法12345if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) {getLogger(LogbackLoggingSystem.class.getName()).warn("Ignoring '" + CONFIGURATION_FILE_PROPERTY + "' system property. "+ "Please use 'logging.config' instead.");} - 如果检查了以上八个文件都不存在的话,就要调用
LogbackLoggingSystem.loadDefaults(initializationContext, logFile)
来配置默认的日志。
Spring Boot 没有任何日志配置文件时配置
这块其实是上面步骤 #2 中的一个子步骤,因其重要才将其单独列出,来看看 SpringBoot 在没有加载到任何的配置文件时如何配置默认 Logback 的 LoggerFactory。入口就是 LogbackLoggingSystem.loadDefaults(initializationContext, logFile)
。
首先,默认显示日志级别的格式是:${logging.pattern.level:${LOG_LEVEL_PATTERN:%5p}}
, 可用 Spring 属性 logging.pattern.level
或系统属性 LOG_LEVEL_PATTERN
,默认为 %5p
。
其他的默认配置就要参考类 org.springframework.boot.logging.logback.DefaultLogbackConfiguation
不管有没有配置 logging.file
或 logging.path
,Spring Boot 都会初始化 consoleAppender, 并且默认的输出模式是
1 2 3 4 |
private static final String CONSOLE_LOG_PATTERN = "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} " + "%clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} " + "%clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} " + "%clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"; |
该模式可用 Spring 属性 logging.pattern.console
进行覆盖设置。注意,Spring Boot 还为我们定义了 clr
, wEx
这两个 Converter
。
如果配置了 Spring 属性 logging.file
和 logging.path
其中一个或两个,就会在 consoleAppender
的基础上再加一个 fileAppender
, 它的输出模式是
1 2 |
private static final String FILE_LOG_PATTERN = "%d{yyyy-MM-dd HH:mm:ss.SSS} " + "${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"; |
该模式可以用 Spring 属性 logging.pattern.file
进行覆盖。
日志文件是 10MB
大小不断滚动的,不会删除旧文件。
日志文件路径如何决定的
- 如果只设置了 Spring 属性
logging.file
, 就是logging.file
所指定的文件,文件名可以是绝对文件路径,或者相对路径 - 如果只设置了 Spring 属性
logging.path
, 那么日志文件是logging.path
下的spring.log
文件 - 如果以上两个属性同时指定了,则只有
logging.file
是有用的,与 #1 同
无论是对于 consoleAppender
还是 fileAppender
, 都是设置 INFO 为默认日志级别,并且预设了一些 logger 的日志输出级别。
简单的 Spring Boot 日志文件配置
理解了 SpringBoot 是如何初始化 Logback 日志配置后,我们来看一下项目中几种最简的日志配置方式。通常我们都会在项目的类路径下放置 logback.xml
或 logback-spring.xml
文件,而不是用 apprication.properties
的 loggin.aa.bb
等属性来配置日志。
日志同时输出到控制台和文件
依据 Spring Boot 加载 Logback 配置文件的顺序,我们可以在 classpath 下放 logback-spring.xml
或 logback.xml
, 注意是 logback.xml
被优先选择。内容如下
1 2 3 4 5 |
<?xml version="1.0" encoding="UTF-8"?> <configuration> <include resource="org/springframework/boot/logging/logback/base.xml"/> <logger name="org.springframework.web" level="DEBUG"/> </configuration> |
设置 Spring 属性 logging.file
或系统属性 LOG_FILE
来指定日志输出文件名。或者用 Spring 属性 logging.path
或系统属性 LOG_PATH
指定 spring.log
的路径。
日志只输出到文件
1 2 3 4 5 6 7 8 9 |
<?xml version="1.0" encoding="UTF-8"?> <configuration> <include resource="org/springframework/boot/logging/logback/defaults.xml" /> <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/> <include resource="org/springframework/boot/logging/logback/file-appender.xml" /> <root level="INFO"> <appender-ref ref="FILE" /> </root> </configuration> |
用上面相同的方式指定日志文件的路径。
只输出到控制台
就更简单了,可以什么配置文件也不要,并且不要配置 logging.file
和 logging.path
。而且效果与下面的配置是一样的。
1 2 3 4 5 6 7 8 |
<?xml version="1.0" encoding="UTF-8"?> <configuration> <include resource="org/springframework/boot/logging/logback/defaults.xml" /> <include resource="org/springframework/boot/logging/logback/console-appender.xml" /> <root level="INFO"> <appender-ref ref="CONSOLE" /> </root> </configuration> |
小结:
- Spring Boot 基础的依赖于 starter 的项目默认采用 Slf4J + Logback 来输出日志
- 没有任何配置文件,也没有配置 Spring 属性
logging.file
,logging.path
或系统属性LOG_FILE
,LOG_PATH
的情况下只会输出日志到控制台 - 没有任何配置文件,且有配置 #2 中任何一个属性的情况下将会同时输出日志到控制台与文件,文件以 10MB 大小滚动,不删除旧文件
- 不采用外部配置文件的情况下,可以用一些 Spring 属些来进行简单的日志配置,如
logging.pattern.file
等 - 可以设置 Spring 属性
logging.config
来指定外部 Logback 配置文件,Logback 默认用系统属性logback.configurationFile
指定配置文件的方式在 Spring Boot 项目中不再生效 - 以上叙述的 Spring 属性,可用多种设置方式,如 Spring 属性
abc
,有四种方式:1) application.properties 文件中的abc=xxx
, 2)环境变量export abc=xxx
, 3) 启动参数--abc=xxx
,4) 系统属性-Dabc=xxx
- 类路径下的配置文件可以用
logback-spring.xml
或logback.xml
,但是logback.xml
优先加载,并未遵循先特殊再普通的原则。(Logback 1.3.0 之后由于支持 Java 9,但是 Groovy 与 Java 9 未处理好关系,所以 Logback 1.3.0 不能支持 .groovy 的配置文件) - 为更精细化的控制 Logback 的输出,我们通常都会在类路径下旋转
logback.xml
或logback-spring.xml
配置文件,二选一了。我没发现这两个文件有什么不同。
链接:
本文链接 https://yanbin.blog/springboot-work-with-logback/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。