前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >springboot中log加载流程

springboot中log加载流程

原创
作者头像
eeaters
发布2024-11-11 16:44:29
发布2024-11-11 16:44:29
1190
举报
文章被收录于专栏:阿杰阿杰

结论

  1. 非自定义情况下, 系统优先拿logback.xml; 然后再拿logback-spring.xml @see AbstractLoggingSystem
  2. 默认情况下; 日志走的是编程式配置: @see DefaultLogbackConfiguration
  3. log支持的配置key包含在: @see LoggingSystemProperty
  4. 除了logging.level是yml/properties中优先; 其他大多数是xml优先(这个没有具体分析, 只是结论)

涉及jar包

代码语言:xml
复制
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

入口

spring-boot > spring.factories

代码语言:properties
复制
# 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


# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener

springboot容器启动的时候, 会在不同的阶段发送Event事件. 在LoggingApplicationListener中代码如下:

代码语言:java
复制
    @Override

    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationStartingEvent startingEvent) {
            onApplicationStartingEvent(startingEvent);
        }

        else if (event instanceof ApplicationEnvironmentPreparedEvent environmentPreparedEvent) {
            onApplicationEnvironmentPreparedEvent(environmentPreparedEvent);
        }

        else if (event instanceof ApplicationPreparedEvent preparedEvent) {
            onApplicationPreparedEvent(preparedEvent);
        }

        else if (event instanceof ContextClosedEvent contextClosedEvent) {
            onContextClosedEvent(contextClosedEvent);
        }

        else if (event instanceof ApplicationFailedEvent) {
            onApplicationFailedEvent();
        }
    }

ApplicationStartingEvent > 指定日志框架

这里拿到具体使用哪一种框架作为应用的日志框架, 默认: LogbackLoggingSystem

代码语言:java
复制
    private void onApplicationStartingEvent(ApplicationStartingEvent event) {

        this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
        this.loggingSystem.beforeInitialize();
    }
代码语言:java
复制
    public static LoggingSystem get(ClassLoader classLoader) {
        String loggingSystemClassName = System.getProperty(SYSTEM\_PROPERTY);
        if (StringUtils.hasLength(loggingSystemClassName)) {
            if (NONE.equals(loggingSystemClassName)) {
                return new NoOpLoggingSystem();
            }
            return get(classLoader, loggingSystemClassName);
        }

        // 以工厂+代理,获取spi中第一个返回的日志看框架: 第一个是logback
        LoggingSystem loggingSystem = SYSTEM\_FACTORY.getLoggingSystem(classLoader);
        Assert.state(loggingSystem != null, "No suitable logging system located");
        return loggingSystem;
    }

ApplicationEnvironmentPreparedEvent > 日志初始化

代码语言:java
复制
    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        SpringApplication springApplication = event.getSpringApplication();
        if (this.loggingSystem == null) {
            this.loggingSystem = LoggingSystem.get(springApplication.getClassLoader());
        }
        initialize(event.getEnvironment(), springApplication.getClassLoader());
    }

在initialize中有 initializeSystem

代码语言:java
复制
    public static final String CONFIG\_PROPERTY = "logging.config";
    private void initializeSystem(ConfigurableEnvironment environment, LoggingSystem system, LogFile logFile) {
        String logConfig = environment.getProperty(CONFIG\_PROPERTY);
        if (StringUtils.hasLength(logConfig)) {
            logConfig = logConfig.strip();
        }
            LoggingInitializationContext initializationContext = new LoggingInitializationContext(environment);
            if (ignoreLogConfig(logConfig)) {
                system.initialize(initializationContext, null, logFile);
            }
            else {
                system.initialize(initializationContext, logConfig, logFile);
            }
        }
    }
代码语言:java
复制
    @Override

    public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) {
        LoggerContext loggerContext = getLoggerContext();
        if (isAlreadyInitialized(loggerContext)) {
            return;
        }
        if (!initializeFromAotGeneratedArtifactsIfPossible(initializationContext, logFile)) {
            super.initialize(initializationContext, configLocation, logFile);
        }
    }

AbstractLoggingSystem - 核心脉络

代码语言:java
复制
    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);
    }
reinitialize - 默认localtion
代码语言:java
复制
    protected String getSelfInitializationConfig() {
        return findConfig(getStandardConfigLocations());
    }
    // logback的实现
    @Override
    protected String[] getStandardConfigLocations() {
        return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", "logback.xml" };
    }
getSpringInitializationConfig - spring环境配置location
代码语言:java
复制
    //如logback是标准位置;没有找到走spring提供的配置路径
    protected String[] getSpringConfigLocations() {
        String[] locations = getStandardConfigLocations();
        for (int i = 0; i < locations.length; i++) {
            String extension = StringUtils.getFilenameExtension(locations[i]);
            locations[i] = locations[i].substring(0, locations[i].length() - extension.length() - 1) + "-spring."
                    + extension;
        }
        return locations;
    }
loadConfiguration - 将文件进行配置
代码语言:java
复制
    @Override

    protected void loadConfiguration(LoggingInitializationContext initializationContext, String location,
            LogFile logFile) {
        LoggerContext loggerContext = getLoggerContext();
        stopAndReset(loggerContext);
        withLoggingSuppressed(() -> {
            if (initializationContext != null) {
                applySystemProperties(initializationContext.getEnvironment(), logFile);
            }
            try {
                Resource resource = new ApplicationResourceLoader().getResource(location);
                configureByResourceUrl(initializationContext, loggerContext, resource.getURL());
            }
            catch (Exception ex) {
                throw new IllegalStateException("Could not initialize Logback logging from " + location, ex);
            }
            loggerContext.start();
        });
        reportConfigurationErrorsIfNecessary(loggerContext);
    }
loadDefaults - java编程式日志配置
代码语言:java
复制
    @Override

    protected void loadDefaults(LoggingInitializationContext initializationContext, LogFile logFile) {
        LoggerContext context = getLoggerContext();
        stopAndReset(context);
        withLoggingSuppressed(() -> {
            boolean debug = Boolean.getBoolean("logback.debug");
            if (debug) {
                StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener());
            }
            Environment environment = initializationContext.getEnvironment();
            // Apply system properties directly in case the same JVM runs multiple apps
            new LogbackLoggingSystemProperties(environment, getDefaultValueResolver(environment), context::putProperty)
                .apply(logFile);
            LogbackConfigurator configurator = debug ? new DebugLogbackConfigurator(context)
                    : new LogbackConfigurator(context);
            new DefaultLogbackConfiguration(logFile).apply(configurator);
            context.setPackagingDataEnabled(true);
            context.start();
        });
    }

java编程配置类:

代码语言:java
复制
/**
 * Default logback configuration used by Spring Boot. Uses {@link LogbackConfigurator} to
 * improve startup time. See also the {@code base.xml}, {@code defaults.xml},
 * {@code console-appender.xml} and {@code file-appender.xml} files provided for classic
 * {@code logback.xml} use.
 */
class DefaultLogbackConfiguration {
    void apply(LogbackConfigurator config) {
        config.getConfigurationLock().lock();
        try {
            defaults(config);
            Appender<ILoggingEvent> consoleAppender = consoleAppender(config);
            if (this.logFile != null) {
                Appender<ILoggingEvent> fileAppender = fileAppender(config, this.logFile.toString());
                config.root(Level.INFO, consoleAppender, fileAppender);
            }
            else {
                config.root(Level.INFO, consoleAppender);
            }
        }
        finally {
            config.getConfigurationLock().unlock();
        }
    }


    private void defaults(LogbackConfigurator config) {
        config.conversionRule("applicationName", ApplicationNameConverter.class);
        config.conversionRule("clr", ColorConverter.class);
        config.conversionRule("correlationId", CorrelationIdConverter.class);
        config.conversionRule("wex", WhitespaceThrowableProxyConverter.class);
        config.conversionRule("wEx", ExtendedWhitespaceThrowableProxyConverter.class);
        config.getContext()
            .putProperty("CONSOLE\_LOG\_PATTERN", resolve(config, "${CONSOLE\_LOG\_PATTERN:-"
                    + "%clr(%d{${LOG\_DATEFORMAT\_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}){faint} %clr(${LOG\_LEVEL\_PATTERN:-%5p}) "
                    + "%clr(${PID:- }){magenta} %clr(---){faint} %clr(%applicationName[%15.15t]){faint} "
                    + "%clr(${LOG\_CORRELATION\_PATTERN:-}){faint}%clr(%-40.40logger{39}){cyan} "
                    + "%clr(:){faint} %m%n${LOG\_EXCEPTION\_CONVERSION\_WORD:-%wEx}}"));
        String defaultCharset = Charset.defaultCharset().name();
        config.getContext()
            .putProperty("CONSOLE\_LOG\_CHARSET", resolve(config, "${CONSOLE\_LOG\_CHARSET:-" + defaultCharset + "}"));
        config.getContext().putProperty("CONSOLE\_LOG\_THRESHOLD", resolve(config, "${CONSOLE\_LOG\_THRESHOLD:-TRACE}"));
        config.getContext()
            .putProperty("FILE\_LOG\_PATTERN", resolve(config, "${FILE\_LOG\_PATTERN:-"
                    + "%d{${LOG\_DATEFORMAT\_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG\_LEVEL\_PATTERN:-%5p} ${PID:- } --- %applicationName[%t] "
                    + "${LOG\_CORRELATION\_PATTERN:-}"
                    + "%-40.40logger{39} : %m%n${LOG\_EXCEPTION\_CONVERSION\_WORD:-%wEx}}"));
        config.getContext()
            .putProperty("FILE\_LOG\_CHARSET", resolve(config, "${FILE\_LOG\_CHARSET:-" + defaultCharset + "}"));
        config.getContext().putProperty("FILE\_LOG\_THRESHOLD", resolve(config, "${FILE\_LOG\_THRESHOLD:-TRACE}"));
        config.logger("org.apache.catalina.startup.DigesterFactory", Level.ERROR);
        config.logger("org.apache.catalina.util.LifecycleBase", Level.ERROR);
        config.logger("org.apache.coyote.http11.Http11NioProtocol", Level.WARN);
        config.logger("org.apache.sshd.common.util.SecurityUtils", Level.WARN);
        config.logger("org.apache.tomcat.util.net.NioSelectorPool", Level.WARN);
        config.logger("org.eclipse.jetty.util.component.AbstractLifeCycle", Level.ERROR);
        config.logger("org.hibernate.validator.internal.util.Version", Level.WARN);
        config.logger("org.springframework.boot.actuate.endpoint.jmx", Level.WARN);
    }
}

以编程式的方式设置日志格式; 同时如果在同一个包下面分别有 base.xml, console-appender.xml,defaults.xml,file-appender.xml. 提供了xml式的配置参考; 并且支持引入

Logback在Spring中支持那些配置

脉络中针对日志加载的顺序进行梳理; 但是log的一些property是如何配置的再找一下

LoggingApplicationListener
代码语言:java
复制
protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {        
        this.logFile = LogFile.get(environment);
            if (this.logFile != null) {
            // 这里可以找到配置的key
            this.logFile.applyToSystemProperties();
        }
    }
LogbackLoggingSystem
代码语言:java
复制
# 这里构建log的配置类    

new LogbackLoggingSystemProperties(environment, getDefaultValueResolver(environment), context::putProperty)
                .apply(logFile);
            LogbackConfigurator configurator = debug ? new DebugLogbackConfigurator(context)
                    : new LogbackConfigurator(context);
            new DefaultLogbackConfiguration(logFile).apply(configurator);
LogbackLoggingSystemProperties
代码语言:java
复制
    public final void apply(LogFile logFile) {
        PropertyResolver resolver = getPropertyResolver();
        apply(logFile, resolver);
    }

    protected void apply(LogFile logFile, PropertyResolver resolver) {
        String defaultCharsetName = getDefaultCharset().name();
        setApplicationNameSystemProperty(resolver);
        setSystemProperty(LoggingSystemProperty.PID, new ApplicationPid().toString());
        setSystemProperty(LoggingSystemProperty.CONSOLE\_CHARSET, resolver, defaultCharsetName);
        setSystemProperty(LoggingSystemProperty.FILE\_CHARSET, resolver, defaultCharsetName);
        setSystemProperty(LoggingSystemProperty.CONSOLE\_THRESHOLD, resolver, this::thresholdMapper);
        setSystemProperty(LoggingSystemProperty.FILE\_THRESHOLD, resolver, this::thresholdMapper);
        setSystemProperty(LoggingSystemProperty.EXCEPTION\_CONVERSION\_WORD, resolver);
        setSystemProperty(LoggingSystemProperty.CONSOLE\_PATTERN, resolver);
        setSystemProperty(LoggingSystemProperty.FILE\_PATTERN, resolver);
        setSystemProperty(LoggingSystemProperty.LEVEL\_PATTERN, resolver);
        setSystemProperty(LoggingSystemProperty.DATEFORMAT\_PATTERN, resolver);
        setSystemProperty(LoggingSystemProperty.CORRELATION\_PATTERN, resolver);
        if (logFile != null) {
            logFile.applyToSystemProperties();
        }
    }
LoggingSystemProperty
代码语言:java
复制
//这就是支持配置化的定义代码
public enum LoggingSystemProperty {
	/**
	 * Logging system property for the application name that should be logged.
	 */
	APPLICATION_NAME("LOGGED_APPLICATION_NAME"),

	/**
	 * Logging system property for the process ID.
	 */
	PID("PID"),

	/**
	 * Logging system property for the log file.
	 */
	LOG_FILE("LOG_FILE"),

	/**
	 * Logging system property for the log path.
	 */
	LOG_PATH("LOG_PATH"),

	/**
	 * Logging system property for the console log charset.
	 */
	CONSOLE_CHARSET("CONSOLE_LOG_CHARSET", "logging.charset.console"),

	/**
	 * Logging system property for the file log charset.
	 */
	FILE_CHARSET("FILE_LOG_CHARSET", "logging.charset.file"),

	/**
	 * Logging system property for the console log.
	 */
	CONSOLE_THRESHOLD("CONSOLE_LOG_THRESHOLD", "logging.threshold.console"),

	/**
	 * Logging system property for the file log.
	 */
	FILE_THRESHOLD("FILE_LOG_THRESHOLD", "logging.threshold.file"),

	/**
	 * Logging system property for the exception conversion word.
	 */
	EXCEPTION_CONVERSION_WORD("LOG_EXCEPTION_CONVERSION_WORD", "logging.exception-conversion-word"),

	/**
	 * Logging system property for the console log pattern.
	 */
	CONSOLE_PATTERN("CONSOLE_LOG_PATTERN", "logging.pattern.console"),

	/**
	 * Logging system property for the file log pattern.
	 */
	FILE_PATTERN("FILE_LOG_PATTERN", "logging.pattern.file"),

	/**
	 * Logging system property for the log level pattern.
	 */
	LEVEL_PATTERN("LOG_LEVEL_PATTERN", "logging.pattern.level"),

	/**
	 * Logging system property for the date-format pattern.
	 */
	DATEFORMAT_PATTERN("LOG_DATEFORMAT_PATTERN", "logging.pattern.dateformat"),

	/**
	 * Logging system property for the correlation pattern.
	 */
	CORRELATION_PATTERN("LOG_CORRELATION_PATTERN", "logging.pattern.correlation");
}

修改方式

logback.xml

自定义一个logback.xml或者logback-spring.xml; 这是大多数项目都统一的做法.公司会根据链路追踪的方式自定义格式. 忽略

修改application.yml

临时项目或者项目前期, 可以简单修改配置文件进行日志打印

logging.pattern.console= "%m %n"为例; 会只打印message并换行

同时设置xml及修改默认配置

如果是logging.level ; 那么yml优先级高. 这也是生产环境应该有的预期行为

其他的绝大多数配置则是logback.xml这种文件优先级高,

比如logging.pattern.console同时在xml和yml中设置, xml优先级高

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 结论
  • 涉及jar包
    • 入口
      • ApplicationStartingEvent > 指定日志框架
      • ApplicationEnvironmentPreparedEvent > 日志初始化
      • AbstractLoggingSystem - 核心脉络
      • Logback在Spring中支持那些配置
      • 修改方式
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档