使用 Logback 進行條件日誌記錄
1.概述
Logback 日誌框架是 Log4j 專案的後繼者,它提供了其他日誌系統所缺少的一些獨特、靈活和進階的功能。其中一個功能是條件日誌記錄,它使我們能夠控制日誌輸出。當我們在運行時有不同的用例來重定向、控製或抑制日誌時,這很有用。
在本教程中,我們將研究 Logback 框架為條件日誌記錄提供的一些有趣的功能。
2. 設定
Logback 需要 Java 的簡單日誌外觀 (SLF4J) 函式庫作為相依性。但是,由於 SLF4J 是 Logback 的參考實現,我們不需要明確導入它。導入logback-classic
函式庫會自動透過 Maven 的傳遞依賴規則將slf4j-api.jar
和logback-core.jar
拉入專案中。
因此,讓我們將 Logback 庫加入到我們的POM
:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.6</version>
</dependency>
此外,Logback 設定檔中的條件處理需要Janino 函式庫。 Janino 是一個嵌入式 Java 編譯器,可以在執行時動態編譯 Java 程式碼或表達式。讓我們將其添加到我們的POM
中:
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>3.1.12</version>
</dependency>
我們可以從 Maven Central 取得logback-classic
和janino
依賴項的更新版本。
3. 設定檔的條件處理
條件日誌記錄允許我們根據特定條件控制日誌輸出。這對於根據某些運行時場景動態過濾日誌很有用,例如:
- 當應用程式在本機系統上執行時與在伺服器上執行時使用不同的日誌輸出(本機上的常規日誌,伺服器上的結構化日誌)
- 只有當應用程式在生產環境中運行時,才將身份驗證或安全錯誤傳送到監控服務
- 根據應用程式運行的位置使用不同的前綴
Logback 透過對設定檔進行條件處理來支援條件日誌記錄。通常,在不同環境中運行的應用程式每個環境都有許多配置文件,這給更新它們的開發人員帶來了額外的複雜性。開發人員很可能會錯過更新不同環境的設定檔。
此外,這些配置檔案大部分都有共同的部分,只有少數地方有所不同。相反,Logback 提供了某些標籤( <if>
、 <then>
和<else>
元素),我們可以使用這些標籤來獲得所有環境的單一設定檔。
3.1.通用格式
讓我們來看看使用條件語句的一般格式:
<!-- if-then form -->
<if condition="conditional expression">
<then>
...
</then>
</if>
此外,Logback 也支援if-then-else
區塊:
<!-- if-then-else form -->
<if condition="conditional expression">
<then>
...
</then>
<else>
...
</else>
</if>
上述程式碼片段中的「 condition
」是一個 Java 表達式,其中只有系統或上下文屬性可存取。條件表達式包含在if-else
標籤內。此外,我們可以在<configuration>
元素中的任何位置使用條件處理。也支援嵌套的if-then-else
語句。
3.2.使用系統或上下文屬性進行條件日誌記錄
要存取特定的系統或上下文屬性,我們使用property()
或p()
方法的較短等效方法,它們傳回String
值。
例如,要存取鍵為「 ENVIRONMENT
」的屬性,我們可以使用property(“ENVIRONMENT”)
或p(“ENVIRONMENT”)
。當鍵未定義時,此方法傳回空字串。
以下是如何存取系統屬性的範例:
<if condition='property("ENVIRONMENT").contains("PROD")'>
<then>
<appender name="FILE_APPENDER" class="ch.qos.logback.core.FileAppender">
<file>${outputDir}/conditional.log</file>
<encoder>
<pattern>%d %-5level %logger{35} -%kvp- %msg %n</pattern>
</encoder>
</appender>
</then>
</if>
同樣,如果我們想檢查null
值,我們可以使用isNull()
方法:
<if condition='isNull("ENVIRONMENT")'>
<then>
<appender name="CONSOLE_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %-5level %logger{35} -%kvp- %msg %n</pattern>
</encoder>
</appender>
</then>
</if>
此外,我們可以使用isDefined()
方法來驗證屬性是否存在。當我們需要將日誌檔案傳輸到外部伺服器(例如 Logstash)進行集中日誌記錄時, isNull()
和isDefined()
方法都會有所幫助:
<if condition='isDefined("LOG_STASH_URL")'>
<then>
<appender name="LOG_STASH_APPENDER" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<destination>${LOG_STASH_URL}</destination>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<customFields>{"app_name": "TestApp"}</customFields>
</encoder>
</appender>
</then>
</if>
Logback 支援<configuration>
根元素內的任何位置進行條件處理。
4.使用EvaluatorFilter
Logback 提供了多個內建過濾器,可以將它們連結在一起以組成任意複雜的過濾策略。這些過濾器基於三元邏輯並傳回DENY
、 NEUTRAL
或ACCEPT
值之一。
返回DENY
意味著日誌事件被立即刪除。如果為NEUTRAL
,則會查閱下一個過濾器。最後, ACCEPT
表示日誌事件的處理成功,並跳過剩餘的過濾器。
條件日誌記錄也可以透過使用EvaluatorFilter
來實現,它在執行時間評估 Java 表達式。 OnMatch
和OnMismatch
標籤提供傳回三個三元值之一( DENY
、 NEUTRAL
或ACCEPT
)的選項。因此,此 Logback 過濾器可以在運行時評估條件並控制日誌的輸出。
現在,我們可以看到一個簡單的例子來示範EvaluationFilter
的用法:
<configuration>
<appender name="FILTER_APPENDER" class="ch.qos.logback.core.FileAppender">
<file>filtered.log</file>
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator>
<expression>return message.contains("billing");</expression>
</evaluator>
<OnMatch>DENY</OnMatch>
<OnMismatch>NEUTRAL</OnMismatch>
</filter>
<encoder>
<pattern>%d %-4relative [%thread] %-5level %logger -%kvp -%msg%n </pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILTER_APPENDER" />
</root>
</configuration>
如上所示, EvaluatorFilter
尋找要評估的表達式,匹配條件,並將日誌輸出分離到正確的附加器。當我們需要限制某些日誌以避免將使用者敏感資料(例如電話號碼)傳輸到日誌檔案時,這特別有用。
5. Spring 設定檔和變數替換
如上所示,Logback支援設定檔的條件處理和EvaluatorFilter
類別來限制日誌的輸出。然而,這兩種方法都需要在 XML 語法中新增額外的行。因此,從長遠來看,過多的條件可能會很快導致設定檔變得不可讀且難以管理。
但是,還有其他方法可以本機管理日誌。對於基於 Spring 的應用程序,我們可以使用 Spring Profiles,這比使用表達式或過濾器管理日誌要簡潔得多。例如,我們可以使用logback-dev.xml
進行開發,使用logback-staging.xml
進行暫存,使用logback-prod.xml
進行生產環境。
或者,我們可以使用特定於環境的變數在運行時有條件地指向特定的記錄器。例如,我們可以透過系統屬性定義變數“ ROOT_APPENDER
”,以便在執行時間透過變數替換選擇實際的根附加器。這樣,我們可以動態地將日誌重定向到指定的輸出,這對於維護特定於環境的附加程式(例如 LogStash)特別有用。
這些方法消除了使用EvaluationFilter
方法進行條件處理所需的多個if-then-else
區塊或評估表達式所需的額外行。
6. 結論
在本文中,我們討論了 Logback 框架提供的條件日誌記錄功能。首先,我們研究了條件處理功能以及一些有用的實用方法來檢查特定屬性的可用性。接下來,我們討論了EvaluatorFilter,
它在執行時間評估 Java 表達式。最後,我們看到了這些功能的一些替代方案,我們可以嘗試 Spring 設定檔或特定於環境的變數來實現相同的結果。
與往常一樣,本文中提供的程式碼可在 GitHub 上找到。