【spring】 spring boot 日志配置以及原理

描述 spring boot 日志配置的使用及其实现

切换日志组件

spring boot 支持 logback 和 log4j2, 默认情况下使用 logback 作为日志组件,可以切换到 log4j2,方法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<!-- 排除 logging,否则 SLF4J 会报找到两个 StaticLoggerBinder
如果是 web 工程就在 spring-boot-starter-web 下排除 -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- 引入 log4j2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

说明:spring-boot-starter-logging 引入 logback 而 spring-boot-starter-log4j2 引入 log4j2。 spring 根据 classpath 里有什么日志组件的类就使用对应的日志组件。

配置列表

在 spring-boot.jar!org.springframework.boot.logging 里有与默认配置等价的配置文件,不同的日志组件在对应的子目录下

配置列表

  • logging.config 指定配置文件,之后日志系统都使得用配置文件的设定。示例 logging.config: classpath:logback.xml
  • logging.exception-conversion-word 打印异常的代码,默认值 %wEx
  • logging.file 日志文件名
  • logging.file.max-size 文件最大的大小
  • logging.file.max-history 文件最大保留天数
  • logging.path 日志文件保存路径
  • logging.group 可用来快速修改一组 logger。示例 logging.group.db=org.hibernate,org.springframework.jdbc
  • logging.level 修改日志级别。示例 logging.level.org.springframework=DEBUG
  • logging.pattern.console 输出到控制台的日志的 pattern
  • logging.pattern.dateformat 日期的输出格式
  • logging.pattern.file 输出到文件的日志的 pattern
  • logging.pattern.level 指示如何在日志中输出日志级别,默认值 %5p
  • logging.register-shutdown-hook 指定是否注册一个 shutdown hook

日志配置初始化过程

  1. LoggingApplicationListener 负责加载和初始化日志。它是一个 ApplicationListener, 通过 spring 事件触发日志的初始化
    LoggingApplicationListener 的成员 LoggingSystem loggingSystem 是日志系统的抽象

  2. 在 ApplicationStartingEvent 事件处理中,通过 LoggingSystem.get 得到 LoggingSystem 实例。LoggingSystem.SYSTEMS 静态成员中保存了 LoggingSystem 会找的类,如果找到就会创建对应的 LoggingSystem 子类实例。对应关系如下:
    logback: 找 ch.qos.logback.core.Appender 类, 找到就创建 LogbackLoggingSystem
    log4j2:找 org.apache.logging.log4j.core.impl.Log4jContextFactory 类, 找到就创建 Log4J2LoggingSystem
    如果都没有就创建 JavaLoggingSystem。 各个 LoggingSystem 的具体实现会用对应的方法设置各个日志配置。

  3. 在 ApplicationEnvironmentPreparedEvent 事件处理中, 调用 LoggingApplicationListener.initialize 根据配置初始化 LoggingSystem

  4. 在 ApplicationPreparedEvent 事件处理中将 LoggingSystem 实例注册到 ApplicationContext, Bean 名是 springBootLoggingSystem。如果有 LogFile 实例,也会注册到 ApplicationContext, Bean 名是 springBootLogFile。

  5. 最后,处理 ContextClosedEvent 事件时调用 LoggingSystem.cleanUp 清理环境

日志配置代码实现

LoggingApplicationListener.initialize 方法对日志进行配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

public class LoggingApplicationListener implements GenericApplicationListener {

...

// 处理 ApplicationEnvironmentPreparedEvent 事件
// 这时 Environment 已经准备好,可以读到用户的配置
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
if (this.loggingSystem == null) {
// 获得 LoggingSystem 的具体实现
this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
}
initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
}

// 进行初始化
protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
// 通过 System.setProperty 设置一些配置
// 有 exception-conversion-word, PID, pattern.console 等,见 LoggingSystemProperties.apply
new LoggingSystemProperties(environment).apply();

// 根据 logging.file 和 logging.path 创建 LogFile 对象
this.logFile = LogFile.get(environment);
if (this.logFile != null) {
this.logFile.applyToSystemProperties();
}

// 设置 Early 日志级别,看有没有以 -Ddebug 或 -Dtrace 启动程序
initializeEarlyLoggingLevel(environment);

// 如果有配置 logging.config, 就用这个配置指定的配置文件重新初始化
// 如果没配置,就尝试找约定的配置文件是否存在,存在就用那个文件做配置
// 如果都没有,就用默认的配置设定底层的日志组件
initializeSystem(environment, this.loggingSystem, this.logFile);

// 设置最终的日志级别
initializeFinalLoggingLevels(environment, this.loggingSystem);

// 注册 shutdown hook
registerShutdownHookIfNecessary(environment, this.loggingSystem);
}

}