老闆下了死命令,要把日誌系統切換到Logback

沉默王二發表於2020-11-03

Log4j 介紹過了,SLF4J 也介紹過了,那接下來,你懂的,Logback 就要隆重地登場了,畢竟它哥仨有一個爹,那就是巨佬 Ceki Gulcu。

就在昨天,老闆聽我說完 Logback 有多牛逼之後,徹底動心了,對我下了死命令,“這麼好的日誌系統,你還不趕緊點,把它切換到我們的專案當中!”

我們專案之前用的 Log4j,在我看來,已經足夠用了,畢竟是小公司,效能上的要求沒那麼苛刻。

01、Logback 強在哪

1)非常自然地實現了 SLF4J,不需要像 Log4j 和 JUL 那樣加一個適配層。

2)Spring Boot 的預設日誌框架使用的是 Logback。一旦某款工具庫成為了預設選項,那就說明這款工具已經超過了其他競品。

注意看下圖(證據找到了,來自 Spring Boot 官網):

也可以通過原始碼的形式看得到:

3)支援自動重新載入配置檔案,不需要另外建立掃描執行緒來監視。

4)既然是巨佬的新作,那必然在效能上有了很大的提升,不然呢?

02、Logback 使用示例

第一步,在 pom.xml 檔案中新增 Logback 的依賴:

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

Maven 會自動匯入另外兩個依賴:

logback-core 是 Logback 的核心,logback-classic 是 SLF4J 的實現。

第二步,來個最簡單的測試用例:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author 微信搜「沉默王二」,回覆關鍵字 PDF
 */

public class Test {
    static Logger logger = LoggerFactory.getLogger(Test.class);
    public static void main(String[] args) {
        logger.debug("logback");
    }
}

Logger 和 LoggerFactory 都來自 SLF4J,所以如果專案是從 Log4j + SLF4J 切換到 Logback 的話,此時的程式碼是零改動的。

執行 Test 類,可以在控制檯看到以下資訊:

12:04:20.149 [main] DEBUG com.itwanger.Test - logback

在沒有配置檔案的情況下,一切都是預設的,Logback 的日誌資訊會輸出到控制檯。可以通過 StatusPrinter 來列印 Logback 的內部資訊:

LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);

在 main 方法中新增以上程式碼後,再次執行 Test 類,可以在控制檯看到以下資訊:

12:59:22.314 [main] DEBUG com.itwanger.Test - logback
12:59:22,261 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
12:59:22,262 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
12:59:22,262 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.xml]
12:59:22,268 |-INFO in ch.qos.logback.classic.BasicConfigurator@5e853265 - Setting up default configuration.

也就是說,Logback 會在 classpath 路徑下先尋找 logback-test.xml 檔案,沒有找到的話,尋找 logback.groovy 檔案,還沒有的話,尋找 logback.xml 檔案,都找不到的話,就輸出到控制檯。

一般來說,我們會在本地環境中配置 logback-test.xml,在生產環境下配置 logback.xml。

第三步,在 resource 目錄下增加 logback-test.xml 檔案,內容如下所示:

<configuration debug="true">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %relative [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

Logback 的配置檔案非常靈活,最基本的結構為 <configuration> 元素,包含 0 或多個 <appender> 元素,其後跟 0 或多個 <logger> 元素,其後再跟最多隻能存在一個的 <root> 元素。

1)配置 appender,也就是配置日誌的輸出目的地,通過 name 屬性指定名字,通過 class 屬性指定目的地:

  • ch.qos.logback.core.ConsoleAppender:輸出到控制檯。
  • ch.qos.logback.core.FileAppender:輸出到檔案。
  • ch.qos.logback.core.rolling.RollingFileAppender:檔案大小超過閾值時產生一個新檔案。

除了輸出到本地,還可以通過 SocketAppender 和 SSLSocketAppender 輸出到遠端裝置,通過 SMTPAppender 輸出到郵件。甚至可以通過 DBAppender 輸出到資料庫中。

encoder 負責把日誌資訊轉換成位元組陣列,並且把位元組陣列寫到輸出流。

pattern 用來指定日誌的輸出格式:

  • %d:輸出的時間格式。
  • %thread:日誌的執行緒名。
  • %-5level:日誌的輸出級別,填充到 5 個字元。比如說 info 只有 4 個字元,就填充一個空格,這樣日誌資訊就對齊了。

反例(沒有指定 -5 的情況):

  • %logger{length}:logger 的名稱,length 用來縮短名稱。沒有指定表示完整輸出;0 表示只輸出 logger 最右邊點號之後的字串;其他數字表示輸出小數點最後邊點號之前的字元數量。
  • %msg:日誌的具體資訊。
  • %n:換行符。
  • %relative:輸出從程式啟動到建立日誌記錄的時間,單位為毫秒。

2)配置 root,它只支援一個屬性——level,值可以為:TRACE、DEBUG、INFO、WARN、ERROR、ALL、OFF。

appender-ref 用來指定具體的 appender。

3)檢視內部狀態資訊

可以在程式碼中通過 StatusPrinter 來列印 Logback 內部狀態資訊,也可以通過在 configuration 上開啟 debug 來列印內部狀態資訊。

重新執行 Test 類,可以在控制檯看到以下資訊:

13:54:54,718 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback-test.xml] at [file:/Users/maweiqing/Documents/GitHub/JavaPointNew/codes/logbackDemo/target/classes/logback-test.xml]
13:54:54,826 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
13:54:54,828 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
13:54:54,833 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
13:54:54,850 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG
13:54:54,850 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
13:54:54,850 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
13:54:54,851 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@f8c1ddd - Registering current configuration as safe fallback point
13:54:54.853 [main] DEBUG com.itwanger.Test - logback

4)自動過載配置

之前提到 Logback 很強的一個功能就是支援自動過載配置,那想要啟用這個功能也非常簡單,只需要在 configuration 元素上新增 scan=true 即可。

<configuration scan="true">
    ...
</configuration>

預設情況下,掃描的時間間隔是一分鐘一次。如果想要調整時間間隔,可以通過 scanPeriod 屬性進行調整,單位可以是毫秒(milliseconds)、秒(seconds)、分鐘(minutes)或者小時(hours)。

下面這個示例指定的時間間隔是 30 秒:

<configuration scan="true" scanPeriod="30 seconds"
   ...
</configuration>

注意:如果指定了時間間隔,沒有指定時間單位,預設的時間單位為毫秒。

當設定 scan=true 後,Logback 會起一個 ReconfigureOnChangeTask 的任務來監視配置檔案的變化。

03、把 log4j.properties 轉成 logback-test.xml

如果你的專案以前用的 Log4j,那麼可以通過下面這個網址把 log4j.properties 轉成 logback-test.xml:

http://logback.qos.ch/translator/

把之前 log4j.properties 的內容拷貝一份:

### 設定###
log4j.rootLogger = debug,stdout,D,E

### 輸出資訊到控制檯 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 輸出DEBUG 級別以上的日誌到=debug.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = debug.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG 
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 輸出ERROR 級別以上的日誌到=error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =error.log 
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR 
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

貼上到該網址的文字域:

點選「Translate」,可以得到以下內容:

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

<!-- For assistance related to logback-translator or configuration  -->
<!-- files in general, please contact the logback user mailing list -->
<!-- at http://www.qos.ch/mailman/listinfo/logback-user             -->
<!--                                                                -->
<!-- For professional support please see                            -->
<!--    http://www.qos.ch/shop/products/professionalSupport         -->
<!--                                                                -->
<configuration>
  <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
    <Target>System.out</Target>
    <encoder>
      <pattern>[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n</pattern>
    </encoder>
  </appender>
  <appender name="D" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!--See http://logback.qos.ch/manual/appenders.html#RollingFileAppender-->
    <!--and http://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy-->
    <!--for further documentation-->
    <Append>true</Append>
    <File>debug.log</File>
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n</pattern>
    </encoder>
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
      <level>DEBUG</level>
    </filter>
  </appender>
  <appender name="E" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!--See http://logback.qos.ch/manual/appenders.html#RollingFileAppender-->
    <!--and http://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy-->
    <!--for further documentation-->
    <File>error.log</File>
    <Append>true</Append>
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n</pattern>
    </encoder>
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
      <level>ERROR</level>
    </filter>
  </appender>
  <root level="debug">
    <appender-ref ref="stdout"/>
    <appender-ref ref="D"/>
    <appender-ref ref="E"/>
  </root>
</configuration>

可以確認一下內容,發現三個 appender 都在。

但是呢,轉換後的檔案並不能直接使用,需要稍微做一些調整,因為:

第一,日誌的格式化有細微的不同,Logback 中沒有 %l

第二,RollingFileAppender 需要指定 RollingPolicy 和 TriggeringPolicy,前者負責日誌的滾動功能,後者負責日誌滾動的時機。如果 RollingPolicy 也實現了 TriggeringPolicy 介面,那麼只需要設定 RollingPolicy 就好了。

TimeBasedRollingPolicy 和 SizeAndTimeBasedRollingPolicy 是兩種最常用的滾動策略。

TimeBasedRollingPolicy 同時實現了 RollingPolicy 與 TriggeringPolicy 介面,因此使用 TimeBasedRollingPolicy 的時候就可以不指定 TriggeringPolicy。

TimeBasedRollingPolicy 可以指定以下屬性:

  • fileNamePattern,用來定義檔案的名字(必選項)。它的值應該由檔名加上一個 %d 的佔位符。%d 應該包含 java.text.SimpleDateFormat 中規定的日期格式,預設是 yyyy-MM-dd。滾動週期是通過 fileNamePattern 推斷出來的。

  • maxHistory,最多保留多少數量的日誌檔案(可選項),將會通過非同步的方式刪除舊的檔案。比如,你指定按月滾動,指定 maxHistory = 6,那麼 6 個月內的日誌檔案將會保留,超過 6 個月的將會被刪除。

  • totalSizeCap,所有日誌檔案的大小(可選項)。超出這個大小時,舊的日誌檔案將會被非同步刪除。需要配合 maxHistory 屬性一起使用,並且是第二條件。

來看下面這個 RollingFileAppender 配置:

<appender name="D" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>debug.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!--            按天滾動 -->
        <fileNamePattern>debug.%d{yyyy-MM-dd}.log</fileNamePattern>
        <!--            儲存 30 天的歷史記錄,最大大小為 30GB -->
        <maxHistory>30</maxHistory>
        <totalSizeCap>3GB</totalSizeCap>
    </rollingPolicy>
    <encoder>
        <pattern>%relative [%thread] %level %logger{35} - %msg%n</pattern>
    </encoder>
</appender>

基於按天滾動的檔案策略,最多保留 30 天,最大大小為 30G。

SizeAndTimeBasedRollingPolicy 比 TimeBasedRollingPolicy 多了一個日誌檔案大小設定的屬性:maxFileSize,其他完全一樣。

基於我們對 RollingPolicy 的瞭解,可以把 logback-test.xml 的內容調整為以下內容:

<configuration>
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <Target>System.out</Target>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
<appender name="D" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <Append>true</Append>
    <File>debug.log</File>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!--            按天輪轉 -->
        <fileNamePattern>debug.%d{yyyy-MM-dd}.log</fileNamePattern>
        <!--            儲存 30 天的歷史記錄,最大大小為 30GB -->
        <maxHistory>30</maxHistory>
        <totalSizeCap>3GB</totalSizeCap>
    </rollingPolicy>
    <encoder>
        <pattern>%relative [%thread] %-5level %logger{35} - %msg%n</pattern>
    </encoder>
</appender>
    <appender name="E" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>error.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--            按天輪轉 -->
            <fileNamePattern>error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!--            儲存 30 天的歷史記錄,最大大小為 30GB -->
            <maxHistory>30</maxHistory>
            <totalSizeCap>3GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
    </appender>
    <root level="debug">
        <appender-ref ref="stdout"/>
        <appender-ref ref="D"/>
        <appender-ref ref="E"/>
    </root>
</configuration>

修改 Test 類的內容:

public class Test {
    static Logger logger = LoggerFactory.getLogger(Test.class);
    public static void main(String[] args) {
        logger.debug("logback");
        logger.error("logback");
    }
}

執行後,可以在 target 目錄下看到兩個檔案:debug.log 和 errror.log。

到此為止,專案已經從 Log4j 切換到 Logback 了,過程非常的絲滑順暢,嘿嘿。

04、Logback 手冊

Logback 的官網上是有一份手冊的,非常詳細,足足 200 多頁,只不過是英文版的。小夥伴們可以看完我這篇文章入門實操的 Logback 教程後,到下面的地址看官方手冊。

http://logback.qos.ch/manual/index.html

如果英文閱讀能力有限的話,可以到 GitHub 上檢視雷鋒翻譯的中文版:

https://github.com/itwanger/logback-chinese-manual

當然了,還有一部分小夥伴喜歡看離線版的 PDF,我已經整理好了:

連結:https://pan.baidu.com/s/16FrbwycYUUIfKknlLhRKYA 密碼:bptl

實話實話吧,白嫖的感覺就是舒服,趕緊去下載吧!日常求個三連,謝謝你勤勞的手指,嘿嘿。

相關文章