Log4j源代碼學習


  了解log4j的源代碼來源於項目中一次需求,我們想將系統所有的warn日志統一收集到common-warn.log的日志中去,以便於系統對其進行監控處理。於是模擬自動生成的error配置完成了warn的配置,但是測試發現common-warn.log中竟然有error日志,而且業務的正常日志中竟然也存在error和warn日志。這樣相當於日志重復打了好多地方,無疑增加了日志量,同時增加了磁盤消耗。

原始配置:

 <appender name="DEFAULT-APPENDER" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="file" value="${log_root}/${sys_host_name}/app-service.log"/>
        <param name="append" value="true"/>
        <param name="encoding" value="UTF-8"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
                   value="%d [%X{loginUserEmail}/%X{loginUserID}/%X{remoteAddr}/%X{clientId} - %X{requestURIWithQueryString}] %-5p %c{2} - %m%n"/>
        </layout>
    </appender>

    <!-- [公共Appender] 匯總錯誤 -->
    <appender name="ERROR-APPENDER" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="file" value="${log_root}/${sys_host_name}/common-error.log"/>
        <param name="append" value="true"/>
        <param name="encoding" value="UTF-8"/>
        <param name="threshold" value="error"/>`
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
                   value="%d [%X{loginUserEmail}/%X{loginUserID}/%X{remoteAddr}/%X{clientId} - %X{requestURIWithQueryString}] %-5p %c{2} - %m%n"/>
        </layout>
    </appender>

    <!-- [公共Appender] 匯總警告 -->
    <appender name="WARN-APPENDER" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="file" value="${log_root}/${sys_host_name}/common-warn.log"/>
        <param name="append" value="true"/>
        <param name="encoding" value="UTF-8"/>
        <param name="threshold" value="WARN"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
                   value="%d [%X{loginUserEmail}/%X{loginUserID}/%X{remoteAddr}/%X{clientId} - %X{requestURIWithQueryString}] %-5p %c{2} - %m%n"/>
        </layout>
    </appender>

    <!-- [應用Logger]  - 默認 -->
    <logger name="APP-LOG" additivity="false">
        <level value="${log_level}"/>
        <appender-ref ref="DEFAULT-APPENDER"/>
        <appender-ref ref="WARN-APPENDER"/>
        <appender-ref ref="ERROR-APPENDER"/>
    </logger>

  


所以我的核心訴求就是:將小於等於info的日志打印到app-service.log,將error打印到common-error.log, 將warn打印到common-warn.log。

1. Log4j核心類

     核心抽象:

  • Logger 用於對日志記錄行為的抽象,提供記錄不同級別日志的統一接口;
  • Level對日志級別的抽象;
  • Appender是對記錄日志形式的抽象,標示了日志打印的目的地;
  • Layout是對日志行格式的抽象;
  • LoggingEvent是對一次日志記錄過程中所需要的信息的抽象,可以理解成一個上下文;

  整個日志打印的過程可以理解為Loger拿着LoggingEvent去找Appender, 讓Appender按照Layout的形式將日志打印到指定的位置。 而Level起的啥作用呢? Logger和Appender都是有原則的不能說你讓我打印我就打印,必須滿足我的規則我才給你打印,這里的規則就是指Level(出來混都是要講原則的)。

核對類圖:

 

 


2. 初始化過程

  核心邏輯在LogManager的靜態代碼塊,根據配置信息解析初始化Logger,Appender和Layout等核心組件;

     

 

 

3. 日志打印  

  核心的日志打印流程, 中間還有很多控制的細節,具體可以看源代碼:

     

 

4. 日志控制

  回到我們的出發點,如何實現:
  將小於等於info的日志打印到app-service.log,將error打印到common-error.log, 將warn打印到common-warn.log。

  • 1. 重寫Appender中2.5.2的方法(isAsSevereAsThreshold),自定義三個Appender的類,Appender1判斷滿足小於等於info的LoggingEvent才進行打印,目標指定為app-service.log;Appender2判斷滿足等於warn的LoggingEvent才進行打印,目標指定為common-warn.log;Appender3判斷滿足等於error的LoggingEvent才進行打印,目標指定為common-error.log;這樣能夠滿足我們的需求,但是這樣就會存兩個問題
    •  存在一個潛規則就是:開發在配置的時候必須要使用自定義的Appender才可以滿足,如果某同學使用默認的話,就可能存在日志打印錯亂,影響監控;
    •  如果某個開發在Logger配置的時候忘記指定error的Appender,那么error日志將不會打印,存在日志丟失,風險很大;
  • 2. 添加2.5.3中提到的Filter配置,進行Appender過濾。log4j支持DenyAllFilter,LevelMatchFilter,LevelRangeFilter,StringMatchFilter的配置。通過LevelRangeFilter可以指定該Appender支持的日志范圍。該方案解決了上個方案a存在的問題,但是無法解決b可能存在的問題;
  • 3. 重寫Logger的日志打印的方法,在遍歷Appender執行的時候,首先篩選出Level匹配(這里只相等)的Appender進行打印,如果沒有匹配的Appender,再按照默認的策略進行打印。這樣可以通過設置Appender的threshold即可實現,而且還可以防止Appender漏配置導致的日志缺失問題。該方案既可以兼容日志丟失,又可以滿足我們的需求,但是logger對象中的AppenderAttachableImpl存儲Appdender的List ,Logger中直接存放的是實現類,沒有提供可擴展的方式,好像不太好實現。 求大神指點。 

      目前項目中使用的方案為2, 同時app-service.log的Appender不配置Filter默認接受error和warn的日志, 但是warn和error通過Fileter進行區分不會相互影響,這樣可以滿足業務監控warn和error日志的需求,不需要修改任何代碼,同時滿足不會丟失日志的問題。但是存在error和warn日志重復打印的問題。 

 

最終的解決方案配置:

    <appender name="DEFAULT-APPENDER" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="file" value="${log_root}/${sys_host_name}/app-default.log"/>
        <param name="append" value="true"/>
        <param name="encoding" value="UTF-8"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
                   value="%d [%X{loginUserEmail}/%X{loginUserID}/%X{remoteAddr}/%X{clientId} - %X{requestURIWithQueryString}] %-5p %c{2} - %m%n"/>
        </layout>
    </appender>

    <!-- [公共Appender] 匯總錯誤 -->
    <appender name="ERROR-APPENDER" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="file" value="${log_root}/${sys_host_name}/common-error.log"/>
        <param name="append" value="true"/>
        <param name="encoding" value="UTF-8"/>
        <param name="threshold" value="error"/>`
        <!-- 僅打印error級別的日志 -->
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="levelMin" value="ERROR" />
            <param name="levelMax" value="ERROR"/>
            <param name="acceptOnMatch" value="true"/>
        </filter>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
                   value="%d [%X{loginUserEmail}/%X{loginUserID}/%X{remoteAddr}/%X{clientId} - %X{requestURIWithQueryString}] %-5p %c{2} - %m%n"/>
        </layout>
    </appender>

    <!-- [公共Appender] 匯總警告 -->
    <appender name="WARN-APPENDER" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="file" value="${log_root}/${sys_host_name}/common-warn.log"/>
        <param name="append" value="true"/>
        <param name="encoding" value="UTF-8"/>
        <param name="threshold" value="WARN"/>
        <!-- 僅打印warn級別的日志 -->
        <filter class="org.apache.log4j.varia.LevelRangeFilter">
            <param name="levelMin" value="WARN" />
            <param name="levelMax" value="WARN"/>
            <param name="acceptOnMatch" value="true"/>
        </filter>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
                   value="%d [%X{loginUserEmail}/%X{loginUserID}/%X{remoteAddr}/%X{clientId} - %X{requestURIWithQueryString}] %-5p %c{2} - %m%n"/>
        </layout>
    </appender>

    <!-- [應用Logger]  - 默認 -->
    <logger name="APP-LOG" additivity="false">
        <level value="${log_level}"/>
        <appender-ref ref="DEFAULT-APPENDER"/>
        <appender-ref ref="WARN-APPENDER"/>
        <appender-ref ref="ERROR-APPENDER"/>
    </logger>

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM