1. 程式人生 > 其它 >Java日誌框架

Java日誌框架

本文主要記錄SLF4J + LogBack該日誌框架組合。

該日誌框架組合的好處有下面幾點:

  • LogBack 自身實現了 SLF4J 的日誌介面,不需要 SLF4J 去做進一步的適配。
  • LogBack 自身是在 Log4J 的基礎上優化而成的,其執行速度和效率都比 LOG4J 高。
  • SLF4J + LogBack 支援佔位符,方便日誌程式碼的閱讀,而 LOG4J 則不支援。

一、LogBack日誌框架

LogBack 被分為3個元件:logback-corelogback-classiclogback-access

  • logback-core 提供了 LogBack 的核心功能,是另外兩個元件的基礎。
  • logback-classic 則實現了 SLF4J 的API,所以當想配合 SLF4J 使用時,需要將 logback-classic 引入依賴中。
  • logback-access 是為了整合Servlet環境而準備的,可提供HTTP-access的日誌介面。

LogBack的日誌記錄資料流是從 Class(Package)到 Logger,再從Logger到Appender,最後從Appender到具體的輸出終端。

LogBack配置檔案可以分為幾個節點,其中 Configuration 是根節點,Appender、Logger、Root是Configuration的子節點。

1、appender節點

負責寫日誌的元件。appender有兩個必要屬性name、class 。

  • name指定appender的名稱;
  • class指定appender的全限定名class,主要包括:
    • ch.qos.logback.core.ConsoleAppender 控制檯輸出
    • ch.qos.logback.core.FileAppender 檔案輸出
    • ch.qos.logback.core.RollingFileAppender 檔案滾動輸出
<?xml version="1.0" encoding="utf-8"?> 
<configuration debug="true" scan="true" scanPeriod="2">
    <!-- conf consoel out -->
    <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
    </appender>

    <!-- conf file out -->
    <appender name="file_out" class="ch.qos.logback.core.FileAppender">
    </appender>
    
    <!-- conf file out -->
    <appender name="file_out" class="ch.qos.logback.core.RollingFileAppender">
    </appender>

    <root></root>
    <logger></logger>
</configuration>

(1) ConsoleAppender

把日誌新增到控制檯,有如下節點:

  • <encoder> : 對日誌進行格式化。
  • <target> : 字串System.out 或者 System.err, 預設 System.out;
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- conf consoel out -->
  <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%date [%thread] %-5level %logger - %message%newline</pattern>
        </encoder>
  </appender>

  <root level="INFO">             
    <appender-ref ref="console_out" />   
  </root>     
</configuration>

(2) FileAppender

把日誌新增到檔案,有如下節點:

  • <file>:被寫入的檔名,可以是相對目錄 , 也可以是絕對目錄 , 如果目錄不存在則會自動建立。
  • <append>:如果是true , 日誌被追加到檔案結尾 , 如果是false, 則清空現存檔案, 預設是true
  • <encoder>:對日誌進行格式化 [具體的轉換符說明請參見官網.]
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <appender name="file_out" class="ch.qos.logback.core.FileAppender">
        <file>logs/debug.log</file>
        <encoder>
            <pattern>%date [%thread] %-5level %logger - %message%newline</pattern>
        </encoder>
    </appender>
</configuration>

(3) rollingFileAppender

滾動紀錄檔案,先將日誌記錄到指定檔案,當符合某種條件時,將日誌記錄到其他檔案,有如下節點:

  • <file>:被寫入的檔名,可以是相對目錄,也可以解決目錄,如果目錄不存在則自動建立。
  • <append>:如果是true,日誌被追加到檔案結尾,如果是false,清空現存檔案,預設是true
  • <encoder>:對日誌進行格式化。
  • <rollingPolicy>:當發生滾動時,決定 RollingFileAppender 的行為,涉及檔案移動和重新命名。
其中rollingPolicy:

TimeBaseRollingPolicy :最常用的滾動策略,根據時間來制定滾動策略,即負責滾動也負責觸發滾動。有如下節點;

  • <fileNamePattern>:必要節點,包含檔案及“%d” 轉換符,“%d”可以包含一個java.text.SimpleDateFormat 制定的時間格式,如:%d{yyyy-MM},如果直接使用%d ,預設格式是 yyyy-MM-dd。
  • <maxHistory>:可選節點,控制保留的歸檔檔案的最大數量,超出數量就刪除舊檔案,假設設定每個月滾動,且 是 6,則只儲存最近6個月的檔案,刪除之前的舊檔案,注意:刪除舊檔案是哪些為了歸檔而建立的目錄也會被刪除。
  • <filenamePattern>:必須包含“%i” 例如:設定最小值,和最大值分別為1和2,命名模式為 log%i.log,會產生歸檔檔案log1.log和log2.log,還可以指定檔案壓縮選項,例如:log%i.log.gz 或者 log%i.log.zip

triggeringPolicy:告知RollingFileAppender,啟用RollingFileAppender滾動

<!-- 03:conf errorAppender out -->
<appender name="errorAppender" class="ch.qos.logback.core.RollingFileAppender">
    <file>logs/error.log</file>
    <!-- 設定滾動策略 -->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
        <!--設定日誌命名模式--> 
        <fileNamePattern>errorFile.%d{yyyy-MM-dd}.log</fileNamePattern>
        <!--最多保留30天log-->
        <maxHistory>30</maxHistory>
    </rollingPolicy>
    <!-- 超過150MB時,觸發滾動策略 -->
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
        <maxFileSize>150</maxFileSize>
    </triggeringPolicy>
    <encoder>
        <pattern>%d [%p] %-5level %logger - %msg%newline</pattern>
    </encoder>
</appender>

2、logger節點

設定某一個包或者具體的某一個類的日誌列印級別,以及指定。logger僅有一個name屬性,兩個可選屬性 level 和 addtivity:

  • name:用來指定受此loger約束的某一個包或者具體的某一個類。
  • level:用來設定列印級別,大小寫無關。可選值有TRACE、DEBUG、INFO、WARN、ERROR、ALL和OFF。還有一個特俗值INHERITED 或者 同義詞NULL,代表強制執行上級的級別。如果未設定此屬性,那麼當前logger將會繼承上級的級別。
  • addtivity:是否向上級logger傳遞列印資訊,預設為true;

可以包含零個或多個元素,表示這個appender將會新增到logger。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- conf consoel out -->
    <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 過濾掉非INFO級別 -->
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!--  conf infoAppender out -->
    <appender name="infoAppender" class="ch.qos.logback.core.RollingFileAppender">
        <file>logs/info.log</file>
        <!-- 設定滾動策略 -->
        <rollingPoliy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
            <!--設定日誌命名模式--> 
            <fileNamePattern>infoFile.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!--最多保留30天log-->
            <maxHistory>30</maxHistory>
        </rollingPoliy>
        <!-- 超過150MB時,觸發滾動策略 -->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <maxFileSize>150</maxFileSize>
        </triggeringPolicy>
        <encoder>
            <pattern>%d [%p] %-5level %logger - %msg%newline</pattern>
        </encoder>
    </appender>
 
    <!-- 新增兩個appender節點 -->
    <logger name="logback.olf.log" level="info">
        <appender-ref ref = "console_out"/>
        <appender-ref ref = "infoAppender"/>
    </logger>
</configuration>

3、root節點

該元素有一個level屬性,沒有name屬性,因為已經被命名 為root。Level屬性的值大小寫無關,其值為下面其中一個字串:TRACE、DEBUG、INFO、 WARN、ERROR、ALL 和 OFF。如果 root 元素沒 有引用任何 appender,就會失去所有 appender。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- conf consoel out -->
    <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 過濾掉非INFO級別 -->
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!-- 01:conf infoAppender out -->
    <appender name="infoAppender" class="ch.qos.logback.core.RollingFileAppender">
        
        <file>logs/info.log</file>
        <!-- 設定滾動策略 -->
        <rollingPoliy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
            <!--設定日誌命名模式--> 
            <fileNamePattern>infoFile.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!--最多保留30天log-->
            <maxHistory>30</maxHistory>
        </rollingPoliy>
        <!-- 超過150MB時,觸發滾動策略 -->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <maxFileSize>150</maxFileSize>
        </triggeringPolicy>
        <encoder>
            <pattern>%d [%p] %-5level %logger - %msg%newline</pattern>
        </encoder>
    </appender>

    <!-- 02:conf debugAppender out -->
    <appender name="debugAppender" class="ch.qos.logback.core.RollingFileAppender">
        <file>logs/debug.log</file>
        <!-- 設定滾動策略 -->
        <rollingPoliy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
            <!--設定日誌命名模式--> 
            <fileNamePattern>debugFile.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!--最多保留30天log-->
            <maxHistory>30</maxHistory>
        </rollingPoliy>
        <!-- 超過150MB時,觸發滾動策略 -->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <maxFileSize>150</maxFileSize>
        </triggeringPolicy>
        <encoder>
            <pattern>%d [%p] %-5level %logger - %msg%newline</pattern>
        </encoder>
    </appender>

    <!-- 03:conf errorAppender out -->
    <appender name="errorAppender" class="ch.qos.logback.core.RollingFileAppender">
        <file>logs/error.log</file>
        <!-- 設定滾動策略 -->
        <rollingPoliy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
            <!--設定日誌命名模式--> 
            <fileNamePattern>errorFile.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!--最多保留30天log-->
            <maxHistory>30</maxHistory>
        </rollingPoliy>
        <!-- 超過150MB時,觸發滾動策略 -->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <maxFileSize>150</maxFileSize>
        </triggeringPolicy>
        <encoder>
            <pattern>%d [%p] %-5level %logger - %msg%newline</pattern>
        </encoder>
    </appender>
 
    <root level="ALL">
        <appender-ref ref="infoAppender"/>
        <appender-ref ref="debugAppender"/>
        <appender-ref ref="errorAppender"/>
    </root>
</configuration>

補充:filter過濾節點

(1)級別過濾器(LevelFilter)

LevelFilter 根據記錄級別對記錄事件進行過濾。如果事件的級別等於配置的級別,過濾 器會根據 onMatch 和 onMismatch 屬性接受或拒絕事件。下面是個配置檔案例子:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- conf consoel out -->
    <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <!-- 過濾掉非INFO級別 -->
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="console_out" />
    </root>
</configuration>

(2) 臨界值過濾器(ThresholdFilter)

ThresholdFilter過濾掉低於指定臨界值的事件。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- conf consoel out -->
    <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">  
        <!-- 過濾掉TRACE和DEBUG級別的日誌 -->
            <level>INFO</level> 
        </filter>
        
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="console_out" />
    </root>
</configuration>

(3)求值過濾器(EvaluatorFilter)

評估是否符合指定的條件

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- conf consoel out -->
    <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.EvaluatorFilter">  
             <evaluator>
             <!--過濾掉所有日誌中不包含hello字元的日誌-->
                <expression>
                    message.contains("hello")
                </expression>
                <onMatch>NEUTRAL</onMatch>
                <onMismatch>DENY</onMismatch>
             </evaluator>
        </filter>
        
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="console_out" />
    </root>
</configuration>

(4) 匹配器(Matchers)

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- conf consoel out -->
    <appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.EvaluatorFilter">  
             <evaluator> 
                <matcher>
                    <Name>odd</Name>
                    <!-- 過濾掉序號為奇數的語句-->
                    <regex>statement [13579]</regex>
                </matcher>
                <expression>odd.matches(formattedMessage)</expression>
                <onMatch>NEUTRAL</onMatch>
                <onMismatch>DENY</onMismatch>
             </evaluator>
        </filter>
        
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="DEBUG">
        <appender-ref ref="console_out" />
    </root>
</configuration>

二、如何進行日誌系統轉換?

在實際的日誌轉換過程中,SLF4J其實是充當了一箇中介的角色。例如當我們一個專案原來是使用LOG4J進行日誌記錄,但是我們要換成LogBack進行日誌記錄。

此時我們需要先將LOG4J轉換成SLF4J日誌系統,再從SLF4J日誌系統轉成LogBack日誌系統。

1、從日誌框架轉向SLF4J

  • jul-to-slf4j:jdk-logging到slf4j的橋樑

  • log4j-over-slf4j:log4j1到slf4j的橋樑

  • jcl-over-slf4j:commons-logging到slf4j的橋樑

2、從SLF4J轉向具體的日誌框架

  • slf4j-jdk14:slf4j到jdk-logging的橋樑

  • slf4j-log4j12:slf4j到log4j1的橋樑

  • log4j-slf4j-impl:slf4j到log4j2的橋樑

  • logback-classic:slf4j到logback的橋樑

  • slf4j-jcl:slf4j到commons-logging的橋樑

例如我們一開始使用的是 Log4J 日誌框架,現在我們希望轉成 LogBack 框架,那麼我們首先需要加入 log4j-over-slf4j.jar 將 Log4J 轉成 SLF4J,之後再加入 logback-classic.jar 將 SLF4J 轉成 LogBack

三、logback.xml配置檔案模板

<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="true" scan="true" scanPeriod="30 seconds"> 

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
    <!-- encoders are  by default assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%level] - %m%n</pattern>
        
        <!-- 常用的Pattern變數,大家可開啟該pattern進行輸出觀察 -->
        <!-- 
          <pattern>
              %d{yyyy-MM-dd HH:mm:ss} [%level] - %msg%n
              Logger: %logger
              Class: %class
              File: %file
              Caller: %caller
              Line: %line
              Message: %m
              Method: %M
              Relative: %relative
              Thread: %thread
              Exception: %ex
              xException: %xEx
              nopException: %nopex
              rException: %rEx
              Marker: %marker
              %n
              
          </pattern>
           -->
    </encoder>
  </appender>
  
  <!-- 按日期區分的滾動日誌 -->
  <appender name="ERROR-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>logs/error.log</file>
      
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
    </encoder>
      
      <filter class="ch.qos.logback.classic.filter.LevelFilter">
      <level>ERROR</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>
      
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!-- daily rollover -->
      <fileNamePattern>error.%d{yyyy-MM-dd}.log.zip</fileNamePattern>

      <!-- keep 30 days' worth of history -->
      <maxHistory>30</maxHistory>
    </rollingPolicy>
  </appender>
  
  <!-- 按檔案大小區分的滾動日誌 -->
  <appender name="INFO-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>logs/info.log</file>
      
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
    </encoder>
    
      <filter class="ch.qos.logback.classic.filter.LevelFilter">
      <level>INFO</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>
      
      <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>info.%i.log</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>3</maxIndex>
    </rollingPolicy>
    
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>5MB</maxFileSize>
    </triggeringPolicy>
    
  </appender>
  
  
  <!-- 按日期和大小區分的滾動日誌 -->
  <appender name="DEBUG-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>logs/debug.log</file>

    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
    </encoder>
      
      <filter class="ch.qos.logback.classic.filter.LevelFilter">
      <level>DEBUG</level>
      <onMatch>ACCEPT</onMatch>
      <onMismatch>DENY</onMismatch>
    </filter>
      
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!-- rollover daily -->
      <fileNamePattern>debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
      
      <timeBasedFileNamingAndTriggeringPolicy
            class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        <!-- or whenever the file size reaches 100MB -->
        <maxFileSize>100MB</maxFileSize>
      </timeBasedFileNamingAndTriggeringPolicy>
      
    </rollingPolicy>
    
  </appender>
  
  
   <!-- 級別閥值過濾 -->
  <appender name="SUM-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>logs/sum.log</file>

    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
    </encoder>
      
    <!-- deny all events with a level below INFO, that is TRACE and DEBUG -->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
      <level>INFO</level>
    </filter>

      
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!-- rollover daily -->
      <fileNamePattern>debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
      
      <timeBasedFileNamingAndTriggeringPolicy
            class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        <!-- or whenever the file size reaches 100MB -->
        <maxFileSize>100MB</maxFileSize>
      </timeBasedFileNamingAndTriggeringPolicy>
      
    </rollingPolicy>
    
  </appender>
  
  
  <root level="debug">
    <appender-ref ref="STDOUT" />
    <appender-ref ref="ERROR-OUT" />
    <appender-ref ref="INFO-OUT" />
    <appender-ref ref="DEBUG-OUT" />
    <appender-ref ref="SUM-OUT" />
  </root>
</configuration>

摘取自:

陳樹義——Java日誌框架