spring boot log4j2配置不生效

最近的一个项目采用Spring Boot构建,使用了log4j2记录日志;按照之前的习惯,通过-Dlog4j.configurationFile指定log4j2.xml路径,启动应用,但意外的发现日志配置竟然没有生效;

初步估计是Spring Boot对日志部分进行了某种修改,导致了这个问题,查看Spring Boot的jar包,发现spring.factories中有这么一段配置:

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.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.logging.LoggingApplicationListener

可以看到有个LoggingApplicationListener:

public class LoggingApplicationListener implements GenericApplicationListener{}

可以看到它会监听Spring的事件,关键逻辑如下:

public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationStartedEvent) {
            onApplicationStartedEvent((ApplicationStartedEvent) 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();
        }
    }

首先看看它的** onApplicationStartedEvent**方法:

    private void onApplicationStartedEvent(ApplicationStartedEvent event) {
        this.loggingSystem = LoggingSystem
                .get(event.getSpringApplication().getClassLoader());
        this.loggingSystem.beforeInitialize();
    }
  public void beforeInitialize() {
        LoggerContext loggerContext = getLoggerContext();
        if (isAlreadyInitialized(loggerContext)) {
            return;
        }
        super.beforeInitialize();
        loggerContext.getConfiguration().addFilter(FILTER);
    }
  private LoggerContext getLoggerContext() {
        return (LoggerContext) LogManager.getContext(false);
    }

LogManager.getContext(false)实际上会调用Log4jContextFactory的getContext方法:

public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext,
                                final boolean currentContext, final URI configLocation, final String name) {
    final LoggerContext ctx = selector.getContext(fqcn, loader, currentContext, configLocation);
    if (externalContext != null && ctx.getExternalContext() == null) {
        ctx.setExternalContext(externalContext);
    }
    if (name != null) {
        ctx.setName(name);
    }
    if (ctx.getState() == LifeCycle.State.INITIALIZED) {
        if (configLocation != null || name != null) {
            ContextAnchor.THREAD_CONTEXT.set(ctx);
            final Configuration config = ConfigurationFactory.getInstance().getConfiguration(name, configLocation);
            LOGGER.debug("Starting LoggerContext[name={}] from configuration at {}", ctx.getName(), configLocation);
            ctx.start(config);
            ContextAnchor.THREAD_CONTEXT.remove();
        } else {
            ctx.start();
        }
    }
    return ctx;
}

而ConfigurationFactory就会从log4j.configurationFile获取配置文件路径执行初始化;既然已经加载了-Dlog4j.configurationFile指定的配置文件,那为什么没最终生效呢?继续看下去:

private void onApplicationEnvironmentPreparedEvent(
        ApplicationEnvironmentPreparedEvent event) {
    if (this.loggingSystem == null) {
        this.loggingSystem = LoggingSystem
                .get(event.getSpringApplication().getClassLoader());
    }
    initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
}

protected void initialize(ConfigurableEnvironment environment,
        ClassLoader classLoader) {
    new LoggingSystemProperties(environment).apply();
    LogFile logFile = LogFile.get(environment);//读取logging.file和logging.path参数
    if (logFile != null) {
        logFile.applyToSystemProperties();//添加系统属性LOG_PATH、LOG_FILE
    }
    initializeEarlyLoggingLevel(environment); //如果在application.yml或application.properties中定义了debug或trace,则设置日志级别为DEBUG或TRACE
       //关键方法,调用LoggingSystem的initialize方法下面单独说明
    initializeSystem(environment, this.loggingSystem, logFile);
    initializeFinalLoggingLevels(environment, this.loggingSystem);
    registerShutdownHookIfNecessary(environment, this.loggingSystem);
}

Log4J2LoggingSystem的initialize方法逻辑如下:

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

继续看super.initialize(initializationContext, configLocation, logFile);实现:

public void initialize(LoggingInitializationContext initializationContext,
        String configLocation, LogFile logFile) {
    if (StringUtils.hasLength(configLocation)) {//如果通过logging.config指定了log4j2配置文件,则采用该配置文件
        initializeWithSpecificConfig(initializationContext, configLocation, logFile);
        return;
    }
       //采用默认配置
    initializeWithConventions(initializationContext, logFile);
}

如果配置了logging.config属性,最终会通过如下代码重新配置log4j2:

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

          //关键方法,采用新的配置文件重新配置log4j2
            ctx.start(ConfigurationFactory.getInstance().getConfiguration(source));
        }
        catch (Exception ex) {
            throw new IllegalStateException(
                    "Could not initialize Log4J2 logging from " + location, ex);
        }
    }

如果没有指定,会调用initializeWithConventions方法:

private void initializeWithConventions(
        LoggingInitializationContext initializationContext, LogFile logFile) {
       //根据支持的格式,在classpath下查找log4j2.yaml、log4j2.yml、log4j2.json、log4j2.jsn和log4j2.xml文件,如果找到则调用getLoggerContext().reconfigure()重现初始化log4j2,防止某些属性文件发生变化
    String config = getSelfInitializationConfig();
    if (config != null && logFile == null) {
        // self initialization has occurred, reinitialize in case of property changes
        reinitialize(initializationContext);
        return;
    }
    if (config == null) {//如果找不到上述配置文件,则在classpath下查找log4j2-spring.yaml、log4j2-spring.yml、log4j2-spring.json、log4j2-spring.jsn和log4j2-spring.xml文件
        config = getSpringInitializationConfig();
    }
    if (config != null) {//如果找到以spring结尾的配置文件,调用ctx.start重新配置log4j2
        loadConfiguration(initializationContext, config, logFile);
        return;
    }
       //如果前面的查找都失败了,则加载默认配置;如果定义了log日志文件路径,则加载log4j2-file.xml,否则加载log4j2.xml
    loadDefaults(initializationContext, logFile);
}

在Spring boot jar包的org.springframework.boot.logging.log4j2包下面,有两个配置文件log4j2.xml和log4j2-file.xml,这是默认的log4j2配置文件,文件内容如下:



    
        ????
        %xwEx
        %5p
        %clr{%d{yyyy-MM-dd HH:mm:ss.SSS}}{faint} %clr{${LOG_LEVEL_PATTERN}} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}
    
    
        
            
        
    
    
        
        
        
        
        
        
        
        
        
        
        
        
        
            
        
    




    
        ????
        %xwEx
        %5p
        %d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN} ${sys:PID} --- [%t] %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}
    
    
        
            
        
        
            
                ${LOG_PATTERN}
            
            
                
            
        
    
    
        
        
        
        
        
        
        
        
        
        
        
        
        
            
            
        
    


你可能感兴趣的:(spring boot log4j2配置不生效)