专栏文章
专栏文章
SpringBoot 系列
1. SpringBoot 系列 #01:SpringBoot 启动流程源码分析 2. SpringBoot 系列 #02:SpringBoot 日志集成原理 3. SpringBoot 系列 #03:SpringBoot 自动装配原理

SpringBoot 系列 #02:SpringBoot 日志集成原理

发布于 2026-05-26 09:43 👁 7 次阅读
#源码解析#spring#springboot#logging

SpringBoot 通过 LoggingApplicationListener 监听 Spring 上下文生命周期,借助 LoggingSystemFactory / LoggingSystem 抽象层,在启动阶段自动完成日志框架(Logback / Log4j2 / JUL)的检测与初始化。

相关笔记SpringBoot 启动流程源码分析 · Spring 术语与缩写速查 · Spring 容器启动流程源码分析


目录

章节 说明
整体架构 核心组件与职责
初始化流程 启动阶段日志系统的完整触发路径
源码分析 关键类与方法解析
扩展点 如何接入自定义日志框架

整体架构

SpringBoot 日志集成由四个层次构成:

组件 层次 职责
LoggingApplicationListener 事件触发层 监听 Spring 上下文事件,在适当阶段驱动日志系统初始化
LoggingSystemFactory 工厂抽象层 定义创建 LoggingSystem 的接口,通过 spring.factories 注册候选实现
DelegatingLoggingSystemFactory 委托选择层 按顺序遍历所有候选 Factory,返回第一个可用实现
LoggingSystem(及其实现) 适配执行层 封装具体日志框架的初始化与配置加载逻辑

SpringBoot 内置三个 LoggingSystem 实现,按类路径优先级依次检测:

实现类 对应框架 检测依据
LogbackLoggingSystem Logback ch.qos.logback.classic.LoggerContext 存在于类路径
Log4J2LoggingSystem Log4j2 org.apache.logging.log4j.core.impl.Log4jContextFactory 存在于类路径
JavaLoggingSystem JUL 兜底实现,始终可用

优先级规则:三个 Factory 按 spring.factories 声明顺序依次尝试,第一个返回非 null 的实现生效。默认 starter 引入 Logback,故 Logback 优先。


初始化流程

springboot logging flow


源码分析

关键类一览

类名 所在包 职责
LoggingApplicationListener o.s.boot.context.logging 事件监听入口
LoggingSystemFactory o.s.boot.logging 工厂接口
DelegatingLoggingSystemFactory o.s.boot.logging 委托选择策略
LogbackLoggingSystem o.s.boot.logging.logback Logback 适配
Log4J2LoggingSystem o.s.boot.logging.log4j2 Log4j2 适配

spring.factories 注册

LoggingApplicationListener 和各 LoggingSystemFactory 均通过 spring.factories 注册:

# Application Listeners
org.springframework.context.ApplicationListener=\
  org.springframework.boot.context.logging.LoggingApplicationListener

# Logging Systems(按优先级排列)
org.springframework.boot.logging.LoggingSystemFactory=\
  org.springframework.boot.logging.logback.LogbackLoggingSystem.Factory,\
  org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.Factory,\
  org.springframework.boot.logging.java.JavaLoggingSystem.Factory

LoggingSystemFactory:工厂接口

public interface LoggingSystemFactory {

    // 根据类加载器判断当前环境是否支持该日志框架,返回 null 表示不支持
    LoggingSystem getLoggingSystem(ClassLoader classLoader);

    // 通过 spring.factories 加载所有候选 Factory,委托给 DelegatingLoggingSystemFactory
    static LoggingSystemFactory fromSpringFactories() {
        return new DelegatingLoggingSystemFactory(
            (classLoader) -> SpringFactoriesLoader.loadFactories(
                LoggingSystemFactory.class, classLoader));
    }
}

DelegatingLoggingSystemFactory:选择策略

class DelegatingLoggingSystemFactory implements LoggingSystemFactory {

    private final Function<ClassLoader, List<LoggingSystemFactory>> delegates;

    @Override
    public LoggingSystem getLoggingSystem(ClassLoader classLoader) {
        List<LoggingSystemFactory> delegates = this.delegates.apply(classLoader);
        if (delegates != null) {
            for (LoggingSystemFactory delegate : delegates) {
                LoggingSystem loggingSystem = delegate.getLoggingSystem(classLoader);
                if (loggingSystem != null) {
                    return loggingSystem;  // 返回第一个非 null 实现,后续 Factory 不再尝试
                }
            }
        }
        return null;
    }
}

Log4J2LoggingSystem.Factory:类路径探测示例

@Order(Ordered.LOWEST_PRECEDENCE)
public static class Factory implements LoggingSystemFactory {

    // 静态检测:类加载时一次性判断 Log4j2 是否在类路径中
    private static final boolean PRESENT = ClassUtils.isPresent(
        "org.apache.logging.log4j.core.impl.Log4jContextFactory",
        Factory.class.getClassLoader());

    @Override
    public LoggingSystem getLoggingSystem(ClassLoader classLoader) {
        if (PRESENT) {
            return new Log4J2LoggingSystem(classLoader);
        }
        return null;
    }
}

Log4J2LoggingSystem:加载配置文件

// Log4J2LoggingSystem#loadConfiguration
protected void loadConfiguration(String location, LogFile logFile) {
    Assert.notNull(location, "Location must not be null");
    try {
        LoggerContext ctx = getLoggerContext();
        URL url = ResourceUtils.getURL(location);
        ConfigurationSource source = getConfigurationSource(url);
        // 通过 LoggerContext#start 将新配置热应用到运行中的 Log4j2 实例
        ctx.start(ConfigurationFactory.getInstance().getConfiguration(ctx, source));
    }
    catch (Exception ex) {
        throw new IllegalStateException(
            "Could not initialize Log4J2 logging from " + location, ex);
    }
}

扩展点

如需接入自定义日志框架,只需三步:

  1. 实现 LoggingSystemFactory,在 getLoggingSystem() 中检测自定义框架类是否存在
  2. 实现 LoggingSystem,封装框架的初始化与配置加载逻辑
  3. src/main/resources/META-INF/spring.factories 中注册:
org.springframework.boot.logging.LoggingSystemFactory=\
  com.example.MyLoggingSystemFactory

⚠️ Factory 的注册顺序即优先级顺序。若要覆盖默认 Logback,需将自定义 Factory 排在 LogbackLoggingSystem.Factory 前面。


参考资料

  • Spring Boot 源码:org.springframework.boot.context.logging.LoggingApplicationListener
  • Spring Boot 源码:org.springframework.boot.logging.LoggingSystemFactory
  • 《Spring Boot Reference Documentation》— Features › Logging
← 返回列表

评论 (0)

暂无评论,来留下第一条吧。

发表评论