spring boot log4j2配置不生效

最近的一個項目采用Spring Boot構(gòu)建,使用了log4j2記錄日志泻肯;按照之前的習(xí)慣,通過-Dlog4j.configurationFile指定log4j2.xml路徑,啟動應(yīng)用疲吸,但意外的發(fā)現(xiàn)日志配置竟然沒有生效;

初步估計是Spring Boot對日志部分進(jìn)行了某種修改前鹅,導(dǎo)致了這個問題摘悴,查看Spring Boot的jar包,發(fā)現(xiàn)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{}

可以看到它會監(jiān)聽Spring的事件舰绘,關(guān)鍵邏輯如下:

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)實際上會調(diào)用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獲取配置文件路徑執(zhí)行初始化蹂喻;既然已經(jīng)加載了-Dlog4j.configurationFile指定的配置文件,那為什么沒最終生效呢捂寿?繼續(xù)看下去:

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參數(shù)
    if (logFile != null) {
        logFile.applyToSystemProperties();//添加系統(tǒng)屬性LOG_PATH口四、LOG_FILE
    }
    initializeEarlyLoggingLevel(environment); //如果在application.yml或application.properties中定義了debug或trace,則設(shè)置日志級別為DEBUG或TRACE
       //關(guān)鍵方法秦陋,調(diào)用LoggingSystem的initialize方法下面單獨(dú)說明
    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);
}

繼續(xù)看super.initialize(initializationContext, configLocation, logFile);實現(xiàn):

public void initialize(LoggingInitializationContext initializationContext,
        String configLocation, LogFile logFile) {
    if (StringUtils.hasLength(configLocation)) {//如果通過logging.config指定了log4j2配置文件蔓彩,則采用該配置文件
        initializeWithSpecificConfig(initializationContext, configLocation, logFile);
        return;
    }
       //采用默認(rèn)配置
    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);

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

如果沒有指定赤嚼,會調(diào)用initializeWithConventions方法:

private void initializeWithConventions(
        LoggingInitializationContext initializationContext, LogFile logFile) {
       //根據(jù)支持的格式,在classpath下查找log4j2.yaml顺又、log4j2.yml更卒、log4j2.json、log4j2.jsn和log4j2.xml文件待榔,如果找到則調(diào)用getLoggerContext().reconfigure()重現(xiàn)初始化log4j2,防止某些屬性文件發(fā)生變化
    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結(jié)尾的配置文件,調(diào)用ctx.start重新配置log4j2
        loadConfiguration(initializationContext, config, logFile);
        return;
    }
       //如果前面的查找都失敗了雕憔,則加載默認(rèn)配置姿骏;如果定義了log日志文件路徑,則加載log4j2-file.xml斤彼,否則加載log4j2.xml
    loadDefaults(initializationContext, logFile);
}

在Spring boot jar包的org.springframework.boot.logging.log4j2包下面分瘦,有兩個配置文件log4j2.xml和log4j2-file.xml,這是默認(rèn)的log4j2配置文件蘸泻,文件內(nèi)容如下:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Properties>
        <Property name="PID">????</Property>
        <Property name="LOG_EXCEPTION_CONVERSION_WORD">%xwEx</Property>
        <Property name="LOG_LEVEL_PATTERN">%5p</Property>
        <Property name="LOG_PATTERN">%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}</Property>
    </Properties>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT" follow="true">
            <PatternLayout pattern="${LOG_PATTERN}" />
        </Console>
    </Appenders>
    <Loggers>
        <Logger name="org.apache.catalina.startup.DigesterFactory" level="error" />
        <Logger name="org.apache.catalina.util.LifecycleBase" level="error" />
        <Logger name="org.apache.coyote.http11.Http11NioProtocol" level="warn" />
        <logger name="org.apache.sshd.common.util.SecurityUtils" level="warn"/>
        <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="warn" />
        <Logger name="org.crsh.plugin" level="warn" />
        <logger name="org.crsh.ssh" level="warn"/>
        <Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="error" />
        <Logger name="org.hibernate.validator.internal.util.Version" level="warn" />
        <logger name="org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration" level="warn"/>
        <logger name="org.springframework.boot.actuate.endpoint.jmx" level="warn"/>
        <logger name="org.thymeleaf" level="warn"/>
        <Root level="info">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Properties>
        <Property name="PID">????</Property>
        <Property name="LOG_EXCEPTION_CONVERSION_WORD">%xwEx</Property>
        <Property name="LOG_LEVEL_PATTERN">%5p</Property>
        <Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN} ${sys:PID} --- [%t] %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>
    </Properties>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT" follow="true">
            <PatternLayout pattern="${LOG_PATTERN}" />
        </Console>
        <RollingFile name="File" fileName="${sys:LOG_FILE}" filePattern="logs/$${date:yyyy-MM}/app-%d{yyyy-MM-dd-HH}-%i.log.gz">
            <PatternLayout>
                <Pattern>${LOG_PATTERN}</Pattern>
            </PatternLayout>
            <Policies>
                <SizeBasedTriggeringPolicy size="10 MB" />
            </Policies>
        </RollingFile>
    </Appenders>
    <Loggers>
        <Logger name="org.apache.catalina.startup.DigesterFactory" level="error" />
        <Logger name="org.apache.catalina.util.LifecycleBase" level="error" />
        <Logger name="org.apache.coyote.http11.Http11NioProtocol" level="warn" />
        <logger name="org.apache.sshd.common.util.SecurityUtils" level="warn"/>
        <Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="warn" />
        <Logger name="org.crsh.plugin" level="warn" />
        <logger name="org.crsh.ssh" level="warn"/>
        <Logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="error" />
        <Logger name="org.hibernate.validator.internal.util.Version" level="warn" />
        <logger name="org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration" level="warn"/>
        <logger name="org.springframework.boot.actuate.endpoint.jmx" level="warn"/>
        <logger name="org.thymeleaf" level="warn"/>
        <Root level="info">
            <AppenderRef ref="Console" />
            <AppenderRef ref="File" />
        </Root>
    </Loggers>
</Configuration>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嘲玫,隨后出現(xiàn)的幾起案子悦施,更是在濱河造成了極大的恐慌,老刑警劉巖去团,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抡诞,死亡現(xiàn)場離奇詭異,居然都是意外死亡土陪,警方通過查閱死者的電腦和手機(jī)昼汗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鬼雀,“玉大人顷窒,你說我怎么就攤上這事≡戳ǎ” “怎么了鞋吉?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長璧疗。 經(jīng)常有香客問我坯辩,道長馁龟,這世上最難降的妖魔是什么崩侠? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮坷檩,結(jié)果婚禮上却音,老公的妹妹穿的比我還像新娘。我一直安慰自己矢炼,他們只是感情好系瓢,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著句灌,像睡著了一般夷陋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胰锌,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天骗绕,我揣著相機(jī)與錄音,去河邊找鬼资昧。 笑死酬土,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的格带。 我是一名探鬼主播撤缴,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼刹枉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了屈呕?” 一聲冷哼從身側(cè)響起微宝,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎虎眨,沒想到半個月后芥吟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡专甩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年钟鸵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涤躲。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖种樱,靈堂內(nèi)的尸體忽然破棺而出蒙袍,到底是詐尸還是另有隱情,我是刑警寧澤嫩挤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站纪吮,受9級特大地震影響巷疼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一嚼沿、第九天 我趴在偏房一處隱蔽的房頂上張望估盘。 院中可真熱鬧,春花似錦骡尽、人聲如沸遣妥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽箫踩。三九已至,卻和暖如春谭贪,著一層夾襖步出監(jiān)牢的瞬間境钟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工俭识, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留慨削,地道東北人。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓套媚,卻偏偏與公主長得像缚态,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子堤瘤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容