目录
- 设计总览(Spring Boot Logging 模块抽象)
- LoggingSystem 加载机制源码分析
- LoggingApplicationListener 启动流程
- Log4J2LoggingSystem 主要源码解析
- logging.config、log4j2-spring.xml 加载逻辑
- 动态日志级别设置(Actuator 调用)
✅ 1️⃣ 设计总览
logging初始化流程总览:
[Spring Boot 启动]
|
v
[LoggingApplicationListener]
|
v
[LoggingSystem.get(ClassLoader)]
|
v
[根据 classpath 判断日志系统]
|
v
[创建 Log4J2LoggingSystem 或 LogbackLoggingSystem 实例]
|
v
[LoggingSystem.initialize(...)]
|
v
[判断是否指定 logging.config]
| Yes | No
v v
initializeWithSpecificConfig() initializeWithConventions()
|
v
getSelfInitializationConfig()
| Yes | No
v v
reinitialize() getSpringInitializationConfig()
| Yes | No
v v
loadConfiguration() loadDefaults()
Spring Boot 的日志系统抽象目标:
✔️ 极早期可用 :SpringApplication 启动前就能生效
✔️ 自动探测 :按 classpath 自动选择 logback、log4j2、jul
✔️ 可插拔 :支持外部定制实现
✔️ 统一配置:application.properties / yaml
核心抽象:
public abstract class LoggingSystem {
public abstract void beforeInitialize();
public abstract void initialize(
LoggingInitializationContext context,
String configLocation,
LogFile logFile
);
public abstract void setLogLevel(String loggerName, LogLevel level);
}
✅ 2️⃣ LoggingSystem 加载机制(源码细节)
🌟 关键点:不是 SPI,不是 spring.factories。
它用的是 硬编码 Map + classpath 探测,简单高效。
源码
private static final Map<String, String> SYSTEMS;
static {
Map<String, String> systems = new LinkedHashMap<>();
systems.put("ch.qos.logback.classic.LoggerContext",
"org.springframework.boot.logging.logback.LogbackLoggingSystem");
systems.put("org.apache.logging.log4j.core.impl.Log4jContextFactory",
"org.springframework.boot.logging.log4j2.Log4J2LoggingSystem");
systems.put("java.util.logging.LogManager", "org.springframework.boot.logging.java.JavaLoggingSystem");
SYSTEMS = Collections.unmodifiableMap(systems);
}
重点方法:get()
/**
* Detect and return the logging system in use. Supports Logback and Java Logging.
* @param classLoader the classloader
* @return the logging system
*/
public static LoggingSystem get(ClassLoader classLoader) {
String loggingSystem = System.getProperty(SYSTEM_PROPERTY);
if (StringUtils.hasLength(loggingSystem)) {
if (NONE.equals(loggingSystem)) {
return new NoOpLoggingSystem();
}
return get(classLoader, loggingSystem);
}
return SYSTEMS.entrySet().stream().filter((entry) -> ClassUtils.isPresent(entry.getKey(), classLoader))
.map((entry) -> get(classLoader, entry.getValue())).findFirst()
.orElseThrow(() -> new IllegalStateException("No suitable logging system located"));
}
✅ 加载流程:
1️⃣ -Dorg.springframework.boot.logging.LoggingSystem=xxx
→ 强制指定实现
2️⃣ -Dorg.springframework.boot.logging.LoggingSystem=none
→ 关闭日志
3️⃣ 否则
→ 遍历 SYSTEMS
→ classpath 上探测第一个可用实现
✔️ 例子:
- 如果存在 org.apache.logging.log4j.core.impl.Log4jContextFactory → 选用 log4j2
✅ 3️⃣ LoggingApplicationListener 启动流程
🌟 日志系统是 Spring Boot 的「超早期」监听器。
🔎 注册
SpringApplication 里有:
private void configureInitialListeners() {
this.listeners.add(new LoggingApplicationListener());
}
✔️ LoggingApplicationListener 是核心入口。
🔎 关键事件
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationStartingEvent) {
onApplicationStartingEvent((ApplicationStartingEvent) event);
}
else if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
else if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent((ApplicationPreparedEvent) event);
}
else if (event instanceof ContextClosedEvent
&& ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
onContextClosedEvent();
}
else if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}
🌟 重点看前两个阶段:
✅ 1️⃣ ApplicationStartingEvent
-
最早期调用
-
disable JUL bridge、打印 Spring Boot banner
✅ 2️⃣ ApplicationEnvironmentPreparedEvent
-
环境准备好(可读取 application.properties)
-
真正初始化日志配置
-
加载 LoggingSystem
🔎 关键调用
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
if (this.loggingSystem == null) {
this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
}
initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
}
✔️ 这里就是调用我们上面说的 LoggingSystem.get()
✔️ 最终会初始化 Log4J2LoggingSystem
✅ 4️⃣ Log4J2LoggingSystem 主要源码
Spring Boot 2.x 的 log4j2 实现类是:
org.springframework.boot.logging.log4j2.Log4J2LoggingSystem
🔎 继承
public class Log4J2LoggingSystem extends AbstractLoggingSystem
✔️ 抽象父类里封装了很多通用工具
✔️ 子类只需实现 log4j2-specific 部分
🔎 核心方法:initialize
@Override
public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
if (StringUtils.hasLength(configLocation)) {
initializeWithSpecificConfig(initializationContext, configLocation, logFile);
return;
}
initializeWithConventions(initializationContext, logFile);
}
private void initializeWithConventions(LoggingInitializationContext initializationContext, LogFile logFile) {
String config = getSelfInitializationConfig();
if (config != null && logFile == null) {
// self initialization has occurred, reinitialize in case of property changes
reinitialize(initializationContext);
return;
}
if (config == null) {
config = getSpringInitializationConfig();
}
if (config != null) {
loadConfiguration(initializationContext, config, logFile);
return;
}
loadDefaults(initializationContext, logFile);
}
✅ 逻辑分支:
是否指定配置文件 configLocation ?
├─ 是 → 初始化用户指定配置 (initializeWithSpecificConfig)
└─ 否 →
是否有 Log4j2 自己的初始化配置且无文件日志?
├─ 是 → 重新初始化 (reinitialize)
└─ 否 →
是否存在 Spring Boot 约定配置文件?
├─ 是 → 加载约定配置 (loadConfiguration)
└─ 否 → 加载默认内置配置 (loadDefaults)
🔎 reinitialize
@Override
protected void reinitialize(LoggingInitializationContext initializationContext) {
getLoggerContext().reconfigure();
}
✔️ 这里调用的是 log4j2 的官方 API
✔️ 支持热加载配置: reconfigure
会重新读取系统属性 log4j.configurationFile
指定的配置文件(或默认配置),完成配置热刷新.
🔎 默认配置优先级
@Override
protected String[] getStandardConfigLocations() {
return getCurrentlySupportedConfigLocations();
}
private String[] getCurrentlySupportedConfigLocations() {
List<String> supportedConfigLocations = new ArrayList<>();
addTestFiles(supportedConfigLocations);
supportedConfigLocations.add("log4j2.properties");
if (isClassAvailable("com.fasterxml.jackson.dataformat.yaml.YAMLParser")) {
Collections.addAll(supportedConfigLocations, "log4j2.yaml", "log4j2.yml");
}
if (isClassAvailable("com.fasterxml.jackson.databind.ObjectMapper")) {
Collections.addAll(supportedConfigLocations, "log4j2.json", "log4j2.jsn");
}
supportedConfigLocations.add("log4j2.xml");
return StringUtils.toStringArray(supportedConfigLocations);
}
Spring Boot 2.x 官方日志文件查找顺序说明:
By default, Spring Boot looks for a configuration file in the following locations (in order):
log4j2-test.properties
(classpath root)
log4j2-test.xml
(classpath root)
log4j2.properties
(classpath root)
log4j2.xml
(classpath root)
log4j2.yaml
/log4j2.yml
(if Jackson YAML is on classpath)
log4j2.json
/log4j2.jsn
(if Jackson is on classpath)
官方文档链接(Spring Boot 2.7 示例):
✅ 5️⃣ logging.config、log4j2-spring.xml 加载逻辑
✔️ 在 application.properties 中配置:
logging.config=classpath:log4j2-spring.xml
→ 会直接调用 reinitialize
✔️ 如果没指定
→ Spring Boot 会去 classpath 里按上面优先顺序找
→ 第一个找到的就加载
✔️ 推荐使用
log4j2-spring.xml
✅ 支持 spring profiles
✅ 支持占位符解析
✅ Spring 官方推荐
✅ 6️⃣ 动态日志级别设置(Actuator)
Spring Boot 2.x 的 Actuator 里有 endpoint:
POST /actuator/loggers/{loggerName} { "configuredLevel": "DEBUG" }
🌟 对应源码
@Override
public void setLogLevel(String loggerName, LogLevel logLevel) {
Level level = LEVELS.convertSystemToNative(logLevel);
LoggerConfig logger = getLogger(loggerName);
if (logger == null) {
logger = new LoggerConfig(loggerName, level, true);
getLoggerContext().getConfiguration().addLogger(loggerName, logger);
}
else {
logger.setLevel(level);
}
getLoggerContext().updateLoggers();
}
✔️ 通过 log4j2 API
✔️ 实现在线动态刷新
spring boot actuator 入门(1)-CSDN博客
Spring Boot 2.x 关于 logging 的常用配置参数及说明:
配置参数 | 类型 | 默认值 | 说明 | 备注 |
---|---|---|---|---|
logging.config |
String | 无 | 指定自定义日志配置文件的位置(支持classpath:和file:) | 优先级最高,覆盖默认配置 |
logging.level.<logger> |
String | INFO (root logger默认) |
设置指定包或类的日志级别,如logging.level.org.springframework=DEBUG |
可为具体类或包名 |
logging.file |
String | 无 | 指定日志文件名称(已过时,建议用logging.file.name) | 兼容旧版 |
logging.path |
String | 无 | 指定日志文件目录(已过时,建议用logging.file.path) | 兼容旧版 |
logging.file.name |
String | 无 | 指定日志文件的完整路径及名称 | 推荐使用 |
logging.file.path |
String | 无 | 指定日志文件目录 | Spring Boot 会在该目录生成默认文件名日志 |
logging.pattern.console |
String | Spring Boot 默认格式 | 控制台日志输出格式 | 不同日志实现支持程度不一 |
logging.pattern.file |
String | Spring Boot 默认格式 | 文件日志输出格式 | |
logging.pattern.level |
String | %5p |
日志级别输出格式 | |
logging.pattern.dateformat |
String | yyyy-MM-dd HH:mm:ss.SSS |
日志时间戳格式 | |
logging.exception-conversion-word |
String | %wEx |
异常堆栈打印格式 | |
logging.logback.rollingpolicy.file-name-pattern |
String | 无 | Logback文件滚动策略配置文件名格式 | 仅对Logback有效 |
logging.logback.rollingpolicy.clean-history-on-start |
Boolean | false | 是否启动时清理旧日志文件 | 仅对Logback有效 |
logging.log4j2.context-selector |
String | 无 | Log4j2上下文选择器类名 | 仅对Log4j2有效 |
logging.log4j2.config.location |
String | 无 | Log4j2配置文件路径 | 仅对Log4j2有效 |
logging.log4j2.shutdown.timeout |
Duration/String | 30s | Log4j2关闭等待超时时间 | 仅对Log4j2有效 |
logging.level.root |
String | INFO |
根日志级别 | 常用配置 |
Spring Boot 2.7.x 官方参考手册 --- Logging
✅ 总结
✅ Spring Boot 2.x 的日志系统设计非常实用:
-
超早期初始化
-
自动探测实现
-
支持外部配置和动态刷新
✅ 以 log4j2 为例:
-
实现类 Log4J2LoggingSystem
-
支持 log4j2-spring.xml
-
Actuator 动态管理