logback官方文檔中文翻譯第四章:Appenders

第四章:Appenders

什么是 Appender

logback 將寫入日志事件的任務(wù)委托給一個(gè)名為 appender 的組件。Appender 必須實(shí)現(xiàn) ch.qos.logback.core.Appender 接口。該接口的方法如下:

package ch.qos.logback.core;
  
import ch.qos.logback.core.spi.ContextAware;
import ch.qos.logback.core.spi.FilterAttachable;
import ch.qos.logback.core.spi.LifeCycle;
  

public interface Appender<E> extends LifeCycle, ContextAware, FilterAttachable {

    public String getName();
    public void setName(String name);
    void doAppend(E event);
}

doAppender() 方法接收一個(gè)泛型參數(shù) E 作為唯一的參數(shù)掉丽。E 的實(shí)際參數(shù)類型取決于 logback 模塊屈糊。在 logback-classic 模塊里面与境,E 的類型是 ILoggingEvent 。在 logback-access 模塊里面芦圾,E 的類型是 AccessEvent骤竹。doAppend() 是 logback 框架里面最重要的模塊餐胀。它的責(zé)任是將日志事件進(jìn)行格式化,然后輸出到對(duì)應(yīng)的設(shè)備上瘤载。

Appender 都是實(shí)體類否灾,這樣可以確保它們通過名字被引用。Appender 接口繼承了 FilterAttachable 接口鸣奔。使得一個(gè)或多個(gè)過濾器可以附加到 appender 實(shí)例上墨技。

Appender 最基本的責(zé)任是將日志事件進(jìn)行輸出。然而挎狸,它們可以委托 Layout 或者 Encoder 對(duì)象來對(duì)日志事件進(jìn)行格式化扣汪。每一個(gè) layout/encoder 有且只與一個(gè) appender 相關(guān)聯(lián)。例如锨匆,SocketAppender 僅僅序列化日志事件崭别,然后再通過線路傳輸。

AppenderBase

ch.qos.logback.core.AppenderBase 是一個(gè)抽象類恐锣,實(shí)現(xiàn)了 Appender 接口茅主。它提供了基本方法供所有 appender 使用。例如:獲取或設(shè)置名稱的方法土榴、激活狀態(tài)诀姚、布局以及過濾器。它是 logback 中所有 appender 的父類玷禽。盡管是一個(gè)抽象類赫段,但是 AppenderBase 還實(shí)現(xiàn)了 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() 的實(shí)現(xiàn)是 synchronized 的。不同的線程通過同一個(gè) appender 打印日志是線程安全的撩银。當(dāng)一個(gè)線程 T 正在執(zhí)行 doAppend() 方法给涕,接下來其它的線程調(diào)用將會(huì)被阻塞直到線程 T 離開 doAppend() 方法,這樣可以確保 T 對(duì) appender 的訪問具有獨(dú)占性。

因?yàn)檫@種同步并不總是適合的稠炬,所以 logback 提供了 ch.qos.logback.core.UnsynchronizedAppenderBase 類焕阿,0跟 AppenderBase 類十分的相似咪啡。為了簡(jiǎn)單起見首启,接下來的內(nèi)容我們只討論 UnsynchronizedAppenderBase

首先撤摸,doAppend() 方法會(huì)去檢查 guard 是不是為 true毅桃。如果是,它會(huì)立即退出准夷。如果 guard 不是為 true钥飞,下一步將它設(shè)置為 true。guard 會(huì)確保 doAppend() 方法不會(huì)被自身遞歸調(diào)用衫嵌。想象一下這樣的一個(gè)組件读宙,被在 append() 方法之外的地方被調(diào)用,用于打印日志楔绞。它的調(diào)用可能被直接指向一個(gè)完全相同的剛剛調(diào)用過它的一個(gè) appender结闸,導(dǎo)致無限循環(huán)和堆棧溢出。

注:``UnsynchronizedAppenderBase類中有一個(gè)guard` 變量

private ThreadLocal<Boolean> guard = new ThreadLocal<Boolean>();

接下來酒朵,我們會(huì)檢查 started 是否為 true桦锄。如果不是,doAppend() 會(huì)發(fā)出一個(gè)警告并返回蔫耽。換句話說结耀,一旦 appender 被關(guān)閉,就不能對(duì)它進(jìn)行寫入匙铡。Appender 對(duì)象實(shí)現(xiàn)了 LifeCycle 接口图甜,也就是說它實(shí)現(xiàn)了 start()stop()鳖眼、isStarted 方法具则。在設(shè)置完一個(gè) appender 的所有屬性之后,logback 的配置框架 - Joran具帮,將會(huì)給 appender 發(fā)一個(gè)信號(hào)去激活它的屬性博肋。根據(jù)它的類型,一個(gè) appender 可能會(huì)啟動(dòng)失敗蜂厅,如果某些特定的屬性丟失或者由于各種屬性之間的沖突匪凡。例如,創(chuàng)建文件依賴截?cái)嗄J剑?code>FileAppender 不能對(duì) File 選項(xiàng)的值起作用掘猿,直到這個(gè)值確定下來病游。明確的激活步驟可以確保一個(gè) appender 在知道它們的值之后再作用于其屬性。

如果 appender 不能被被啟動(dòng)或者被停止,logback 內(nèi)部狀態(tài)管理系統(tǒng)將會(huì)發(fā)出一條警告信息衬衬。在幾次嘗試之后买猖,為了避免被同一條警告信息淹沒內(nèi)部狀態(tài)系統(tǒng),doAppend() 會(huì)停止發(fā)出這些警告滋尉。

接下來 if 語句檢查過濾器的結(jié)果玉控,根據(jù)過濾器鏈的結(jié)果,事件可以被拒絕或者被接受狮惜。如果缺少過濾器鏈高诺,事件默認(rèn)會(huì)被接受。

接下來 doAppend() 調(diào)用 append() 的實(shí)現(xiàn)方法碾篡。這個(gè)方法是實(shí)際的執(zhí)行者虱而,用來將事件附加到合適的設(shè)備上。

最后开泽,guard 被釋放牡拇,后續(xù)對(duì) append() 的調(diào)用得以執(zhí)行。

在手冊(cè)的其余部分穆律,我們保留 optionproperty巷屿,用于 JavaBean 通過 get 和 set 方法動(dòng)態(tài)推斷出來的任何屬性球化。

Logback-core

Logback-core 為 logback 其他模塊的構(gòu)建奠定了基礎(chǔ)附帽。一般來說粪小,logback-core 的組件需要一些定制,盡管很少贡歧。但是滩租,在接下來的幾個(gè)部分,我們描述了一種可以開箱即用的 appender利朵。

OutputStreamAppender

OutputStreamAppender 將事件附加到 java.io.OutputStream 上律想。這個(gè)類提供了其它 appender 構(gòu)建的基礎(chǔ)服務(wù)。用戶通常不會(huì)直接實(shí)例一個(gè) OutputStreamAppender 實(shí)例绍弟。因?yàn)橐话銇碚f java.io.OutputStream 類型不能方便的轉(zhuǎn)為 String技即。因?yàn)樵谂渲梦募袥]有方法去直接指定一個(gè) OutputStream 目標(biāo)對(duì)象。簡(jiǎn)單來說樟遣,你不能通過配置文件配置一個(gè) OutputStreamAppender而叼。但是這并不意味著 OutputStreamAppender 缺少配置屬性。這些屬性描述如下:

屬性名 屬性值 描述
ecoder Encoder 決定通過哪種方式將事件寫入 OutputStreamAppender豹悬,Encoder 將會(huì)在單獨(dú)的章節(jié)介紹
immediateFlush boolean immediateFlush 的默認(rèn)值為 true葵陵。立即刷新輸出流可以確保日志事件被立即寫入,并且可以保證一旦你的應(yīng)用沒有正確關(guān)閉 appender瞻佛,日志事件也不會(huì)丟失脱篙。從另一方面來說,設(shè)置這個(gè)屬性為 false,有可能會(huì)使日志的吞吐量翻兩番(視情況而定)绊困。但是文搂,設(shè)置為 false,當(dāng)應(yīng)用退出的時(shí)候沒有正確關(guān)閉 appender秤朗,會(huì)導(dǎo)致日志事件沒有被寫入磁盤煤蹭,可能會(huì)丟失。

OutputStreamAppender 是其他三個(gè) appender 的父類川梅,分別是 ConsoleAppender疯兼、FileAppender 以及 RollingFileAppender然遏。FileAppender 又是 RollingFileAppender 的父類贫途。下面的類圖展示 OutputStreamAppender 與子類之間的關(guān)系:

點(diǎn)擊查看大圖

ConsoleAppender

ConsoleAppender 就跟名字顯示的一樣,是將日志事件附加到控制臺(tái)待侵,跟進(jìn)一步說就是通過 System.out 或者 System.err 來進(jìn)行輸出丢早。默認(rèn)通過前者。ConsoleAppender 通過用戶指定的 encoder秧倾,格式化日志事件怨酝。Encoder 會(huì)在接下來的章節(jié)討論。System.outSystem.err 兩者都是 java.io.PrintStream 類型那先。因此农猬,它們被包裝在可以進(jìn)行 I/O 緩存操作的 OutputStreamWriter 中。

屬性名 類型 描述
encoder Encoder OutputStreamAppender 屬性
target String System.outSystem.err售淡。默認(rèn)為 System.out
withJansi boolean withJansi 的默認(rèn)值為 false斤葱。設(shè)置 withJansitrue 可以激活 Jansi 在 windows 使用 ANSI 彩色代碼。在 windows 上如果設(shè)置為 true揖闸,你應(yīng)該將 org.fusesource.jansi:jansi:1.9 這個(gè) jar 包放到 classpath 下揍堕。基于 Unix 實(shí)現(xiàn)的操作系統(tǒng)汤纸,像 Linux衩茸、Max OS X 都默認(rèn)支持 ANSI 才彩色代碼。

Example: ConsoleAppender configuraion (logback-Console.xml)

<configuration>
    
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender" >
        <!-- encoder 默認(rèn)使用 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>

通過以下命令執(zhí)行上面的配置文件:

java chapters.appenders.ConfigurationTester logback-Console.xml

FileAppender

FileAppenderOutputStreamAppender 的子類贮泞,將日志事件輸出到文件中楞慈。通過 file 來指定目標(biāo)文件。如果該文件存在啃擦,根據(jù) append 的值囊蓝,要么將日志追加到文件中,要么該文件被截?cái)唷?/p>

屬性名 類型 描述
append boolean 如果為 true议惰,日志事件會(huì)被追加到文件中慎颗,否則的話,文件會(huì)被截?cái)唷DJ(rèn)為 true
encoder Encoder 參見 OutputStreamAppender 的屬性
file String 要寫入文件的名稱俯萎。如果文件不存在傲宜,則新建。在 windows 平臺(tái)上夫啊,用戶經(jīng)常忘記對(duì)反斜杠進(jìn)行轉(zhuǎn)義函卒。例如,c:\temp\test.log 不會(huì)被正確解析撇眯,因?yàn)?'\t' 是一個(gè)轉(zhuǎn)義字符报嵌,會(huì)被解析為一個(gè) tab 字符 (\u0009)。正確的值應(yīng)該像:c:/temp/test.log 或者 c:\\temp\\test.log熊榛。沒有默認(rèn)值锚国。
prudent boolean 在嚴(yán)格模式下,FileAppender 會(huì)將日志安全的寫入指定文件玄坦。即使在不同的 JVM 或者不同的主機(jī)上運(yùn)行 FileAppender 實(shí)例血筑。默認(rèn)的值為 false。<br />嚴(yán)格模式可以與 RollingFileAppender 結(jié)合使用煎楣。<br />嚴(yán)格模式也意味著 append 屬性被自動(dòng)設(shè)置為 true豺总。<br />嚴(yán)格模式依賴排他文件鎖。實(shí)驗(yàn)證明择懂,文件鎖大概是寫入日志事件成本的 3 倍喻喳。在嚴(yán)格模式關(guān)閉的情況下,往一臺(tái)"普通"電腦的硬盤上將一個(gè)日志事件寫入文件困曙,大概需要耗費(fèi) 10 微秒表伦。但是在開啟的情況下,大概需要 30 微秒赂弓。也就是說在關(guān)閉的情況下可以一秒鐘寫入 100'000 個(gè)日志事件绑榴,但是在開啟的情況下,一秒鐘只能寫入33'000 個(gè)日志事件盈魁。<br />嚴(yán)格模式可以在所有 JVM 寫入同一個(gè)文件時(shí)翔怎,有效的序列化 I/O 操作。因此杨耙,隨著競(jìng)相訪問同一個(gè)文件的 JVM 數(shù)量上升赤套,將會(huì)延遲每一個(gè) I/O 操作。只要總共的 I/O 操作大約為每秒 20 個(gè)日志請(qǐng)求珊膜,對(duì)性能的影響可以被忽略容握。但是,如果應(yīng)用每秒產(chǎn)生了 100 個(gè)以上的 I/O 操作车柠,性能會(huì)受到明顯的影響剔氏,應(yīng)該避免使用嚴(yán)格模式塑猖。<br />網(wǎng)絡(luò)文件鎖 當(dāng)日志文件位于網(wǎng)絡(luò)文件系統(tǒng)上時(shí),嚴(yán)謹(jǐn)模式的成本會(huì)更高谈跛。同樣重要的是羊苟,網(wǎng)絡(luò)文件系統(tǒng)的文件鎖帶有很強(qiáng)的偏向性,當(dāng)前獲得鎖的進(jìn)程在釋放鎖之后會(huì)立馬又重新獲得感憾。因此蜡励,當(dāng)一個(gè)進(jìn)程獨(dú)占日志文件,將會(huì)導(dǎo)致其它進(jìn)程饑餓死鎖阻桅。<br />嚴(yán)格模式的影響嚴(yán)重依賴網(wǎng)速以及操作系統(tǒng)實(shí)現(xiàn)的細(xì)節(jié)凉倚。我們提供了一個(gè)小型應(yīng)用 FileLockSimulator 用于在你的環(huán)境中模擬嚴(yán)格模式。

立即刷新 默認(rèn)情況下嫂沉,每一個(gè)日志事件都會(huì)被立即刷新到底層的輸出流稽寒。默認(rèn)方法更加的安全,因?yàn)槿罩臼录谀愕膽?yīng)用沒有正確關(guān)閉 appender 的情況下不會(huì)丟失输瓜。但是瓦胎,要想顯著的增加日志的吞吐率芬萍,你可以將 immediateFlush 設(shè)置為 false尤揣。

下面是 FileAppender 的配置示例:

Example: logback-fileAppender.xml

<configuration>
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>testFile.log</file>
<!--        將 immediateFlush 設(shè)置為 false 可以獲得更高的日志吞吐量 -->
        <immediateFlush>true</immediateFlush>
<!--        默認(rèn)為 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>

logback-examples 的文件夾下,運(yùn)行以下命令:

java chapters.appenders.ConfigurationTester logback-fileAppender.xml

要指定配置文件的具體路徑

也可以直接在 eclipse 里面 Run Application 時(shí)設(shè)置 Arguments

文件唯一命名 (使用時(shí)間戳)

在應(yīng)用的開發(fā)階段或者短期應(yīng)用中柬祠,例如:批處理程序北戏,在每個(gè)應(yīng)用啟動(dòng)的時(shí)候創(chuàng)建一個(gè)新的日志文件。通過 <timestamp> 元素可以輕易做到這點(diǎn)漫蛔。

Example: logback-timestamp.xml

<configuration>
<!--    通過 "bySecond" 將時(shí)間格式化成 "yyyyMMdd'T'HHmmss" 的形式插入到 logger 的上下文中 
        這個(gè)值對(duì)后續(xù)的配置也適用
-->
    <timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss" />
    
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
<!--        利用之前創(chuàng)建的 timestamp 來創(chuàng)建唯一的文件 -->
        <file>log-${bySecond}.txt</file>
        <encoder>
            <pattern>%logger{35} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <root level="DEBUG">
        <appender-ref ref="FILE" />
    </root>
</configuration>

timestamp 元素需要兩個(gè)強(qiáng)制的屬性 keydatePattern 以及可選的屬性 timeReference嗜愈。key 屬性的值是來區(qū)分哪個(gè) timestamp 元素,并且在后續(xù)的配置中可以通過TODO 變量替換來使用莽龟。datePattern 屬性用于將當(dāng)前時(shí)間格式化成字符串蠕嫁。日期格式必須遵循 SimpleDateFormat 中的規(guī)范。timeReference 表示時(shí)間戳引用哪個(gè)時(shí)間毯盈。默認(rèn)為解析配置文件的時(shí)間剃毒,也就是當(dāng)前時(shí)間。但是搂赋,在一些特定的情況下赘阀,可以設(shè)置為上下文初始化的時(shí)間。通過 設(shè)置 timeReference 的值為 contextBirth脑奠。

通過一下命令來測(cè)試 <timestamp> 元素:

java chapters.appenders.ConfigurationTester logback-timestamp.xml

譯者注:需要指定具體配置文件的具體路徑基公,也可以通過 eclipse 來運(yùn)行。后續(xù)不再重復(fù)此注意事項(xiàng)宋欺。

設(shè)置 timeReference 的值為 "contextBirth" 的例子如下:

Example: logback-timestamp-contextBirth.xml

<configuration>
    <timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss" timeReference="contextBirth"/>
    ...
</configuration>

RollingFileAppender

RollingFileAppender 繼承自FileAppender轰豆,具有輪轉(zhuǎn)日志文件的功能胰伍。例如,RollingFileAppender 將日志輸出到 log.txt 文件酸休,在滿足了特定的條件之后喇辽,將日志輸出到另外一個(gè)文件。

RollingFileAppender 進(jìn)行交互的有兩個(gè)重要的子組件雨席。第一個(gè)是 RollingPolicy菩咨,它負(fù)責(zé)日志輪轉(zhuǎn)的功能。另一個(gè)是 TriggeringPolicy陡厘,它負(fù)責(zé)日志輪轉(zhuǎn)的時(shí)機(jī)抽米。所以 RollingPolicy 負(fù)責(zé)發(fā)生什么,TriggeringPolicy 負(fù)責(zé)什么時(shí)候發(fā)生糙置。

為了讓 RollingFileAppender 生效云茸,必須同時(shí)設(shè)置 RollingPolicyTriggeringPolicy。但是谤饭,如果 RollingPolicy 也實(shí)現(xiàn)了 TriggeringPolicy 接口标捺,那么只需要設(shè)置前一個(gè)就好了。

RollingFileAppender 的屬性如下所示:

屬性名 類型 描述
file String 參見 FileAppender
append boolean 參見 FileAppender
encoder Encoder 參見 OutputStreamAppender
rollingPolicy RollingPolicy 當(dāng)輪轉(zhuǎn)發(fā)生時(shí)揉抵,指定 RollingFileAppender 的行為亡容。下面將會(huì)詳細(xì)說明
triggeringPolicy TriggeringPolicy 告訴 RollingFileAppender 什么時(shí)候發(fā)生輪轉(zhuǎn)行為。下面將會(huì)詳細(xì)說明
prudent boolean FixedWindowRollingPolicy 不支持該屬性冤今。<br />RollingFileAppender 在使用嚴(yán)格模式時(shí)要與 [TimeBasedRollingPolicy](#TimeBasedRollingPolicy) 結(jié)合使用闺兢,但是有兩個(gè)限制:<br />1. 在嚴(yán)格模式下,也不支持也不允許文件壓縮(我們不能讓一個(gè) JVM 在寫入文件時(shí)戏罢,另一個(gè) JVM 在壓縮該文件)<br />2. 不能對(duì)FileAppenderfile屬性進(jìn)行設(shè)置屋谭。實(shí)際上,大多數(shù)的操作系統(tǒng)不允許在有進(jìn)程操作文件的情況下對(duì)文件改名龟糕。<br />其它的參考FileAppender`

Rolling policy 簡(jiǎn)介

RollingPolicy 負(fù)責(zé)輪轉(zhuǎn)的方式為:移動(dòng)文件以及對(duì)文件改名桐磁。

RollingPolicy 接口如下:

package ch.qos.logback.core.rolling;  

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 方法負(fù)責(zé)對(duì)日志文件進(jìn)行歸檔。getActiveFileName() 方法負(fù)責(zé)獲取當(dāng)前日志文件的名字讲岁。getCompressionMode 方法決定采取哪種壓縮模式我擂。通過 setParent 方法引用父類。

TimeBasedRollingPolicy

TimeBasedRollingPolicy 是最常用的輪轉(zhuǎn)策略催首。它是基于時(shí)間來定義輪轉(zhuǎn)策略扶踊。例如按天或者按月。TimeBasedRollingPolicy 既負(fù)責(zé)輪轉(zhuǎn)的行為郎任,也負(fù)責(zé)觸發(fā)輪轉(zhuǎn)秧耗。實(shí)際上,TimeBasedRollingPolicy 同時(shí)實(shí)現(xiàn)了 RollingPolicyTriggeringPolicy 接口舶治。

TimeBasedRollingPolicy 的配置需要一個(gè)強(qiáng)制的屬性 fileNamePattern 以及其它的可選屬性分井。

屬性名 類型 描述
fileNamePattern String 該屬性定義了輪轉(zhuǎn)時(shí)的屬性名车猬。它的值應(yīng)該由文件名加上一個(gè) %d 的占位符。%d 應(yīng)該包含 java.text.SimpleDateFormat 中規(guī)定的日期格式尺锚。如果省略掉這個(gè)日期格式珠闰,那么就默認(rèn)為 yyyy-MM-dd。輪轉(zhuǎn)周期是通過 fileNamePattern 推斷出來的瘫辩。<br /> <br />注意:可以選擇對(duì) RollingFileAppenderTimeBasedRollingPolicy 的父類)中的 file 屬性進(jìn)行設(shè)置伏嗜,也可以忽略。通過設(shè)置 FileAppenderfile 屬性伐厌,你可以將當(dāng)前活動(dòng)日志的路徑與歸檔日志的路徑分隔開來承绸。當(dāng)前日志永遠(yuǎn)會(huì)是通過 file 指定的文件。它的名字不會(huì)隨著時(shí)間的推移而發(fā)生變化挣轨。但是军熏,如果你選擇忽略 file 屬性,當(dāng)前活動(dòng)日志在每個(gè)周期內(nèi)將會(huì)根據(jù) fileNamePattern 的值變化卷扮。稍后的例子將會(huì)說明這一點(diǎn)荡澎。<br />%d{} 中的日期格式將會(huì)遵循 java.text.SimpleDateFormat 中的約定。斜桿 '/' 或者反斜杠 '\' 都會(huì)被解析成目錄分隔符晤锹。<br /><br />指定多個(gè) %d<br /><br />可以指定多個(gè) %d摩幔,但是只能有一個(gè)是主要的,用于推斷輪轉(zhuǎn)周期抖甘。其它的 %d 占位符必須通過 'aux' 標(biāo)記為輔助的热鞍。見下面的示例:<br />多個(gè) %d 占位符允許你在文件夾中去管理歸檔文件,這個(gè)跟輪轉(zhuǎn)周期不同衔彻。如下所示:通過年月來管理日志文件夾,但是輪轉(zhuǎn)周期是在每天晚上零點(diǎn)偷办。<br />/var/log/%d{yyyy/MM, aux}/myapplication.%d{yyyy-MM-dd}.log<br /><br />TimeZone<br /><br />在某些情況下艰额,你可能想要根據(jù)時(shí)區(qū)而不是主機(jī)的時(shí)鐘來輪轉(zhuǎn)日志枉侧。你可以通過如下方式來指定一個(gè)時(shí)區(qū)波闹,例如:<br />aFloder/test.%d{yyyy-MM-dd-HH, UTC}.log<br />如果指定的 timezone 不能被識(shí)別或者拼寫錯(cuò)誤刺彩,將會(huì)根據(jù) TimeZone.getTimeZone(String) 方法指定為 GMT油坝。
maxHistory int 這個(gè)可選的屬性用來控制最多保留多少數(shù)量的歸檔文件丁溅,將會(huì)異步刪除舊的文件贴见。比如佑力,你指定按月輪轉(zhuǎn)筋量,指定 maxHistory = 6湖苞,那么 6 個(gè)月內(nèi)的歸檔文件將會(huì)保留在文件夾內(nèi)拯欧,大于 6 個(gè)月的將會(huì)被刪除。注意:當(dāng)舊的歸檔文件被移除時(shí)财骨,當(dāng)初用來保存這些日志歸檔文件的文件夾也會(huì)在適當(dāng)?shù)臅r(shí)候被移除镐作。
totalSizeCap int 這個(gè)可選屬性用來控制所有歸檔文件總的大小藏姐。當(dāng)達(dá)到這個(gè)大小后,舊的歸檔文件將會(huì)被異步的刪除该贾。使用這個(gè)屬性時(shí)還需要設(shè)置 maxHistory 屬性羔杨。而且,maxHistory 將會(huì)被作為第一條件杨蛋,該屬性作為第二條件兜材。
cleanHistoryOnStart boolean 如果設(shè)置為 true,那么在 appender 啟動(dòng)的時(shí)候逞力,歸檔文件將會(huì)被刪除护姆。默認(rèn)的值為 false。<br />歸檔文件的刪除通常在輪轉(zhuǎn)期間執(zhí)行掏击。但是卵皂,有些應(yīng)用的存活時(shí)間可能等不到輪轉(zhuǎn)觸發(fā)。對(duì)于這種短期應(yīng)用砚亭,可以通過設(shè)置該屬性為 true灯变,在 appender 啟動(dòng)的時(shí)候執(zhí)行刪除操作。

下面是關(guān)于 fileNamePattern 的介紹捅膘。

fileNamePattern 輪轉(zhuǎn)周期 示例
/wombat/foo.%d 每天輪轉(zhuǎn)(晚上零點(diǎn))添祸。由于省略了指定 %d 的日期格式,所以默認(rèn)為 yyyy-MM-dd 沒有設(shè)置 file 屬性:在 2006.11.23 這一天的日志都會(huì)輸出到 /wombat/foo.2006-11-23 這個(gè)文件寻仗。晚上零點(diǎn)以后刃泌,日志將會(huì)輸出到 wombat/foo.2016-11-24 這個(gè)文件。<br />設(shè)置 file 的值為 /wombat/foo.txt:在 2016.11.23 這一天的日志將會(huì)輸出到 /wombat/foo.txt 這個(gè)文件署尤。在晚上零點(diǎn)的時(shí)候耙替,foo.txt 將會(huì)被改名為 /wombat/foo.2016-11-23。然后將創(chuàng)建一個(gè)新的 foo.txt曹体,11.24 號(hào)這一天的日志將會(huì)輸出到這個(gè)新的文件中俗扇。
/wombat/%d{yyyy/MM}/foo.txt 每個(gè)月開始的時(shí)候輪轉(zhuǎn) 沒有設(shè)置 file 屬性:在 2016.10 這一個(gè)月中的日志將會(huì)輸出到 /wombat/2006/10/foo.txt。在 10.31 晚上凌晨以后箕别,11 月份的日志將會(huì)被輸出到 /wombat/2006/11/foo.txt铜幽。<br />設(shè)置 file 的值為 /wombat/foo.txt:在 2016.10,這個(gè)月份的日志都會(huì)輸出到 /wombat/foo.txt串稀。在 10.31 晚上零點(diǎn)的時(shí)候除抛,/wombat/foo.txt 將會(huì)被重命名為 /wombat/2006/10/foo.txt,并會(huì)創(chuàng)建一個(gè)新的文件 /wombat/foo.txt 母截,11 月份的日志將會(huì)輸出到這個(gè)文件到忽。依此類推。
/wombat/foo.%d{yyyy-ww}.log 每周的第一天(取決于時(shí)區(qū)) 每次輪轉(zhuǎn)發(fā)生在每周的第一天微酬,其它的跟上一個(gè)例子類似
/wombat/foo%d{yyyy-MM-dd_HH}.log 每小時(shí)輪轉(zhuǎn) 跟之前的例子類似
/wombat/foo%d{yyyy-MM-dd_HH-mm}.log 每分鐘輪轉(zhuǎn) 跟之前的例子類似
/wombat/foo%d{yyyy-MM-dd_HH-mm, UTC}.log 每分鐘輪轉(zhuǎn) 跟之前的例子類似绘趋,不過時(shí)間格式是 UTC
/foo/%d{yyyy-MM, aux}/%d.log 每天輪轉(zhuǎn)颤陶。歸檔文件在包含年月的文件夾下 第一個(gè) %d 被輔助標(biāo)記。第二個(gè) %d 為主要標(biāo)記陷遮,但是日期格式省略了滓走。因此,輪轉(zhuǎn)周期為每天(由第二個(gè) %d 控制)帽馋,文件夾的名字依賴年與月搅方。例如,在 2016.11 的時(shí)候绽族,所有的歸檔文件都會(huì)在 /foo/2006-11/ 文件夾下姨涡,如:/foo/2006-11/2006-11-14.log

任何斜桿或者反斜杠夠會(huì)被當(dāng)作文件夾分隔符。任何必要的文件夾都會(huì)在有需要的時(shí)候創(chuàng)建吧慢。你可以輕松的將日志文件放在單獨(dú)的文件夾中涛漂。

TimeBasedRollingPolicy 支持文件自動(dòng)壓縮。如果 fileNamePattern.gz 或者 .zip 結(jié)尾检诗,將會(huì)啟動(dòng)這個(gè)特性匈仗。

fileNamePattern 輪轉(zhuǎn)周期 示例
/wombat/foo.%d.gz 每天輪轉(zhuǎn)(晚上零點(diǎn)),自動(dòng)將歸檔文件壓縮成 GZIP 格式 file 屬性沒有設(shè)置:在 2009.11.23逢慌,日志將會(huì)被輸出到 /wombat/foo.2009-11-23 這個(gè)文件悠轩。但是,在晚上零點(diǎn)的時(shí)候攻泼,文件將會(huì)被壓縮成 /wombat/foo.2009-11-23.gz火架。在 11.24,這一天的日志將會(huì)被直接輸出到 /wombat/folder/foo.2009-11-24 這個(gè)文件忙菠。<br />file 屬性的值設(shè)置為 /wombat/foo.txt:在 2009.11.23何鸡,日志將會(huì)被輸出到 /wombat/foo.txt 這個(gè)文件。在晚上零點(diǎn)的時(shí)候只搁,該文件會(huì)被壓縮成 /wombat/foo.2009-11-23.gz音比。并會(huì)創(chuàng)建一個(gè)新的 /wombat/foo.txt 文件,11.24 這一天的日志將會(huì)被輸出到該文件氢惋。依此類推。

fileNamePattern 有兩個(gè)目的稽犁。logback 通過該屬性可以進(jìn)行周期性的輪轉(zhuǎn)并且得到每個(gè)歸檔文件的名字焰望。注意,兩種跟不同的 pattern 可能會(huì)有相同的輪轉(zhuǎn)周期已亥。yyyy-MMyyyy@MM 同樣都是按月輪轉(zhuǎn)熊赖,但是歸檔文件最終的名字不一樣。

通過設(shè)置 file 屬性虑椎,你可以將活動(dòng)日志文件的路徑與歸檔文件的路徑分隔開來震鹉。日志將會(huì)一直輸出到通過 file 屬性指定的文件中俱笛,并且不會(huì)隨著時(shí)間而改變。但是传趾,如果你選擇忽略 file 屬性迎膜,活動(dòng)日志的名字將會(huì)根據(jù) fileNamePattern 的值在每個(gè)周期內(nèi)變化。不設(shè)置 file 屬性的時(shí)候浆兰,如果在輪轉(zhuǎn)期間存在外部文件句柄引用日志文件磕仅,將會(huì)避免命名錯(cuò)誤

maxHistory 控制歸檔文件保留的最大數(shù)目簸呈,并刪除舊的文件榕订。例如,如果你指定按月輪轉(zhuǎn)蜕便,并設(shè)定 maxHistory 的值為 6劫恒,那么 6 個(gè)月之內(nèi)的歸檔文件都會(huì)被保留,大于 6 個(gè)月的文件將會(huì)被刪除轿腺。注意两嘴,當(dāng)舊的文件被移除時(shí),為文件歸檔而創(chuàng)建的文件夾在適當(dāng)?shù)臅r(shí)候也會(huì)被移除吃溅。

由于各種技術(shù)原因溶诞,輪轉(zhuǎn)并不是時(shí)間驅(qū)動(dòng)的,而是依賴日志事件决侈。例如螺垢,在 2002.03.08,假設(shè) fileNamePattern 的值為 yyyy-MM-dd(按天輪轉(zhuǎn))赖歌,在晚上零點(diǎn)之后枉圃,沒有日志事件到來,假設(shè)在 23 分 47 秒之后庐冯,第一個(gè)到達(dá)的日志事件將會(huì)觸發(fā)輪轉(zhuǎn)孽亲。也就是說輪轉(zhuǎn)實(shí)際發(fā)生在 03.09 00:23'47 AM 而不是 0:00 AM。因此展父,依賴日志事件的到達(dá)速度返劲,所以輪轉(zhuǎn)可能會(huì)有延遲。但是栖茉,不管延遲的情況是什么樣篮绿,一定周期內(nèi)生成的日志事件將會(huì)被輸出到指定的文件中,從這個(gè)角度來看吕漂,輪轉(zhuǎn)算法始終都會(huì)是正確的亲配。

下面是 RollingFileAppenderTimeBaseRollingPolicy 結(jié)合使用的例子:

Example: logback-RollingTimeBased.xml

<configuration>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logFile.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--            按天輪轉(zhuǎn) -->
            <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>
    
    <root level="DEBUG">
        <appender-ref ref="FILE" />
    </root>
</configuration>

下面是在 prudent 模式下(嚴(yán)格模式)RollingFileAppenderTimeBasedRollingPolicy 的結(jié)合使用的例子:

Example: logback-PrudentTimeBasedRolling.xml

<configuration>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 支持多個(gè) JVM 同時(shí)寫一個(gè)文件 -->
        <prudent>true</prudent>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
            <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>

基于大小以及時(shí)間的輪轉(zhuǎn)策略

有時(shí)你希望按時(shí)輪轉(zhuǎn),但同時(shí)又想限制每個(gè)日志文件的大小吼虎。特別是如果后期處理工具需要對(duì)日志進(jìn)行大小限制犬钢。為了滿足這個(gè)需求,logback 配備了 SizeAndTimeBasedRollingPolicy思灰。

注意玷犹,TimeBasedRollingPolicy 可以限制歸檔文件總的大小。所以如果你想要這個(gè)限制官辈,你可以通過設(shè)置 totalSizeCap 來達(dá)到這個(gè)目的箱舞。

下面的示例展示了基于時(shí)間及大小的配置:

Example: logback-sizeAndTime.xml

<configuration debug="true">
    <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>mylog.txt</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--            按天輪轉(zhuǎn) -->
            <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="ROLLING" />
    </root>
</configuration>

注意,除了 %d 之外還有 %i拳亿。這兩個(gè)占位符都是強(qiáng)制要求的晴股。在當(dāng)前時(shí)間還沒有到達(dá)周期輪轉(zhuǎn)之前,日志文件達(dá)到了 maxFileSize 指定的大小肺魁,會(huì)進(jìn)行歸檔电湘,遞增索引從 0 開始。

基于大小與時(shí)間的文件歸檔支持刪除舊的歸檔文件鹅经。你需要指定 maxHistory 屬性的值來保存幾個(gè)周期的日志寂呛。當(dāng)你的應(yīng)用停止或者啟動(dòng)的時(shí)候,日志將會(huì)繼續(xù)向正確的位置輸出瘾晃。即當(dāng)前周期內(nèi)索引最大的贷痪。

在 1.17 版本前,這個(gè)文檔會(huì)提及一個(gè)叫 SizeAndTimeBasedFNATP 的組件蹦误。但是 SizeAndTimeBasedFNATP 組件只提供一個(gè)最簡(jiǎn)單的配置劫拢。我們不再提供關(guān)于 SizeAndTimeBasedFNATP 的文檔。盡管這樣强胰,早期的配置文件使用 SizeAndTimeBasedFNATP 依然會(huì)運(yùn)行的很好舱沧。 實(shí)際上,SizeAndTimeBasedRollingPolicy 是使用 SizeAndTimeBasedFNATP 實(shí)現(xiàn)的偶洋。

FixedWindowRollingPolicy

在輪轉(zhuǎn)時(shí)熟吏,FixedWindowRollingPolicy 根據(jù)固定窗口算法重命名文件,具體描述如下:

filaNamePattern 表示歸檔文件的名字玄窝。這個(gè)屬性是必須的牵寺,而且必須包含一個(gè)表示整形的占位符 i%

FixedWindowRollingPolicy 的可用屬性如下:

屬性名 類型 描述
minIndex int 表示窗口索引的下界
maxIndex int 表示窗口索引的上界
fileNamePattern String FixedWindowRollingPolicy 在重命名日志文件時(shí)將會(huì)根據(jù)這個(gè)屬性來命名恩脂。它必須包含一個(gè) i% 的占位符缸剪,該占位符指明了窗口索引的值應(yīng)該插入的位置。<br /><br />例如东亦,當(dāng)該屬性的值為 MyLogFile%i.log,最小與最大的值分別為 13。將會(huì)產(chǎn)生的歸檔文件為 MyLogFile1.log典阵,MyLogFile2.log奋渔,MyLogFile3.log。<br /><br />文件壓縮的方式也是通過該屬性來指定壮啊。例如嫉鲸,設(shè)置該屬性的值為 MyLogFile%i.log.zip,那么歸檔文件將會(huì)被壓縮成 zip 格式歹啼。也可以選擇壓縮成 gz 格式玄渗。

由于窗口固定算法需要跟窗口大小一樣的的重命名次數(shù),因此強(qiáng)烈不推薦太大的窗口大小狸眼。當(dāng)用戶指定一個(gè)較大值時(shí)藤树,當(dāng)前的實(shí)現(xiàn)會(huì)將窗口大小自動(dòng)減少為 20。

讓我們通過一個(gè)例子來了解下固定窗口算法拓萌。假設(shè) minIndex 的值為 1岁钓,maxIndex 的值為 3fileNamePattern 的值為 foo%i.log微王,file 屬性的值為 foo.log屡限。

輪轉(zhuǎn)數(shù)目 當(dāng)前輸出文件 歸檔日志文件 描述
0 foo.log - 還沒有到輪轉(zhuǎn)周期,logbak 將日志輸出初始文件
1 foo.log foo1.log 第一次輪轉(zhuǎn)炕倘,foo.log 被重命名為 foo1.log钧大。一個(gè)新的 foo.log 文件將會(huì)被創(chuàng)建并成為當(dāng)前輸出文件
2 foo.log foo1.log,foo2.log 第二次輪轉(zhuǎn)罩旋,foo1.log 被重命名為 foo2.log啊央。foo.log 被重命名為 foo1.log。一個(gè)新的 foo.log 被創(chuàng)建并成為當(dāng)前輸出文件
3 foo.log foo1.log瘸恼,foo2.log劣挫,foo3.log 第三次輪轉(zhuǎn),foo2.log 被重命名為 foo3.log东帅。foo1.log 被命名為 foo2.log压固。foo.log 被重命名為 foo1.log。一個(gè)新的 foo.log 被創(chuàng)建并成為當(dāng)前輸出文件
4 foo.log foo1.log靠闭,foo2.log帐我,foo3.log 在這次以及后續(xù)的輪轉(zhuǎn)中,將會(huì)刪除 foo3.log 文件愧膀,其它文件的重命名操作跟之前的步驟一樣拦键。在本次以及以后的輪轉(zhuǎn)中,將會(huì)一直只有三個(gè)歸檔文件以及一個(gè)活躍的日志文件

下面的給出了 RollingFileAppender 配合 FixedWindowRollingPolicy 使用的例子檩淋。注意芬为,file 屬性是強(qiáng)制的萄金,即使它包含了一些跟 fileNamePattern 屬性相同的信息。

Example:logback-RollingFixedWindow.xml

<configuration>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>test.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
            <fileNamePattern>tests.%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>

觸發(fā)策略簡(jiǎn)介

TriggeringPolicy 的實(shí)現(xiàn)用于通知 RollingFileAppender 何時(shí)輪轉(zhuǎn)媚朦。

TriggeringPolicy 接口僅僅只包含了一個(gè)方法氧敢。

package ch.qos.logback.core.rolling;

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() 方法接收當(dāng)前活動(dòng)的文件以及當(dāng)前的日志事件作為參數(shù)⊙牛基于這些參數(shù)孙乖,通過具體的實(shí)現(xiàn)來決定輪轉(zhuǎn)是不是應(yīng)該發(fā)生。

TimeBasedRollingPolicy 是使用最廣泛的觸發(fā)策略份氧。也可以用作輪轉(zhuǎn)策略來使用唯袄。

SizeBasedTriggeringPolicy

SizeBasedTriggeringPolicy 觀察當(dāng)前活動(dòng)文件的大小,如果已經(jīng)大于了指定的值蜗帜,它會(huì)給 RollingFileAppender 發(fā)一個(gè)信號(hào)觸發(fā)對(duì)當(dāng)前活動(dòng)文件的輪轉(zhuǎn)恋拷。

SizeBasedTriggeringPolicy 只接收 maxFileSize 這一個(gè)參數(shù),它的默認(rèn)值是 10 MB钮糖。

maxFileSize 可以為字節(jié)梅掠,千字節(jié),兆字節(jié)店归,千兆字節(jié)阎抒,通過在數(shù)值后面指定一個(gè)后綴 KBMB 或者 GB消痛。例如且叁,50000005000KB秩伞,5MB 以及 2GB 都是有效的先蒋,前三個(gè)是一樣的秸谢。

<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>

Logback Classic

雖然日志事件在 logback-core 是通用的偷厦,但是在 logback-classic 中捆愁,它們永遠(yuǎn)是 ILoggingEvent 的實(shí)例。logback-classic 只不過是用來處理 ILoggingEvent 實(shí)例的專門處理管道脸爱。

SocketAppender and SSLSocketAppender

到目前為止遇汞,所介紹的 appender 只能將日志輸出到本地資源。相反的是簿废,SocketAppender 被設(shè)計(jì)成可以將 ILoggingEvent 實(shí)例序列化再傳輸?shù)竭h(yuǎn)端機(jī)器空入。當(dāng)使用 SocketAppender 時(shí),日志事件將以明文發(fā)送族檬,使用 SSLSocketAppender 時(shí)歪赢,日志事件將通過安全的通道傳輸。

序列化事件的實(shí)際類型為 LoggingEventVO单料,它實(shí)現(xiàn)了 ILoggingEvent 接口埋凯。就日志事件而言点楼,遠(yuǎn)程日志是非侵入式的。在接收到日志事件并反序列化之后递鹉,日記事件就像在本地生成的一樣盟步。多個(gè) SocketAppender 實(shí)例運(yùn)行在不同的機(jī)器上,直接將它們的日志通過固定的格式輸出到中央日志服務(wù)器上躏结。SocketAppender 不會(huì)關(guān)聯(lián) layout,因?yàn)樗切蛄谢罩臼录竭h(yuǎn)程服務(wù)器上狰域。SocketAppenderTransmission Control Protocol (TCP) 層上運(yùn)行媳拴,該層提供了可靠,有序兆览,流式控制以及段對(duì)端的八位字節(jié)流屈溉。所以,如果遠(yuǎn)程服務(wù)器是可以到達(dá)的抬探,那么日志事件最終都會(huì)到達(dá)那里子巾。相反,如果遠(yuǎn)程服務(wù)器掛掉或者不可達(dá)到小压,那么日志事件會(huì)被丟棄线梗。如果服務(wù)器重新恢復(fù),那么日志事件的傳輸將會(huì)繼續(xù)進(jìn)行怠益。這種重連是通過一個(gè)連接線程池周期性的嘗試連接來進(jìn)行的仪搔。

日志事件由本地 TCP 實(shí)現(xiàn)自動(dòng)緩沖。也就是說如果連接到服務(wù)器的速度很慢蜻牢,但是比客戶端產(chǎn)生日志事件的速度要快烤咧,那么客戶端不會(huì)受到網(wǎng)速的影響。但是抢呆,如果網(wǎng)絡(luò)連接速度比日志產(chǎn)生速度要慢煮嫌,那么客戶端只能以網(wǎng)絡(luò)速度進(jìn)行處理。特別在極端情況下抱虐,連接到服務(wù)器的網(wǎng)絡(luò)掛掉了昌阿,客戶端最終會(huì)被阻塞。如果網(wǎng)絡(luò)連接恢復(fù)了梯码,但是服務(wù)器掛掉了宝泵,客戶端不會(huì)阻塞,盡管因?yàn)榉?wù)器掛掉了轩娶,日志事件丟失儿奶。

盡管 SocketAppender 不再依賴任何的 logger,在當(dāng)前線程的連接下也不會(huì)被垃圾收集鳄抒。但是連接線程只有在服務(wù)器掛掉的情況下才存在闯捎,為了避免出現(xiàn)垃圾收集問題椰弊,你需要明確的關(guān)閉 SocketAppender。生命周期長(zhǎng)的應(yīng)用會(huì)創(chuàng)建/銷毀許多 SocketAppender 實(shí)例瓤鼻,應(yīng)該注意到這個(gè)垃圾回收問題秉版。大部分的應(yīng)用可以忽略這個(gè)問題。如果 JVM 在 SocketAppender 關(guān)閉之前退出茬祷,無論是顯式的退出或者是通過后續(xù)的垃圾回收清焕,都可能會(huì)導(dǎo)致未傳輸?shù)臄?shù)據(jù)在管道中被丟失。這是基于 windows 系統(tǒng)常見的問題祭犯。為了避免數(shù)據(jù)丟失秸妥,通常的做法是調(diào)用 close() 方法去關(guān)閉 SocketAppender,或者在應(yīng)用退出之前調(diào)用 LoggerContextstop() 方法沃粗。

遠(yuǎn)程服務(wù)器通過 remoteHostport 屬性來標(biāo)識(shí)粥惧。SocketAppender 屬性羅列在下表當(dāng)中。SSLSocketAppender 支持額外的一些屬性最盅,將在第十五章進(jìn)行討論突雪。

屬性名 類型 描述
includeCallerData boolean 如果為 true,那么調(diào)用者的信息也會(huì)被發(fā)送到服務(wù)端涡贱。默認(rèn)為 false咏删。
port int 遠(yuǎn)程服務(wù)器的端口號(hào)
reconnectionDelay Duration 接受一個(gè)表示持續(xù)時(shí)間的字符串,例如 "10 seconds" 代表每次重連的間隔時(shí)間盼产。默認(rèn)值為 30 秒饵婆。將這個(gè)值設(shè)置為 0,將會(huì)關(guān)閉重連機(jī)制戏售。注意侨核,如果連接服務(wù)器成功,則不會(huì)出現(xiàn)連接線程池灌灾。
queueSize int 接受一個(gè)整數(shù)(大于0)代表有多少個(gè)日志事件傳輸?shù)椒?wù)端搓译。當(dāng)這個(gè)值為 1 時(shí),會(huì)同步傳輸日志事件到遠(yuǎn)端锋喜。當(dāng)這個(gè)值大于 1 時(shí)些己,將設(shè)隊(duì)列中還有剩余的空間,那么一個(gè)新的事件將會(huì)入隊(duì)嘿般。隊(duì)列的長(zhǎng)度大于 1 可以提高性能段标,消除網(wǎng)絡(luò)引起的延時(shí)。<br /><br />參見 eventDelayLimist 屬性炉奴。
eventDelayLimit Duration 接受一個(gè)表示持續(xù)時(shí)間的字符串逼庞,例如:"10 seconds"。它表示一旦當(dāng)前隊(duì)列已滿(例如已經(jīng)包含了 queueSieze 個(gè)事件)瞻赶,在丟棄事件之前的等待時(shí)間赛糟。在遠(yuǎn)端服務(wù)器一直緩慢接收事件時(shí)派任,這種情況就會(huì)發(fā)生。默認(rèn)值為 100 毫秒璧南。
remoteHost String 服務(wù)器的主機(jī)名
ssl SSLConfiguration 只支持 SSLSocketAppender掌逛,這個(gè)屬性提供了 SSL 配置供 appender 使用,將在第十五章進(jìn)行討論

日志服務(wù)器的選擇

logback classic 給服務(wù)器提供了兩個(gè)選擇接收來自 SocketAppenderSSLSocketAppender 的日志事件司倚。

  • ServerSocketReceiver 與跟它相對(duì)應(yīng)的具有 SSL 功能的 SSLServerSocketReceiver 都是接收組件豆混。可以通過配置應(yīng)用中的 logback.xml 文件來接收遠(yuǎn)程 socket appender 的日志事件对湃。查看第十四章獲取更多的信息崖叫。
  • SimpleSocketServer 與具有 SSL 功能的 SimpleSSLSocketServer 都提供了一個(gè)簡(jiǎn)單的 java 應(yīng)用程序,該應(yīng)用程序被設(shè)計(jì)成可配置拍柒,并且可以在命令行界面運(yùn)行。這些應(yīng)用僅僅等待來自 SocketAppenderSSLSocketAppender 的日志事件屈暗,每個(gè)被接收的日志事件按照本地服務(wù)器策略進(jìn)行打印拆讯。下面給出一個(gè)簡(jiǎn)單的例子。

使用 SimpleSocketServer

SimpleSocketServer 應(yīng)用接收兩個(gè)命令行參數(shù):portconfigFile养叛。port 監(jiān)聽的端口种呐,configFile 表示 XML 格式的配置文件。

logback-examples/ 文件夾下弃甥,通過一下命令來啟動(dòng) SimpleSocketServer

java ch.qos.logback.classic.net.SimpleSocketServer 6000 \
  src/main/java/chapters/appenders/socket/server1.xml

6000 為監(jiān)聽的端口爽室,在 server1.xml 中,ConsoleAppenderRollingFileAppender 被添加到 root logger 上淆攻。

在啟動(dòng)了 SimpleSocketServer 之后阔墩,你可以在多個(gè)客戶端上使用 SocketAppender 來發(fā)送日志事件。這個(gè)手冊(cè)中相關(guān)的示例包含了兩個(gè)這樣的客戶端:chapters.appenders.SocketClient1chapters.appenders.SocketClient2瓶珊。兩個(gè)客戶端都會(huì)等待用戶在控制臺(tái)輸入字符啸箫。輸入的字符會(huì)被包裹在 debug 級(jí)別的日志事件中,然后發(fā)送到遠(yuǎn)程服務(wù)器伞芹。這個(gè)兩個(gè)客戶端不同的地方在于 SocketAppender 的配置忘苛。SocketClient1 通過編碼來配置,SocketClient2 需要獲取一個(gè)配置文件唱较。

SimpleSocketServer 在本地機(jī)器上啟動(dòng)之后扎唾,通過以下命令去連接:

java chapters.appenders.socket.SocketClient1 localhost 6000

你輸入的每一行字符都會(huì)出現(xiàn)在之前啟動(dòng) SimpleSocketServer 的控制臺(tái)上。如果你停止或者重啟 SimpleSocketServer南缓,客戶端會(huì)重連新的服務(wù)實(shí)例胸遇,但是在斷開連接時(shí)的日志時(shí)間會(huì)丟失(不能取消)。

SocketClient1 不同的是西乖,SocketClient2 需要獲取一個(gè) XML 格式的配置文件來進(jìn)行配置狐榔。配置文件 client1.xml 如下所示坛增,它創(chuàng)建了一個(gè) SocketAppender 附加到 root logger 上。

Example: client1.xml

<configuration>
          
  <appender name="SOCKET" class="ch.qos.logback.classic.net.SocketAppender">
    <remoteHost>${host}</remoteHost>
    <port>${port}</port>
    <reconnectionDelay>10000</reconnectionDelay>
    <includeCallerData>${includeCallerData}</includeCallerData>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="SOCKET" />
  </root>  

</configuration>

注意配置文件中的 remoteHost薄腻,port收捣,includeCallerData 屬性的值并沒有直接給出,而是通過占位符來代替庵楷。這些值可以通過系統(tǒng)屬性來指定:

java -Dhost=localhost -Dport=6000 -DincludeCallerData=false \
  chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/client1.xml

這個(gè)命令應(yīng)該會(huì)得到跟之前 SocketClient1 這個(gè)例子類似的結(jié)果罢艾。

讓我再重復(fù)強(qiáng)調(diào)一遍,日志事件的序列化沒有侵入性尽纽。反序列化出來的日志事件像其它的日志事件一樣攜帶同樣的信息咐蚯。可以像操作本地日志事件一樣操作它弄贿,除了序列化日志事件默認(rèn)不會(huì)包含調(diào)用者的信息春锋。下面通過一個(gè)例子來說明,首先通過一下命令啟動(dòng) SimpleSocketServer

 java ch.qos.logback.classic.net.SimpleSocketServer 6000 \
  src/main/java/chapters/appenders/socket/server2.xml

配置文件 server2.xml 創(chuàng)建了一個(gè)可以輸出調(diào)用者文件名以及行號(hào)的 ConsoleAppender差凹。如果你像之前通過配置文件 client1.xml 來運(yùn)行 SocketClient2期奔,你將會(huì)在兩個(gè)括號(hào)之間看到兩個(gè)問號(hào),而不是調(diào)用者的文件名以及行號(hào):

2006-11-06 17:37:30,968 DEBUG [Thread-0] [?:?] chapters.appenders.socket.SocketClient2 - Hi

可以通過設(shè)置 includeCallerData 的值為 true危尿,來改變 SocketAppender 的輸出信息呐萌。使用如下命令:

java -Dhost=localhost -Dport=6000 -DincludeCallerData=true \
  chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/client1.xml

因?yàn)榉葱蛄谢鰜淼氖录梢韵癖镜厝罩緯r(shí)間一樣被處理,所以它們甚至可以被傳送到第二個(gè)服務(wù)器做進(jìn)一步的處理谊娇。在練習(xí)的時(shí)候肺孤,你可以設(shè)置兩臺(tái)服務(wù)器,第一臺(tái)服務(wù)器接受客戶端的日志事件济欢,然后轉(zhuǎn)發(fā)到第二臺(tái)服務(wù)器赠堵。

使用 SimpleSSLSocketServer

SimpleSSLSocketServer 跟之前使用的 SimpleSocketServer 一樣,在命令行接收兩個(gè)參數(shù):port船逮,configFile顾腊。此外,你必須為日志服務(wù)器的 X.509 認(rèn)證通過系統(tǒng)屬性提供位置與密碼信息挖胃。

logback-examples/ 文件夾下杂靶,通過如下命令來啟動(dòng) SimpleSSLSocketServer

java -Djavax.net.ssl.keyStore=src/main/java/chapters/appenders/socket/ssl/keystore.jks \
    -Djavax.net.ssl.keyStorePassword=changeit \
    ch.qos.logback.classic.net.SimpleSSLSocketServer 6000 \
    src/main/java/chapters/appenders/socket/ssl/server.xml

SimpleSSLSocketServer 示例使用 X.509 認(rèn)證,非常適合測(cè)試以及實(shí)驗(yàn)酱鸭。在生產(chǎn)環(huán)境使用 SimpleSSLSocketServer 之前吗垮,你應(yīng)該為你的日志服務(wù)器獲取一個(gè) X.509 認(rèn)證來標(biāo)識(shí)你的服務(wù)器。詳情請(qǐng)參考第十五章凹髓。

因?yàn)榉?wù)器的配置文件在根元素上指定了 debug="true"烁登,所以你將會(huì)在服務(wù)器啟動(dòng)的過程中看到 SSL 的配置信息。這在驗(yàn)證本地安全策略是否被正確實(shí)現(xiàn)時(shí)非常有效。

SimpleSSLSocketServer 啟動(dòng)的時(shí)候饵沧,你可以使用 SSLSocketAppender 來連接服務(wù)器锨络。下面這個(gè)例子展示所需要的配置信息:

<configuration debug="true">
          
  <appender name="SOCKET" class="ch.qos.logback.classic.net.SSLSocketAppender">
    <remoteHost>${host}</remoteHost>
    <port>${port}</port>
    <reconnectionDelay>10000</reconnectionDelay>
    <ssl>
      <trustStore>
        <location>${truststore}</location>
        <password>${password}</password>
      </trustStore>
    </ssl>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="SOCKET" />
  </root>  

</configuration>

注意,跟之前一樣狼牺,remoteHost羡儿、port 的值通過占位符來指定。另外是钥,注意一下 ssl 屬性掠归,它包含了一個(gè)內(nèi)置屬性 trustStore,通過這個(gè)屬性來指定信用商店的位置以及密碼悄泥。這個(gè)配置非常的必要虏冻,因?yàn)槲覀兪褂米院灻淖C書。見第十五章來查看更多使用 SSLSocketAppender 關(guān)于 SSL 的配置信息弹囚。

我們使用這個(gè)配置來運(yùn)行一個(gè)客戶端實(shí)例厨相,在命令行通過系統(tǒng)屬性指定占位符的值:

java -Dhost=localhost -Dport=6000 \
    -Dtruststore=file:src/main/java/chapters/appenders/socket/ssl/truststore.jks \
    -Dpassword=changeit \
    chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/ssl/client.xml

跟之前的示例一樣,在客戶端提示的時(shí)候輸入一些信息鸥鹉,然后這些信息將會(huì)傳送到服務(wù)端(使用安全的通道)领铐,并展示在控制臺(tái)。

注意宋舷, truststore 的值是通過在命令行使用系統(tǒng)屬性指定了一個(gè)標(biāo)識(shí)信用商店的文件路徑。你也可以使用 classpath 路徑瓢姻,具體參考第十五章祝蝠。

我們可以看到跟服務(wù)器啟動(dòng)時(shí)類似的信息,因?yàn)槲覀冊(cè)诳蛻舳说呐渲梦募母刂刑砑恿?debug="true"幻碱,客戶端啟動(dòng)的時(shí)候绎狭,詳細(xì)打印了 SSL 的配置信息來幫助我們驗(yàn)證本地策略的一致性。

ServerSocketAppender and SSLServerSocketAppender

我們之前討論過的 SocketAppender 組件(以及具有 SSL 能力的副本)被設(shè)計(jì)成允許應(yīng)用程序通過網(wǎng)絡(luò)連接遠(yuǎn)程日志服務(wù)器褥傍,以便傳輸日志事件儡嘶。在某些情況下,通過應(yīng)用程序初始化一個(gè)對(duì)日志服務(wù)器的連接可能不方便或者不可行恍风。在這些情況下蹦狂,logback 提供了 ServerSocketAppender

ServerSocketAppender 不會(huì)初始化一個(gè)到日志服務(wù)器的連接朋贬,而是被動(dòng)的監(jiān)聽 TCP 端口凯楔,等待客戶端的連接。日志事件被傳輸給這個(gè) appender锦募,然后再分發(fā)給每個(gè)連接的客戶端摆屯。如果沒有客戶端連接,日志事件會(huì)被馬上丟棄糠亩。

除了基本的 ServerSocketAppender 之外虐骑,logback 還提供了 SSLServerSocketAppender准验,它通過一個(gè)安全,加密的通道傳輸日志事件到每個(gè)連接的客戶端廷没。而且糊饱,具有 SSL 功能的 appender 完全支持基于證書的雙向認(rèn)證,只有認(rèn)證通過的客戶端才可以連接到這個(gè) appender 去接收日志事件腕柜。

對(duì)日志事件進(jìn)行編碼再傳輸?shù)姆椒ㄅc SocketAppender 完全一致济似,每個(gè)日志事件都是 ILoggingEvent 的實(shí)例。只不過連接的起始方向是相反的盏缤。雖然 SocketAppender 充當(dāng)了一個(gè)主動(dòng)的角色去連接日志服務(wù)器砰蠢,ServerSocketAppender 是被動(dòng)的監(jiān)聽即將到來的日志事件。

ServerSocketAppender 的子類型只提供給 logback 的接收組件唉铜。關(guān)于接收組件的信息台舱,請(qǐng)查看第十四章

ServerSocketAppender 支持如下的配置屬性:

屬性名 類型 描述
address String appender 監(jiān)聽的本地網(wǎng)絡(luò)接口地址潭流。如果沒有指定竞惋,則監(jiān)聽所有的網(wǎng)絡(luò)接口
includeCallerData boolean 如果為 true,調(diào)用者的信息將會(huì)發(fā)送給遠(yuǎn)程服務(wù)器灰嫉。為 false拆宛,則不發(fā)送。
port int appender 監(jiān)聽的端口
ssl SSLConfiguration 僅僅支持 SSLServerSocketAppender讼撒。具體參見第十五章

以下是關(guān)于 ServerSocketAppender 的配置:

<configuration debug="true">
    <appender name="SERVER" class="ch.qos.logback.classic.net.server.ServerSocketAppender">
        <port>${port}</port>
        <includeCallerData>${includeCallerData}</includeCallerData>
    </appender>
    
    <root level="debug">
        <appender-ref ref="SERVER" />
    </root>
</configuration>

注意這個(gè)配置跟之前 SocketAppender 的配置只有 class 這個(gè)屬性不同浑厚。remoteHost 缺失表示 appender 被動(dòng)的等待遠(yuǎn)程主機(jī)的連接,而不是新開一個(gè)到遠(yuǎn)程日志服務(wù)器的連接根盒。

以下是關(guān)于 SSLServerSocketAppender 的配置:

<configuration debug="true">
  <appender name="SERVER" 
    class="ch.qos.logback.classic.net.server.SSLServerSocketAppender">
    <port>${port}</port>
    <includeCallerData>${includeCallerData}</includeCallerData>
    <ssl>
      <keyStore>
        <location>${keystore}</location>
        <password>${password}</password>
      </keyStore>
    </ssl>
  </appender>

  <root level="debug">
    <appender-ref ref="SERVER" />
  </root>  

</configuration>

這個(gè)配置跟上一個(gè)配置主要的不同在于 appender 的 class 屬性為 SSLServerSocketAppender 類型钳幅,包含一個(gè)嵌套的 ssl 元素。在這個(gè)例子中炎滞,為 appender 配置了 X.509 認(rèn)證的 keyStore敢艰。

具體的 SSL 配置參見第十五章,這句話翻的我想吐了

因?yàn)?ServerSocketAppender 的子類是專門為接收組件設(shè)計(jì)的册赛,所以我們將對(duì)這個(gè)的闡述推遲到 第十四章 介紹钠导。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市击奶,隨后出現(xiàn)的幾起案子辈双,更是在濱河造成了極大的恐慌,老刑警劉巖柜砾,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件湃望,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)证芭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門瞳浦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人废士,你說我怎么就攤上這事叫潦。” “怎么了官硝?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵矗蕊,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我氢架,道長(zhǎng)傻咖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任岖研,我火速辦了婚禮比规,結(jié)果婚禮上掌敬,老公的妹妹穿的比我還像新娘统求。我一直安慰自己熊镣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布拓售。 她就那樣靜靜地躺著窥摄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪础淤。 梳的紋絲不亂的頭發(fā)上溪王,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音值骇,去河邊找鬼。 笑死移国,一個(gè)胖子當(dāng)著我的面吹牛吱瘩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播迹缀,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼使碾,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了祝懂?” 一聲冷哼從身側(cè)響起票摇,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎砚蓬,沒想到半個(gè)月后矢门,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年祟剔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了隔躲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡物延,死狀恐怖宣旱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情叛薯,我是刑警寧澤浑吟,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站耗溜,受9級(jí)特大地震影響组力,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜强霎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一忿项、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧城舞,春花似錦轩触、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拉馋,卻和暖如春榨为,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背煌茴。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工随闺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蔓腐。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓矩乐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親回论。 傳聞我的和親對(duì)象是個(gè)殘疾皇子散罕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353