2020年2月6日 星期四

Log4j2不同級別的日誌輸出到不同檔案

使用到Log4j2的時候,如果我們需要根據不同的日誌級別,自動輸出成不同的日誌檔案的話,我們可以在Appender標籤(例如:ConsoleFile),加入ThresholdFilter標籤,來定義需要過濾掉的日誌,只剩下特定級別的日誌。

如果像是以下寫法,不管甚麼級別的日誌,只要被送到Console去,都會印出來:
<Console name="Console" target="SYSTEM_OUT">
                <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n" />
</Console>

如果我們希望Console只會印出info級別的日誌,可以如下寫:
<Console name="Console" target="SYSTEM_OUT">
                <Filters>
                                <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL" />
                                <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" />
                </Filters>
                <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n" />
</Console>

如此,不管我們送甚麼級別的日誌到Console去,它只會印出info級別的日誌,其它級別的日誌都會被過濾掉。
所以,如果今天我們想要把不同級別的日誌,自動輸出成不同的日誌檔案的話,我們的Log4j2.xml可以這麼定義:
<?xml version="1.0" encoding="UTF-8"?>

<Configuration status="WARN">
    <!-- log文件產生在使用者目錄下的Z_Proj_Java中的logs -->
    <properties>
          <property name="filename">${sys:user.home}/Z_Proj_Java/logs/${date:yyyy-MM-dd}</property>
    </properties>

    <Appenders>
          <File name="FileT" fileName="${filename}-trace.log" bufferedIO="true" immediateFlush="true" append="true">
               <ThresholdFilter level="debug" onMatch="DENY" onMismatch="ACCEPT" />
               <PatternLayout pattern="%-5p   %d{yyyy-MM-dd HH:mm:ss} - %m%n" />
          </File>
                               
          <File name="FileD" fileName="${filename}-debug.log" bufferedIO="true" immediateFlush="true" append="true">
                <Filters>
                         <ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL" />
                       <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" />
                </Filters>
                <PatternLayout pattern="%-5p   %d{yyyy-MM-dd HH:mm:ss} - %m%n" />
           </File>
                               
            <File name="FileI" fileName="${filename}-info.log" bufferedIO="true" immediateFlush="true" append="true">
                    <Filters>
                        <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL" />
                        <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY" />
                   </Filters>
                   <PatternLayout pattern="%-5p   %d{yyyy-MM-dd HH:mm:ss} - %m%n" />
              </File>
                               
              <File name="FileW" fileName="${filename}-warn.log" bufferedIO="true" immediateFlush="true" append="true">
                      <Filters>
                         <ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL" />
                         <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY" />
                     </Filters>
                     <PatternLayout pattern="%-5p   %d{yyyy-MM-dd HH:mm:ss} - %m%n" />
               </File>
                               
                <File name="FileE" fileName="${filename}-error.log" bufferedIO="true" immediateFlush="true" append="true">
                       <Filters>
                            <ThresholdFilter level="fatal" onMatch="DENY" onMismatch="NEUTRAL" />
                            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY" />
                       </Filters>
                       <PatternLayout pattern="%-5p   %d{yyyy-MM-dd HH:mm:ss} - %m%n" />
                 </File>
                               
                 <File name="FileF" fileName="${filename}-fatal.log" bufferedIO="true" immediateFlush="true" append="true">
                        <Filters>
                              <ThresholdFilter level="fatal" onMatch="ACCEPT" onMismatch="DENY" />
                         </Filters>
                         <PatternLayout pattern="%-5p   %d{yyyy-MM-dd HH:mm:ss} - %m%n" />
                   </File>
                               
     </Appenders>
     <Loggers>
              <Root level="error">
                        <AppenderRef ref="Console" />
                        <AppenderRef ref="FileT" />
                        <AppenderRef ref="FileD" />
                        <AppenderRef ref="FileI" />
                        <AppenderRef ref="FileW" />
                        <AppenderRef ref="FileE />
                        <AppenderRef ref="FileF" />
                 </Root>
                <Logger name="log4j2.difference_level_file" level="trace" additivity="false">
                          <AppenderRef ref="Console" />
                          <AppenderRef ref="FileT" />
                        <AppenderRef ref="FileD" />
                          <AppenderRef ref="FileI" />
                        <AppenderRef ref="FileW" />
                        <AppenderRef ref="FileE" />
                        <AppenderRef ref="FileF" />
                 </Logger>
      </Loggers>
</Configuration>

這樣的寫法,程式運行時執行如下程式碼:
(logger的取得的部份省略)
……

logger.trace("Entering application.");
logger.debug("Entering application.");
logger.info("Entering application.");
logger.warn("Entering application.");
logger.error("Entering application.");
logger.fatal("Entering application.");

如此將在使用者目錄下的Z_Proj_Java目錄下的logs目錄中,建立以日期為首接著是日誌層級文字的六個檔案,分別是:
xxx-trace.log
xxx-debug.log
xxx-info.log
xxx-warn.log
xxx-error.log
xxx-fatal.log

各級別的日誌檔案中,只會有該級別的日誌訊息。像是

2020-02-06-debug.log檔案中,只會有如下的訊息:
DEBUG   2020-02-06 15:56:14 - Entering application.
DEBUG   2020-02-06 15:56:51 - Entering application.

2020-02-06-info.log檔案中,只會有如下的訊息:
INFO    2020-02-06 15:56:14 - Entering application.
INFO    2020-02-06 15:56:51 - Entering application.

ThresholdFilter標籤的寫法,其中的屬性:
onMatch是指吻合的級別的動作
onMismatch是指不吻合的級別的動作

而它們的屬性值,有三種:
ACCEPT代表接受輸出
DENY代表拒絕輸出
NEUTRAL代表中立,也就是不特別處理,讓它交由下一個ThresholdFilter判斷

如果有多個ThresholdFilter,那么就要放在Filters標籤中,如果只有一個ThresholdFilter,就不需要放在Filters標籤中。而多個ThresholdFilterFilters標籤中的時候,過濾的寫法原則上是要先過濾高級別的,才到低級別的

整理一下各組合的寫法,所得到的結果:
onMatch如果是DENYonMismatchACCEPT:小於level設定的層級會輸出,包含以及大於都不會輸出
onMatch如果是DENYonMismatchDENY:完全不會有輸出
onMatch如果是DENYonMismatchNEUTRAL:小於level設定的層級會輸出,包含以及大於都不會輸出

onMatch如果是ACCEPTonMismatchACCEPT:全部都會輸出
onMatch如果是ACCEPTonMismatchDENY:包含以及大於level設定的層級會輸出,小於不會輸出
onMatch如果是ACCEPTonMismatchNEUTRAL:全部都會輸出

onMatch如果是NEUTRALonMismatchACCEPT:全部都會輸出
onMatch如果是NEUTRALonMismatchDENY:包含以及大於level設定的層級會輸出,小於不會輸出
onMatch如果是NEUTRALonMismatchNEUTRAL:全部都會輸出