淺談LogBack

大嘎好,好久沒有寫博客了,最近996感覺身體被掏空,準備在國慶大假之際更一波。

說道日誌,其實以前我是覺得地位和System.out.print差不多,基本上屬於日誌小白。後面慢慢才發現,哇,原來有日誌,追根溯源起來這麼方便啊!而且日誌基本屬於無腦操作,反正就那些配置,知道就是會,不知道就是不會。今天特地來梳理一下SpringBoot的默認Log框架LogBack。

一、LogBack和Log4j的區別

說道LogBack,就不得不說它的前身Log4j,當時也是火的一塌糊塗。Logback是由log4j創始人設計的,可以說LogBack是log4j的升級版。
那麼都有哪些升級呢?

1、更快的實現:Logback的內核重寫了,在一些關鍵執行路徑上性能提升10倍以上。而且logback不僅性能提升了,初始化內存加載也更小了。
  2、非常充分的測試:Logback經過了幾年,數不清小時的測試。Logback的測試完全不同級別的。
  3、Logback-classic非常自然實現了SLF4j:Logback-classic實現了SLF4j。在使用SLF4j中,你都感覺不到logback-classic。而且因爲logback-classic非常自然地實現了slf4j , 所 以切換到log4j或者其他,非常容易,只需要提供成另一個jar包就OK,根本不需要去動那些通過SLF4JAPI實現的代碼。
  4、非常充分的文檔 官方網站有兩百多頁的文檔。
  5、自動重新加載配置文件,當配置文件修改了,Logback-classic能自動重新加載配置文件。掃描過程快且安全,它並不需要另外創建一個掃描線程。這個技術充分保證了應用程序能跑得很歡在JEE環境裏面。
  6、Lilith是log事件的觀察者,和log4j的chainsaw類似。而lilith還能處理大數量的log數據 。
  7、謹慎的模式和非常友好的恢復,在謹慎模式下,多個FileAppender實例跑在多個JVM下,能 夠安全地寫道同一個日誌文件。RollingFileAppender會有些限制。Logback的FileAppender和它的子類包括 RollingFileAppender能夠非常友好地從I/O異常中恢復。
  8、配置文件可以處理不同的情況,開發人員經常需要判斷不同的Logback配置文件在不同的環境下(開發,測試,生產)。而這些配置文件僅僅只有一些很小的不同,可以通過,和來實現,這樣一個配置文件就可以適應多個環境。
  9、Filters(過濾器)有些時候,需要診斷一個問題,需要打出日誌。在log4j,只有降低日誌級別,不過這樣會打出大量的日誌,會影響應用性能。在Logback,你可以繼續 保持那個日誌級別而除掉某種特殊情況,如alice這個用戶登錄,她的日誌將打在DEBUG級別而其他用戶可以繼續打在WARN級別。要實現這個功能只需加4行XML配置。可以參考MDCFIlter 。
  10、SiftingAppender(一個非常多功能的Appender):它可以用來分割日誌文件根據任何一個給定的運行參數。如,SiftingAppender能夠區別日誌事件跟進用戶的Session,然後每個用戶會有一個日誌文件。
  11、自動壓縮已經打出來的log:RollingFileAppender在產生新文件的時候,會自動壓縮已經打出來的日誌文件。壓縮是個異步過程,所以甚至對於大的日誌文件,在壓縮過程中應用不會受任何影響。
  12、堆棧樹帶有包版本:Logback在打出堆棧樹日誌時,會帶上包的數據。
  13、自動去除舊的日誌文件:通過設置TimeBasedRollingPolicy或者SizeAndTimeBasedFNATP的maxHistory屬性,你可以控制已經產生日誌文件的最大數量。如果設置maxHistory 12,那那些log文件超過12個月的都會被自動移除。

二、LogBack架構

2.1Logger, Appender 和 Layouts

Logback 構建在三個主要的類上:Logger,Appender 和 Layouts。這三個不同類型的組件一起作用能夠讓開發者根據消息的類型以及日誌的級別來打印日誌。

Logger 類作爲 logback-classic 模塊的一部分。Appender 與 Layouts 接口作爲 logback-core 的一部分。作爲一個通用的模塊,logback-core 沒有 logger 的概念。

2.1.1Logger

logger可以理解爲一個日誌實體,Logger 能夠被分成不同的等級。不同的等級(TRACE, DEBUG, INFO, WARN, ERROR)定義ch.qos.logback.classic.Level 類中。

2.1.1.1 logger的level

Level的可見關係如下圖所示,簡單來說就是TRACE, DEBUG, INFO, WARN, ERROR排成一排,依次只能看見自己右邊的等級。所以TRACE等級可以看見所有的,而ERROR只能看見自己。
在這裏插入圖片描述在LogBack的體系中,其實logger的主要屬性就是level,比較簡單,而具體的其他配置則是放在appender中,再把appender和logger綁定。

2.1.2獲取logger

通過 LoggerFactory.getLogger() 可以獲取到具體的 logger 實例,名字相同則返回的 logger 實例也相同。

Logger x = LoggerFactory.getLogger(“wombat”);
Logger y = LoggerFactory.getLogger(“wombat”);

除此以外,還可以
Logger y = LoggerFactory.getLogger(當前類.class);

大多數情況下,我們希望是每個service都有自己的日誌,否則一個系統那麼多模塊,都記到一個log裏,那是要爆炸。我昨天才因爲這個事兒被前輩批評了,囧。

在每個類裏面通過指定全限定類名爲 logger 的名字來實例化一個 logger 是最好也是最簡單的方式。因爲日誌能夠輸出這個 logger 的名字,所以這個命名策略能夠看出日誌的來源是哪裏。雖然這是命名 logger 常見的策略,但是 logback 不會嚴格限制 logger 的命名,你完全可以根據自己的喜好來,你開心就好。

但是,根據類的全限定名來對 logger 進行命名,是目前最好的方式,沒有之一。

2.1.3rootLogger

和JAVA設計一樣,logger也有一個類似於“Object”的共有祖先類,logger如果沒有的屬性,會去往上追溯自己的父類屬性,直到rootLogger。

2.2 Appender 與 Layout

有選擇的啓用或者禁用日誌的輸出只是 logger 的一部分功能。logback 允許日誌在多個地方進行輸出。站在 logback 的角度來說,輸出目的地叫做 appender。appender 包括console、file、remote socket server、MySQL、PostgreSQL、Oracle 或者其它的數據庫、JMS、remote UNIX Syslog daemons 中。

一個 logger 可以有多個 appender。

logger 通過 addAppender 方法來新增一個 appender。對於給定的 logger,每一個允許輸出的日誌都會被轉發到該 logger 的所有 appender 中去。換句話說,appender 從 logger 的層級結構中去繼承疊加性。例如:如果 root logger 添加了一個 console appender,所有允許輸出的日誌至少會在控制檯打印出來。如果再給一個叫做 L 的 logger 添加了一個 file appender,那麼 L 以及 L 的子級 logger 都可以在文件和控制檯打印日誌。可以通過設置 additivity = false 來改寫默認的設置,這樣 appender 將不再具有疊加性。

2.3底層實現初探

在介紹了基本的 logback 組件之後,我們準備介紹一下,當用戶調用日誌的打印方法時,logback 所執行的步驟。現在我們來分析一下當用戶通過一個名爲 com.wombat 的 logger 調用了 info() 方法時,logback 執行了哪些步驟。

第一步:獲取過濾器鏈

如果存在,則 TurboFilter 過濾器會被調用,Turbo 過濾器會設置一個上下文的閥值,或者根據每一條相關的日誌請求信息,例如:Marker, Level, Logger, 消息,Throwable 來過濾某些事件。如果過濾器鏈的響應是 FilterReply.DENY,那麼這條日誌請求將會被丟棄。如果是 FilterReply.NEUTRAL,則會繼續執行下一步,例如:第二步。如果響應是 FilterRerply.ACCEPT,則會直接跳到第三步。

第二步:應用基本選擇規則

在這步,logback 會比較有效級別與日誌請求的級別,如果日誌請求被禁止,那麼 logback 將會丟棄調這條日誌請求,並不會再做進一步的處理,否則的話,則進行下一步的處理。

第三步:創建一個 LoggingEvent 對象

如果日誌請求通過了之前的過濾器,logback 將會創建一個 ch.qos.logback.classic.LoggingEvent 對象,這個對象包含了日誌請求所有相關的參數,請求的 logger,日誌請求的級別,日誌信息,與日誌一同傳遞的異常信息,當前時間,當前線程,以及當前類的各種信息和 MDC。MDC 將會在後續章節進行討論。

第四步:調用 appender

在創建了 LoggingEvent 對象之後,logback 會調用所有可用 appender 的 doAppend() 方法。這些 appender 繼承自 logger 上下文。

所有的 appender 都繼承了 AppenderBase 這個抽象類,並實現了 doAppend() 這個方法,該方法是線程安全的。AppenderBase 的 doAppend() 也會調用附加到 appender 上的自定義過濾器。自定義過濾器能動態的動態的添加到 appender 上,在過濾器章節會詳細討論。

第五步:格式化輸出

被調用的 appender 負責格式化日誌時間。但是,有些 appender 將格式化日誌事件的任務委託給 layout。layout 格式化 LoggingEvent 實例並返回一個字符串。對於其它的一些 appender,例如 SocketAppender,並不會把日誌事件轉變爲一個字符串,而是進行序列化,因爲,它們就不需要一個 layout。

第六步:發送 LoggingEvent

當日志事件被完全格式化之後將會通過每個 appender 發送到具體的目的地。

三、LogBack配置

在應用程序當中使用日誌語句需要耗費大量的精力。根據調查,大約有百分之四的代碼用於打印日誌。即使在一箇中型應用的代碼當中也有成千上萬條日誌的打印語句。考慮到這種情況,我們需要使用工具來管理這些日誌語句。

3.1 logback 的初始化步驟:

1、logback 會在類路徑下尋找名爲 logback-test.xml 的文件。
2、如果沒有找到,logback 會繼續尋找名爲 logback.groovy 的文件。
3、 如果沒有找到,logback 會繼續尋找名爲 logback.xml 的文件。
4、 如果沒有找到,將會通過 JDK 提供的 ServiceLoader 工具在類路徑下尋找文件 META-INFO/services/ch.qos.logback.classic.spi.Configurator,該文件的內容爲實現了 Configurator 接口的實現類的全限定類名。
如果以上都沒有成功,logback 會通過 BasicConfigurator 爲自己進行配置,並且日誌將會全部在控制檯打印出來。

最後一步的目的是爲了保證在所有的配置文件都沒有被找到的情況下,提供一個默認的(但是是非常基礎的)配置(我就經常用這個,哈哈哈哈哈)。

3.2 具體配置

logback 的配置文件非常的靈活,不需要指定 DTD 或者 xml 文件需要的語法。但是,最基本的結構爲 元素,包含 0 或多個 元素,其後跟 0 或多個 元素,其後再跟最多隻能存在一個的 元素。基本結構圖如下:
在這裏插入圖片描述

1、當配置文件更改時,自動加載

默認情況下,一分鐘掃描一次配置文件,看是否有更改。通過 標籤上的 scanPeriod 屬性可以指定掃描週期。掃描週期的時間單位可以是毫秒、秒、分鐘或者小時。

Example:

<configuration scan=“true” scanPeriod=“30 seconds”

</configuration>

2、配置 logger

通過 標籤來過 logger 進行配置,一個 標籤必須包含一個 name 屬性,一個可選的 level 屬性,一個可選 additivity 屬性。additivity 的值爲 true 或 false。level 的值爲 TRACE,DEBUG,INFO,WARN,ERROR,ALL,OFF,INHERITED,NULL。當 level 的值爲 INHERITED 或 NULL 時,將會強制 logger 繼承上一層的級別。

元素至少包含 0 或多個 元素。每一個 appender 通過這種方式被添加到 logger 上。與 log4j 不同的是,logbakc-classic 不會關閉或移除任何之前在 logger 上定義好的的 appender。

3、配置 rootLogger

root logger 通過 元素來進行配置。它只支持一個屬性——level。它不允許設置其它任何的屬性,因爲 additivity 並不適用 root logger。而且,root logger 的名字已經被命名爲 “ROOT”,也就是說也不支持 name 屬性。level 屬性的值可以爲:TRACE、DEBUG、INFO、WARN、ERROR、ALL、OFF,但是不能設置爲 INHERITED 或 NULL。

跟 <logger> 元素類似, 元素可以包含 0 或多個 元素。

4、配置 appender

appender 通過 元素進行配置,需要兩個強制的屬性 name 與 class。name 屬性用來指定 appender 的名字,class 屬性需要指定類的全限定名用於實例化。 元素可以包含 0 或一個 元素,0 或多個 元素,0 或多個 元素。除了這些公共的元素之外, 元素可以包含任意與 appender 類的 JavaBean 屬性相一致的元素。

看個例子:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>myApp.log</file>
        <encoder>
            <pattern>
                %date %level [%thread] %logger{10} [%file:%line] %msg%n
            </pattern>
        </encoder>
    </appender>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>
                %msg%n
            </pattern>
        </encoder>
    </appender>

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

上面聲明瞭兩個appender,並把它們加到了root下面,這樣每個log都會調用這兩個appender。

5、設置 context 的名字

在之前的章節中提到,每一個 logger 都會附加到一個 logger context 上去。默認這個 logger context 的名字爲 “default”。但是你可以通過 設置其它的名字。但是如果設置過一次就不能再設置)。當多個應用輸出日誌到同一個目的地,設置 logger context 的名字可以更好的區分。

舉個栗子:

<configuration>
    <contextName>myAppName</contextName>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d %contextName [%t] %level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>
6、變量的替換

變量的定義

logback 支持變量的定義以及替換,變量有它的作用域。而且,變量可以在配置文件中,外部文件中,外部資源文件中,甚至動態定義。

<configuration>
    <property name="USER_NAME" value="/data/logs" />

    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>${USER_NAME}/myApp.log</file>
        <encoder>
            <pattern>%msg%n</pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="FILE" />
    </root>    
</configuration>
7、appender詳細
7.1 ConsoleAppender

ConsoleAppender 就跟名字顯示的一樣,是將日誌事件附加到控制檯,跟進一步說就是通過 System.out 或者 System.err 來進行輸出。默認通過前者。ConsoleAppender 通過用戶指定的 encoder,格式化日誌事件。Encoder 會在接下來的章節討論。System.out 與 System.err 兩者都是 java.io.PrintStream 類型。因此,它們被包裝在可以進行 I/O 緩存操作的 OutputStreamWriter 中。

<configuration>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender" >
        <!-- encoder 默認使用 ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
        </encoder>    
    </appender>

    <root level="DEBUG">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>
7.2 FileAppender

FileAppender 是 OutputStreamAppender 的子類,將日誌事件輸出到文件中。通過 file 來指定目標文件。如果該文件存在,根據 append 的值,要麼將日誌追加到文件中,要麼該文件被截斷。

<configuration>
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>testFile.log</file>
<!--         將 immediateFlush 設置爲 false 可以獲得更高的日誌吞吐量 -->
        <immediateFlush>true</immediateFlush>
<!--         默認爲 ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="DEBUG">
        <appender-ref ref="FILE" />
    </root>
</configuration>
7.3 RollingFileAppender

RollingFileAppender 繼承自FileAppender,具有輪轉日誌文件的功能。例如,RollingFileAppender 將日誌輸出到 log.txt 文件,在滿足了特定的條件之後,將日誌輸出到另外一個文件。

與 RollingFileAppender 進行交互的有兩個重要的子組件。第一個是 RollingPolicy,它負責日誌輪轉的功能。另一個是 TriggeringPolicy,它負責日誌輪轉的時機。所以 RollingPolicy 負責發生什麼,TriggeringPolicy 負責什麼時候發生。

爲了讓 RollingFileAppender 生效,必須同時設置 RollingPolicy 與 TriggeringPolicy。但是,如果 RollingPolicy 也實現了 TriggeringPolicy 接口,那麼只需要設置前一個就好了。

<configuration>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logFile.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--             按天輪轉 -->
            <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!--             保存 30 天的歷史記錄,最大大小爲 30GB -->
            <maxHistory>30</maxHistory>
            <totalSizeCap>3GB</totalSizeCap>
        </rollingPolicy>

        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>mylog.txt</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--             按天輪轉 -->
            <fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
            <maxFileSize>100MB</maxFileSize>
            <maxHistory>60</maxHistory>
            <totalSizeCap>20GB</totalSizeCap>
        </rollingPolicy>

        <encoder>
            <pattern>%msg%n</pattern>
        </encoder>
    </appender>

    <root level="DEBUG">
        <appender-ref ref="FILE" />
    </root>
</configuration>

其實還有很多其他的appender,比如可以發郵件的,或者直接插入數據庫中的,請讀者自行去官網查看。

8、encoder

PatternLayoutEncoder最爲常用

由於 PatternLayout 是最常用的 layout,logback 使用 PatternLayoutEncoder 來滿足這種用法。它擴展了 LayoutWrappingEncoder,被限制用來包裹 PatternLayout 實例。
用格式化字符串作爲開頭

爲了幫助解析日誌文件,logback 可以將格式化字符串插入到日誌文件的頂部。這個功能默認是關閉的。可以爲相關的 PatternLayoutEncoder 設置 outputPatternAsHeader 屬性的值爲 true 來開啓這個功能。下面是示例:

<appender name="FILE" class="ch.qos.logback.core.FileAppender"> 
  <file>foo.log</file>
  <encoder>
    <pattern>%d %-5level [%thread] %logger{0}: %msg%n</pattern>
    <outputPatternAsHeader>true</outputPatternAsHeader>
  </encoder> 
</appender>

將會在日誌文件中輸出類似下面的日誌:

#logback.classic pattern: %d [%thread] %-5level %logger{36} - %msg%n
2012-04-26 14:54:38,461 [main] DEBUG com.foo.App - Hello world
2012-04-26 14:54:38,461 [main] DEBUG com.foo.App - Hi again
...

以 “#logback.classic pattern” 開頭的行就是新插入的行。

9、layout

layout 是 logback 的組件,負責將日誌事件轉換爲字符串。Layout 接口中的 format() 方法接受一個表示日誌事件的對象 (任何類型) 並返回一個字符串。

定製 Layout

讓我們爲 logback-classic 模塊實現一個簡單但是實用的功能,打印應用啓動所耗費的時間,日誌事件的級別,被綜括號包裹的調用者線程,logger 名,破折號後面跟日誌信息,以及新起一行。

類似下面的輸出:


10489 DEBUG [main] com.marsupial.Pouch - Hello world.

那麼我們需要自己定義一個LayOut

package chapters.layouts;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.LayoutBase;

public class MySampleLayout extends LayoutBase<ILoggingEvent> {

  public String doLayout(ILoggingEvent event) {
    StringBuffer sbuf = new StringBuffer(128);
    sbuf.append(event.getTimeStamp() - event.getLoggingContextVO.getBirthTime());
    sbuf.append(" ");
    sbuf.append(event.getLevel());
    sbuf.append(" [");
    sbuf.append(event.getThreadName());
    sbuf.append("] ");
    sbuf.append(event.getLoggerName();
    sbuf.append(" - ");
    sbuf.append(event.getFormattedMessage());
    sbuf.append(CoreConstants.LINE_SEP);
    return sbuf.toString();
  }
}

然後把寫好的LayOut配置進配置文件

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
      <layout class="chapters.layouts.MySampleLayout" />
    </encoder>
  </appender>

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

再來看一個稍微複雜一點的LayOut類

package chapters.layouts;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.LayoutBase;

public class MySampleLayout2 extends LayoutBase<ILoggingEvent> {

  String prefix = null;
  boolean printThreadName = true;

  public void setPrefix(String prefix) {
    this.prefix = prefix;
  }

  public void setPrintThreadName(boolean printThreadName) {
    this.printThreadName = printThreadName;
  }

  public String doLayout(ILoggingEvent event) {
    StringBuffer sbuf = new StringBuffer(128);
    if (prefix != null) {
      sbuf.append(prefix + ": ");
    }
    sbuf.append(event.getTimeStamp() - event.getLoggerContextVO().getBirthTime());
    sbuf.append(" ");
    sbuf.append(event.getLevel());
    if (printThreadName) {
      sbuf.append(" [");
      sbuf.append(event.getThreadName());
      sbuf.append("] ");
    } else {
      sbuf.append(" ");
    }
    sbuf.append(event.getLoggerName());
    sbuf.append(" - ");
    sbuf.append(event.getFormattedMessage());
    sbuf.append(LINE_SEP);
    return sbuf.toString();
  }
}

同樣的,光寫好LayOut類沒啥用,我們也要在配置文件裏啓用它

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
      <layout class="chapters.layouts.MySampleLayout2"> 
        <prefix>MyPrefix</prefix>
        <printThreadName>false</printThreadName>
      </layout>
    </encoder>
  </appender>

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

注意我們在啓用的時候,自己手動注入了屬性<prefix>MyPrefix</prefix>
和 <printThreadName>false</printThreadName>

10、filter

在 logback-classic 中,有兩種類型的過濾器,regular 過濾器以及 turbo 過濾器。

10.1 Regular 過濾器

reqular 過濾器繼承自 Filter 這個抽象類。本質上它由一個單一的 decide() 方法組成,接收一個 ILoggingEvent 實例作爲參數。

過濾器通過一個有序列表進行管理,並且基於三元邏輯。每個過濾器的 decide(ILoggingEvent event) 被依次調用。這個方法返回 FilterReply 枚舉值中的一個, DENY, NEUTRAL 或者 ACCEPT。如果 decide() 方法返回 DENY,那麼日誌事件會被丟棄掉,並且不會考慮後續的過濾器。如果返回的值是 NEUTRAL,那麼纔會考慮後續的過濾器。如果沒有其它的過濾器了,那麼日誌事件會被正常處理。如果返回值是 ACCEPT,那麼會跳過剩下的過濾器而直接被處理。

在 logback-classic 中,過濾器可以被直接添加到 Appender 實例上。通過將一個或者多個過濾器添加到 appender 上,你可以通過任意標準來過濾日誌事件。例如,日誌消息的內容,MDC 的內容,時間,或者日誌事件的其它部分。


import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;

public class SampleFilter extends Filter<ILoggingEvent> {

  @Override
  public FilterReply decide(ILoggingEvent event) {    
    if (event.getMessage().contains("sample")) {
      return FilterReply.ACCEPT;
    } else {
      return FilterReply.NEUTRAL;
    }
  }
}
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

    <filter class="chapters.filters.SampleFilter" />

    <encoder>
      <pattern>
        %-4relative [%thread] %-5level %logger - %msg%n
      </pattern>
    </encoder>
  </appender>

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

logbcak內置的
<filter>:

LevelFilter: 級別過濾器,根據日誌級別進行過濾。如果日誌級別等於配置級別,過濾器會根據onMath 和 onMismatch接收或拒絕日誌。有以下子節點:

<level>:設置過濾級別

<onMatch>:用於配置符合過濾條件的操作

<onMismatch>:用於配置不符合過濾條件的操作

    <configuration>   
      <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">   
        <filter class="ch.qos.logback.classic.filter.LevelFilter">   
          <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" />   
      </root>   
    </configuration>  
10.2 turbo 過濾器

TurboFilter 對象都繼承 TurboFilter 抽象類。對於 regular 過濾器,它們使用三元邏輯來返回對日誌事件的評估。

總之,它們跟之前提到的過濾工作原理差不多。主要的不同點在於 Filter 與 TurboFilter 對象。

TurboFilter 對象被綁定剛在 logger 上下文中。因此,在使用給定的 appender 以及每次發出的日誌請求都會調用 TurboFilter 對象。因此,turbo 過濾器可以爲日誌事件提供高性能的過濾,即使是在事件被創建之前。

關於過濾器,Logback提供了好幾個out-of-box(開箱即用)的,如果真正要用到,基本那些也夠用了。關於本文未提到的,請移步logback官方文檔。

四、本文參考

logback官網
logback中文網
深夜不說話的博客

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章