浅析 spring-boot log日志加载

1,108 阅读3分钟

本篇文章主要介绍spring-boot 如何自动加载日志相关处理类,方便后面在编码过程中更加清晰的配置配置文件。

日志模块加载方式

spring-boot 是靠 Event (点击 event 可以复习event相关知识点 ) 事件监听来加载相关组件的。主要监听器为 LoggingApplicationListener 接下来我们来一起看看 LoggingApplicationListener 是如何一步一步加载的。

加载 LoggingSystem

LoggingSystem是一个抽象类,主要是负责桥接其它日志系统。他自己本身提供一个公共静态方法用来加载LoggingSystem实现类,当然这些实现类也是spring-boot自己实现的,只不过他会根据当前类加载器环境中是否能加载到其它日志系统的主要类来确定加载哪一个对应的LoggingSystem实现类。根据源码我们可以找到如下对应关系

systems.put("ch.qos.logback.core.Appender", "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");

根据以上对应关系我们不难发现只要我们当前类加载器能加载到 Appender 就会使用 LogbackLoggingSystem 实现类。具体逻辑如下

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"));
}

细心的同学可能发现了,其实我们也可以指定系统属性(org.springframework.boot.logging.LoggingSystem)来告知spring应该加载哪个实现类,这里可以写自定义的实现类。

LogbackLoggingSystem

这里我们来拿 LogbackLoggingSystem 来做为例子来分析,spring-boot后续的日志加载逻辑。

当加载到 LogbackLoggingSystem 首先会调用 beforeInitialize 方法,这个方法主要加载 LoggerContext,这里需要注意,当我们存在logback包冲突时会经常遇到的错误 LoggerFactory is not a Logback LoggerContext ... 就是出自这里

Loggback正常jar包依赖 logback-classic slf4j-api

private LoggerContext getLoggerContext() {
    ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory();
    Assert.isInstanceOf(LoggerContext.class, factory,
        String.format(
            "LoggerFactory is not a Logback LoggerContext but Logback is on "
                            + "the classpath. Either remove Logback or the competing "
                            + "implementation (%s loaded from %s). If you are using "
                            + "WebLogic you will need to add 'org.slf4j' to "
                            + "prefer-application-packages in WEB-INF/weblogic.xml",
            factory.getClass(), getLocation(factory)));
    return (LoggerContext) factory;
    }

初始化日志系统

当加载完 LoggingSystem 后就开始初始化日志系统,来细品initialize方法

protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
    //向System.property 中添加相关log属性信息
    new LoggingSystemProperties(environment).apply();
    //创建LogFile 对象 这里关注两个属性 LogFile.FILE_NAME_PROPERTY = logging.file.name  logging.file.path
    this.logFile = LogFile.get(environment);
    if (this.logFile != null) {
            this.logFile.applyToSystemProperties();
    }
    //添加默认的logger(web,sql)  LoggingApplicationListener.DEFAULT_GROUP_LOGGERS
    this.loggerGroups = new LoggerGroups(DEFAULT_GROUP_LOGGERS);
    initializeEarlyLoggingLevel(environment);
    //初始化loggingsystem 配置,其中包括:logging配置文件路径(LoggingApplicationListener.CONFIG_PROPERTY = logging.config)
    //默认为:"logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml"
    initializeSystem(environment, this.loggingSystem, this.logFile);
    //日志级别配置-LogLevel(LOGGING_LEVEL=logging.level)
    initializeFinalLoggingLevels(environment, this.loggingSystem);
    //配置shutdown钩子(REGISTER_SHUTDOWN_HOOK_PROPERTY = "logging.register-shutdown-hook")
    registerShutdownHookIfNecessary(environment, this.loggingSystem);
}

向spring容器中注册LoggingSystem等对象

if (!beanFactory.containsBean(LOGGING_SYSTEM_BEAN_NAME)) {
    beanFactory.registerSingleton(LOGGING_SYSTEM_BEAN_NAME, this.loggingSystem);
}
if (this.logFile != null && !beanFactory.containsBean(LOG_FILE_BEAN_NAME)) {
    beanFactory.registerSingleton(LOG_FILE_BEAN_NAME, this.logFile);
}
if (this.loggerGroups != null && !beanFactory.containsBean(LOGGER_GROUPS_BEAN_NAME)) {
    beanFactory.registerSingleton(LOGGER_GROUPS_BEAN_NAME, this.loggerGroups);
}

总结

spring-boot 默认会以我们依赖的日志框架来自动装配对应的日志系统。在使用过程中我们只要知道如何通过配置来达到我们想要的结果。比如常用配置如下

  • logging.level 配置日志级别
  • logging.config 配置配置文件地址 一般采用:classpath:logback-debug.xml
  • logback 默认自动加载 logback.xml 配置文件

感谢

欢迎留言指正! 内容持续更新!