Log4j升級(jí)Log4j2中遇到的一些問題

最近項(xiàng)目為了對(duì)接docker,升級(jí)日志組件,但是由于項(xiàng)目用到的一些東西版本比較低,所以遇到了一些問題。
項(xiàng)目版本說明:

  • Spring版本 3.2.9
  • JDK版本 1.7
  • Servlet版本 2.5

日志改造說明

1. maven依賴

    <!--log4j2日志配置-->
    <!--橋接使用log4j的項(xiàng)目-->
    <dependency>
      <artifactId>log4j-1.2-api</artifactId>
      <groupId>org.apache.logging.log4j</groupId>
      <version>2.11.1</version>
    </dependency>
<!--   使用slf4j作為統(tǒng)一日志接口-->
    <dependency>
      <artifactId>slf4j-api</artifactId>
      <groupId>org.slf4j</groupId>
      <version>1.7.25</version>
    </dependency>
<!--   log4j2依賴-->
    <dependency>
      <artifactId>log4j-core</artifactId>
      <groupId>org.apache.logging.log4j</groupId>
      <version>2.11.1</version>
    </dependency>
<!--    橋接使用common-logging的項(xiàng)目-->
    <dependency>
      <artifactId>log4j-jcl</artifactId>
      <exclusions>
        <exclusion>
          <artifactId>commons-logging</artifactId>
          <groupId>commons-logging</groupId>
        </exclusion>
      </exclusions>
      <groupId>org.apache.logging.log4j</groupId>
      <version>2.11.1</version>
    </dependency>

<!--    讓slf4j選擇log4j2作為日志組件實(shí)現(xiàn)-->
    <dependency>
      <artifactId>log4j-slf4j-impl</artifactId>
      <groupId>org.apache.logging.log4j</groupId>
      <version>2.11.1</version>

    </dependency>
<!--    web項(xiàng)目依賴-->
    <dependency>
      <artifactId>log4j-web</artifactId>
      <groupId>org.apache.logging.log4j</groupId>
      <version>2.11.1</version>

    </dependency>
    <!-- https://mvnrepository.com/artifact/com.lmax/disruptor -->
    <dependency>
      <artifactId>disruptor</artifactId>
      <groupId>com.lmax</groupId>
      <version>3.4.2</version>
    </dependency>

關(guān)于slf4j版本的說明,因?yàn)槭褂玫膌og4j2版本較低,因?yàn)椤?.8”大版的slf4j修改了logger的綁定機(jī)制,需要日志框架做修改來適配“1.8”大版的slf4j,但是目前“2.11.1”的log4j也還沒有做適配。使用的各種橋接包都是為了統(tǒng)一日志組件,其實(shí)應(yīng)用的原理無非是SPI。(一定要排除所有的log4j的jar包

2. web.xml 改造

<!-- 日志記錄 -->
    <context-param>
        <param-name>log4jConfiguration</param-name>
        <param-value>classpath:META-INF/log/log4j2.xml</param-value>
    </context-param>
<!-- log4j2-begin -->
    <listener>
        <listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class>
    </listener>
    <filter>
        <filter-name>log4jServletFilter</filter-name>
        <filter-class>org.apache.logging.log4j.web.Log4jServletFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>log4jServletFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>

這里出現(xiàn)的問題比較多,我一一說明,

  • servlet版本
    并非maven中依賴的servlet的版本就是servlet的版本,要以web.xml中xsd為準(zhǔn),采用servlet3.0以及以上的版本是不需要進(jìn)行上述配置的,采用官方文檔的描述,

Log4j 2 "just works" in Servlet 3.0 and newer web applications. It is capable of automatically starting when the application deploys and shutting down when the application undeploys. Thanks to the [ServletContainerInitializer] API added to Servlet 3.0, the relevantServletContextListener classes can be registered dynamically on web application startup.
當(dāng)采用servlet2.5的時(shí)候,一定要按照上面的配置進(jìn)行,詳細(xì)的配置請(qǐng)參考官方文檔log4j2web配置。關(guān)于日志的配置這里不做敘述,請(qǐng)參考官方文檔。

問題

tomcat服務(wù)啟動(dòng)的時(shí)候打印出了一段錯(cuò)誤日志如下,

ERROR StatusLogger No Log4j 2 configuration file found. Using default configuration (logging only errors to the console), or user programmatically provided configurations. Set system property 'log4j2.debug' to show Log4j 2 internal initialization logging. See https://logging.apache.org/log4j/2.x/manual/configuration.html for instructions on how to configure Log4j 2
上面可以看到其實(shí)在web.xml中是配置了log4jConfiguration的,所以日志配置文件是一定存在的,在確定命名以及路徑都沒有問題之后。我特意找了log4j2的源碼進(jìn)行查看,這段錯(cuò)誤日志是在org.apache.logging.log4j.core.config.ConfigurationFactory.Factory#getConfiguration(org.apache.logging.log4j.core.LoggerContext, java.lang.String, java.net.URI)中輸出的,這說明日志組件初始化的過程一定出現(xiàn)了問題,仔細(xì)查看web.xml中配置的所有內(nèi)容,能夠引發(fā)這個(gè)問題的只有l(wèi)istener,一共配置了兩個(gè)listener,一個(gè)是spring容器,一個(gè)便是日志組件,而Spring容器是先于日志容器初始化的,于是在兩個(gè)監(jiān)聽器的contextInitialized()方法中打了斷點(diǎn),發(fā)現(xiàn)果然在spring容器初始化的初始化了日志組件,spring內(nèi)部使用的common-logging組件,由于使用了jcl進(jìn)行橋接,所以會(huì)調(diào)用log4j2組件進(jìn)行初始化,問題就出在這里,我們看下圖,

spring容器初始化

spring初始化調(diào)用順序

log4j2獲取配置
由于先初始化了spring容器,spring容器內(nèi)部調(diào)用了日志初始化,所以我們?cè)趙eb.xml中配置的contextConfigLocation便不起作用,(有興趣的可以看一下這段代碼)導(dǎo)致log4j2找不到配置文件,最終打印了錯(cuò)誤日志。
在spring容器初始化之后,按照我們配置的listener的順序,輪到了日志組件的初始化,如下圖,
log4j2初始化
log4j2在檢查到日志context已經(jīng)初始化后之后會(huì)進(jìn)行重新配置context,這個(gè)時(shí)候我們配置的contextConfigLocation才重新起到了作用,關(guān)于詳細(xì)的過程,大家可以查看源碼。
在這之后,日志回復(fù)正常輸出,雖然對(duì)程序的影響不大,但是在logcontext自身初始化之前,打印的日志是不正常的,放一張圖給大家看
image.png

!所以一定要按照官方說明,先初始化日志組件,servlet3.0中,日志組件是自動(dòng)初始化的,在Log4jServletContainerInitializer中可以看到這樣一段注釋,In a Servlet 3.0 or newer environment, this initializer is responsible for starting up Log4j logging before anything else happens in application initialization. For consistency across all containers, if the effective Servlet major version of the application is less than 3.0, this initializer does nothing,這也說明了日志初始化的順序

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容