logback 入門教程系列-04-logback Appender
Appender
Appender 是什麼
Logback將記錄事件的任務委託給稱為appender的元件。
Appenders必須實現 ch.qos.logback.core.Appender
介面。
該介面的顯著方法總結如下:
public interface Appender<E> extends LifeCycle, ContextAware, FilterAttachable {
public String getName();
public void setName(String name);
void doAppend(E event) ;
}
Appenders最終負責輸出日誌記錄事件。
但是,他們可以將事件的實際格式委派給佈局或編碼器物件。每個佈局/編碼器與一個且僅一個appender相關聯,稱為擁有的appender。
一些appender具有內建或固定的事件格式。因此,它們不需要也沒有佈局/編碼器。
例如,SocketAppender只是在通過線路傳輸之前序列化記錄事件。
AppenderBase
ch.qos.logback.core.AppenderBase
類是實現Appender介面的抽象類。
它提供了所有appender共享的基本功能,例如獲取或設定其名稱,啟用狀態,佈局和過濾器的方法。
它是帶有logback的所有appender的超類。雖然是一個抽象類,但AppenderBase實際上在Append介面中實現了doAppend()方法。
也許討論AppenderBase類的最清晰的方法是提供實際原始碼的摘錄。
public synchronized void doAppend(E eventObject) {
// prevent re-entry.
if (guard) {
return;
}
try {
guard = true;
if (!this.started) {
if (statusRepeatCount++ < ALLOWED_REPEATS) {
addStatus(new WarnStatus(
"Attempted to append to non started appender [" + name + "].",this));
}
return;
}
if (getFilterChainDecision(eventObject) == FilterReply.DENY) {
return;
}
// ok, we now invoke the derived class's implementation of append
this.append(eventObject);
} finally {
guard = false;
}
}
doAppend()方法的這種實現是同步的。因此,從不同執行緒登入到同一個appender是安全的。當一個執行緒(比如T)正在執行doAppend()方法時,其他執行緒的後續呼叫將排隊,直到T離開doAppend()方法,確保T對appender的獨佔訪問。
由於這種同步並不總是合適,因此logback附帶了ch.qos.logback.core.UnsynchronizedAppenderBase,它與AppenderBase類非常相似。為簡明起見,我們將在本文件的其餘部分討論UnsynchronizedAppenderBase。
doAppend()方法的第一件事是檢查guard是否設定為true。如果是,它會立即退出。如果未設定防護,則在下一個語句中將其設定為true。守衛確保doAppend()方法不會遞迴呼叫自身。試想一下,一個名為append()方法之外的元件想要記錄一些東西。它的呼叫可以被引導到剛剛呼叫它的同一個appender,導致無限迴圈和堆疊溢位。
在以下語句中,我們檢查started欄位是否為true。如果不是,doAppend()將傳送警告訊息並返回。換句話說,一旦appender關閉,就不可能寫入它。 Appender物件實現了LifeCycle介面,這意味著它們實現了start(),stop()和isStarted()方法。在設定了appender的所有屬性後,Joran(logback的配置框架)呼叫start()方法來通知appender啟用其屬性。根據其型別,如果缺少某些屬性或者由於各種屬性之間的干擾,則appender可能無法啟動。例如,假設檔案建立依賴於截斷模式,則FileAppender不能對其File選項的值起作用,直到Append選項的值也確定已知。顯式啟用步驟確保appender在其值已知後對其屬性進行操作。
如果appender無法啟動或已停止,將通過logback的內部狀態管理系統發出警告訊息。在多次嘗試之後,為了避免使用相同警告訊息的副本使內部狀態系統氾濫,doAppend()方法將停止發出這些警告。
下一個if語句檢查附加過濾器的結果。根據過濾器鏈產生的決定,可以拒絕或明確接受事件。如果過濾器鏈沒有做出決定,則預設接受事件。
然後,doAppend()方法呼叫派生類的append()方法實現。此方法執行將事件附加到適當裝置的實際工作。
最後,釋放保護以便允許隨後呼叫doAppend()方法。
對於本手冊的其餘部分,我們為通過setter和getter方法使用JavaBeans內省動態推斷的任何屬性保留術語“選項”或“屬性”。
logback核心
Logback-core奠定了構建其他logback模組的基礎。
通常,logback-core中的元件需要一些(儘管很少)定製。
但是,在接下來的幾節中,我們將描述幾個可以立即使用的appender。
OutputStreamAppender
OutputStreamAppender將事件附加到java.io.OutputStream。
此類提供其他appender構建的基本服務。
使用者通常不直接例項化OutputStreamAppender物件,因為通常java.io.OutputStream型別不能方便地對映到字串,因為無法在配置指令碼中指定目標OutputStream物件。
簡而言之,您無法從配置檔案配置OutputStreamAppender。
但是,這並不意味著OutputStreamAppender缺少可配置的屬性。
接下來描述這些屬性。
屬性
- encoder
確定將事件寫入底層OutputStreamAppender的方式。
- immediateFlush
immediateFlush的預設值為’true’。
立即重新整理輸出流可確保立即寫出日誌記錄事件,並且在應用程式退出而未正確關閉appender的情況下不會丟失。
另一方面,將此屬性設定為“false”可能會使記錄吞吐量翻兩番(您的里程可能會有所不同)。
同樣,如果將immediateFlush設定為“false”,並且在應用程式退出時未正確關閉appender,則可能會丟失尚未寫入磁碟的日誌記錄事件。
OutputStreamAppender是其他三個appender的超類,即ConsoleAppender,FileAppender,它又是RollingFileAppender的超類。
下圖說明了OutputStreamAppender及其子類的類圖。
ConsoleAppender
ConsoleAppender,如名稱所示,附加在控制檯上,或者更確切地說,附加在System.out或System.err上,前者是預設目標。
ConsoleAppender藉助使用者指定的編碼器格式化事件。
System.out和System.err都是java.io.PrintStream型別。
因此,它們被包裝在OutputStreamWriter中,後者緩衝I/O操作。
屬性
- encoder
Encoder請參見OutputStreamAppender屬性。
- target
String值System.out或System.err之一。 預設目標是System.out。
- withJansi
預設情況下將withJansi屬性設定為false。
將withJansi設定為true可啟用Jansi庫,該庫在Windows機器上提供對ANSI顏色程式碼的支援。
在Windows主機上,如果此屬性設定為true,則應在類路徑上放置“org.fusesource.jansi:jansi:1.9”。
請注意,預設情況下,基於Unix的作業系統(如Linux和Mac OS X)支援ANSI顏色程式碼。
案例
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
FileAppender
FileAppender是OutputStreamAppender的子類,它將日誌事件追加到檔案中。
目標檔案由“檔案”選項指定。 如果檔案已存在,則根據append屬性的值將其附加到或截斷。
屬性
- append
boolean 型別
如果為true,則在現有檔案的末尾附加事件。
否則,如果append為false,則截斷任何現有檔案。預設情況下,append選項設定為true。
- encoder
Encoder請參見OutputStreamAppender屬性。
- file
String 型別
要寫入的檔案的名稱。
如果該檔案不存在,則建立該檔案。
在MS Windows平臺上,使用者經常忘記逃避反斜槓。
例如,值 c:\temp\test.log
不太可能被正確解釋,因為’\t’是解釋為單個製表符(\u0009
)的轉義序列。
正確的值可以指定為 c/temp/test.log
,也可以指定為 c\\temp\\test.log
。
“檔案”選項沒有預設值。
如果檔案的父目錄不存在,FileAppender將自動建立它,包括任何必要但不存在的父目錄。
- prudent
boolean 型別
在謹慎模式下,FileAppender將安全地寫入指定的檔案,即使存在執行在不同JVM中的其他FileAppender例項,也可能在不同的主機上執行。
prudent模式的預設值為false。
儘管存在一些限制,但謹慎模式可與RollingFileAppender結合使用。
謹慎模式意味著append屬性自動設定為true。
謹慎更依賴於獨佔檔案鎖。實驗表明,檔案鎖定大約是編寫日誌記錄事件的三倍(x3)。在寫入位於本地硬碟上的檔案的“普通”PC上,當謹慎模式關閉時,編寫單個日誌記錄事件大約需要10微秒。當謹慎模式開啟時,輸出單個記錄事件大約需要30微秒。這意味著當謹慎模式關閉時記錄吞吐量為每秒100’000個事件,而謹慎模式下每秒記錄大約33’000個事件。
謹慎模式有效地序列化寫入同一檔案的所有JVM之間的I/O操作。因此,隨著競爭訪問檔案的JVM數量的增加,每個I/O操作引起的延遲也會增加。只要I/O操作的總數大約為每秒20個日誌請求,對效能的影響應該可以忽略不計。每秒生成100個或更多I/O操作的應用程式可以看到對效能的影響,應避免使用謹慎模式。
聯網檔案鎖當日志文件位於網路檔案系統上時,審慎模式的成本會更高。同樣重要的是,網路檔案系統上的檔案鎖定有時會受到強烈偏見,使得當前擁有鎖定的程序會在其釋放時立即重新獲得鎖定。因此,當一個程序佔用日誌檔案的鎖定時,其他程序將等待鎖定到出現死鎖的點。
謹慎模式的影響很大程度上取決於網路速度以及作業系統實現細節。
我們提供了一個名為FileLockSimulator的非常小的應用程式,它可以幫助您模擬環境中謹慎模式的行為。
預設情況下,每個日誌事件都會立即重新整理到基礎輸出流。
這種預設方法更安全,因為如果應用程式在沒有正確關閉appender的情況下退出,則日誌事件不會丟失。
但是,為了顯著增加日誌記錄吞吐量,您可能希望將immediateFlush屬性設定為false。
下面是FileAppender的配置檔案示例:
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>testFile.log</file>
<append>true</append>
<!-- set immediateFlush to false for much higher logging throughput -->
<immediateFlush>true</immediateFlush>
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
獨一無二的檔名稱
唯一命名的檔案(按時間戳)
在應用程式開發階段或在短期應用程式的情況下,例如, 批量應用程式,最好在每次新的應用程式啟動時建立一個新的日誌檔案。
藉助<timestamp>
元素,這很容易做到。 這是一個例子。
<configuration>
<!-- Insert the current time formatted as "yyyyMMdd'T'HHmmss" under
the key "bySecond" into the logger context. This value will be
available to all subsequent configuration elements. -->
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<!-- use the previously created timestamp to create a uniquely
named log file -->
<file>log-${bySecond}.txt</file>
<encoder>
<pattern>%logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
timestamp元素採用兩個必需屬性key和datePattern以及一個可選的timeReference屬性。
key屬性是鍵的名稱,在該鍵下,時間戳可作為變數用於後續配置元素。
datePattern屬性表示用於將當前時間(解析配置檔案)轉換為字串的日期模式。
日期模式應遵循SimpleDateFormat中定義的約定。
timeReference屬性表示時間戳的時間參考。
預設值是配置檔案的解釋/解析時間,即當前時間。
但是,在某些情況下,使用上下文出生時間作為時間參考可能是有用的。
這可以通過將timeReference屬性設定為“contextBirth”來完成。
<configuration>
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"
timeReference="contextBirth"/>
</configuration>
RollingFileAppender
RollingFileAppender擴充套件了FileAppender,具有翻轉日誌檔案的功能。
例如,RollingFileAppender可以記錄到名為log.txt檔案的檔案,並且一旦滿足某個條件,就將其日誌記錄目標更改為另一個檔案。
有兩個與RollingFileAppender互動的重要子元件。
第一個RollingFileAppender子元件,即RollingPolicy(見下文)負責執行翻轉所需的操作。
RollingFileAppender的第二個子元件,即TriggeringPolicy(見下文)將確定是否以及何時發生翻轉。
因此,RollingPolicy負責什麼和TriggeringPolicy負責什麼時候。
作為任何用途,RollingFileAppender必須同時設定RollingPolicy和TriggeringPolicy。
但是,如果其RollingPolicy也實現了TriggeringPolicy介面,則只需要顯式指定前者。
屬性
- file
String請參見FileAppender屬性。
- append
boolean請參閱FileAppender屬性。
- encoder
Encoder請參見OutputStreamAppender屬性。
- rollingPolicy
RollingPolicy 此選項是在發生翻轉時指示RollingFileAppender的行為的元件。請參閱以下更多資訊。
- triggeringPolicy
TriggeringPolicy此選項是告訴RollingFileAppender何時啟用翻轉過程的元件。請參閱以下更多資訊。
謹慎模式下不支援prudent boolean FixedWindowRollingPolicy。
儘管有兩個限制,RollingFileAppender與TimeBasedRollingPolicy一起支援謹慎模式。
在謹慎模式下,不支援也不允許檔案壓縮。 (當另一個JVM正在壓縮檔案時,我們不能將一個JVM寫入檔案。)
無法設定FileAppender的檔案屬性,必須將其留空。實際上,大多數作業系統不允許在另一個程序開啟檔案時重新命名檔案。
另請參見FileAppender的屬性。
案例
RollingPolicy負責涉及檔案移動和重新命名的翻轉過程。
RollingPolicy介面如下所示:
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.spi.LifeCycle;
public interface RollingPolicy extends LifeCycle {
public void rollover() throws RolloverFailure;
public String getActiveFileName();
public CompressionMode getCompressionMode();
public void setParent(FileAppender appender);
}
rollover方法完成了歸檔當前日誌檔案所涉及的工作。
呼叫getActiveFileName() 方法來計算當前日誌檔案的檔名(寫入實時日誌的位置)。
如getCompressionMode方法所示,RollingPolicy還負責確定壓縮模式。
最後,RollingPolicy通過setParent方法提供對其父級的引用。
TimeBasedRollingPolicy
TimeBasedRollingPolicy可能是最受歡迎的滾動策略。
它根據時間定義翻轉策略,例如按天或按月。 TimeBasedRollingPolicy承擔滾動和觸發所述翻轉的責任。
實際上,TimeBasedTriggeringPolicy實現了RollingPolicy和TriggeringPolicy介面。
TimeBasedRollingPolicy的配置採用一個必需的fileNamePattern屬性和幾個可選屬性。
屬性
fileNamePattern具有雙重用途。首先,通過研究模式,logback計算請求的翻轉週期。
其次,它計算每個存檔檔案的名稱。請注意,兩種不同的模式可以指定相同的週期。
模式yyyy-MM和yyyy @ MM都指定了每月翻轉,儘管生成的存檔檔案將帶有不同的名稱。
通過設定file屬性,您可以解除活動日誌檔案的位置和存檔日誌檔案的位置。日誌記錄輸出將定位到file屬性指定的檔案中。因此,活動日誌檔案的名稱不會隨時間而變化。但是,如果選擇省略file屬性,則將根據fileNamePattern的值為每個句點重新計算活動檔案。通過保留檔案選項未設定,可以避免在翻轉期間存在引用日誌檔案的外部檔案控制代碼時發生的檔案重新命名錯誤。
maxHistory屬性控制要保留的最大歸檔檔案數,刪除舊檔案。例如,如果您指定每月翻轉,並將maxHistory設定為6,則將保留6個月的歸檔檔案,其中包含超過6個月的檔案。請注意,刪除舊的歸檔日誌檔案後,將根據需要刪除為日誌檔案歸檔而建立的所有資料夾。
由於各種技術原因,翻轉不是時鐘驅動的,而是取決於記錄事件的到來。例如,在2002年3月8日,假設fileNamePattern設定為yyyy-MM-dd(每日翻轉),則午夜之後第一個事件的到達將觸發翻轉。如果在午夜之後的23分47秒沒有記錄事件,那麼翻轉實際上將發生在3月9日00:23’47 AM而不是凌晨0:00。因此,根據事件的到達率,可能會以一些延遲觸發翻轉。但是,無論延遲如何,已知翻轉演算法是正確的,因為在某個時間段內生成的所有日誌記錄事件都將在限定該時間段的正確檔案中輸出。
案例
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logFile.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- keep 30 days' worth of history capped at 3GB total size -->
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
基於規模和時間的滾動政策
有時您可能希望按日期歸檔檔案,但同時限制每個日誌檔案的大小,特別是如果後處理工具對日誌檔案施加大小限制。
為了滿足此要求,logback隨附SizeAndTimeBasedRollingPolicy。
請注意,TimeBasedRollingPolicy已允許限制歸檔日誌檔案的組合大小。
如果您只想限制日誌存檔的組合大小,那麼上面描述的TimeBasedRollingPolicy和設定totalSizeCap屬性應該足夠了。
這是一個示例配置檔案,演示了基於時間和大小的日誌檔案歸檔。
<configuration>
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>mylog.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="ROLLING" />
</root>
</configuration>
觸發策略概述
TriggeringPolicy實現負責指示RollingFileAppender何時進行翻轉。
TriggeringPolicy介面僅包含一個方法。
import java.io.File;
import ch.qos.logback.core.spi.LifeCycle;
public interface TriggeringPolicy<E> extends LifeCycle {
public boolean isTriggeringEvent(final File activeFile, final <E> event);
}
isTriggeringEvent() 方法將活動檔案和當前正在處理的日誌記錄事件作為引數。
具體實現基於這些引數確定是否應該發生翻轉。
最廣泛使用的觸發策略,即TimeBasedRollingPolicy也可以兼作滾動策略,之前已經與其他滾動策略一起討論過。
SizeBasedTriggeringPolicy
SizeBasedTriggeringPolicy檢視當前活動檔案的大小。如果它大於指定的大小,它將發出擁有的RollingFileAppender訊號,以觸發現有活動檔案的翻轉。
SizeBasedTriggeringPolicy只接受一個引數,即maxFileSize,預設值為10 MB。
maxFileSize選項可以以位元組,千位元組,兆位元組或千兆位元組為單位,字尾數值為KB,MB和GB。例如,5000000,5000KB,5MB和2GB都是有效值,前三個是等效的。
下面是一個示例配置,當日志文件大小達到5MB時,RollingFileAppender與SizeBasedTriggeringPolicy一起觸發翻轉。
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>test.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>test.%i.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
參考資料
https://logback.qos.ch/manual/appenders.html