1. 程式人生 > >Log4j2官方架構文件翻譯

Log4j2官方架構文件翻譯

官網原文標題《Architecture》

翻譯時間:2017-11-14

譯者:本文介紹了log4j的主要構成元件和核心概念,並就每個元件分別進行了講解。尤其需要讀者重點理解的是log level的繼承概念,以及appender的additivity屬性。仔細理解本片後,可繼續學習配置的文章。

後續閱讀:《Configuration》(還未更新)

架構

主要元件

log4j使用下圖中所展示的class

使用Log4j2 API的程式需要向LogManager請求一個指定名稱的Logger。LogManager會定位到合適的LoggerContext,然後從它獲取logger。如果必須去建立logger,這會關聯到包含下面因素的LoggerConfig a)同樣名稱的logger,b)父包的名稱 或者c)root LoggerConfig。LoggerConfig物件通過配置中的Logger宣告來建立。LoggerConfig會關聯上appender,實際上由他負責傳遞LogEvent。

Logger的層級

任何超越System.out.println的日誌API,最首要的優勢就是它有能力使某些指定的日誌程式碼片段失效,同時允許其他的正常列印。這種能力假定日誌的空間,所有可能的日誌片段的空間,基於開發者給出的條件進行分類。

在Log4j 1.x中,Logger 層級通過Logger之間的關係進行維護。在Log4j2中,這種關係不復存在。與之代替的,層級結構圍護在LoggerConfig物件之間。

Logger和LoggerConfig是一組被命名的實體。Logger的命名是大小寫敏感的,並且遵守層級命名規範:

命名層級

一個LoggerConfig可以被稱為另外一個LoggerConfig的祖先,如果他的名字後面跟了一個點作為子孫logger名稱的字首。一個LoggerConfig可以稱為孩子LoggerConfig的父親,如果在他們之間沒有任何祖先。

例如,一個loggerConfig稱為"com.foo",是"com.foo.bar"這個logger的父親。相似的,"java"是"java.util"的父親,是"java.util.Vector"的祖先。這種命名的方式應該對於絕大多數開發者是很熟悉的。

root loggerConfig存在於LoggerConfig層級的頂層。它作為一個例外,它一直存在並且它是任何層級的一部分。直接關聯到root LoggerConfig上的logger,可以按如下方式得到:

Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);

其實,還可以更為簡單:

Logger logger = LogManager.getRootLogger();

所有其他的Logger也可以通過傳遞Logger名稱給LogManager.getLogger靜態方法被查到。更多關於Logging API的資訊可以在訪問Log4j 2 API.

LoggerContext

LoggerContext扮演著日誌系統中的錨點角色。其實,我們可能在程式中有多個啟用的LoggerContext,這取決於環境。關於LoggerContext更多的細節,在Log Separation 章節。

Configuration

每個LoggerContext都有一個啟用的Configuration。這個configuration含有所有的Appender,context-wide Filter,LoggerConfig,並且含有StrSubstitutor的引用。在重新配置的過程中,會存在兩個Configuration物件。一旦所有的Logger都重新關聯到新的配置上,舊的configuraion就會被停掉並被拋棄。

Logger

像之前所說,Logger通過呼叫LogManager.getLogger來建立。Logger本身並不會執行任何直接的動作。他只是有個名稱並且關聯到LoggerConfig上。它繼承於AbstractLogger ,實現了必要的方法。在當配置發生變化時,Logger可能被關聯到另外的loggerConfig上。這會使得他們的行為被修改。

Retrieving Logger

通過傳入同樣名稱,呼叫LogManager.getLogger,將會返回給你同樣的Logger物件引用。

例如:

Logger x = LogManager.getLogger("wombat"); 

Logger y = LogManager.getLogger("wombat");

X和y關聯到同一個logger物件上。

Log4j環境的配置一般會在程式初始化的時候完成。最佳的方式是讀取配置檔案。這些在 Configuration中討論。

Log4j使得通過軟體元件命名logger變得很容易。我們可以在class中通過初始化Logger來完成,使用class的全路徑名稱作為logger的名稱。這是很有用,並且直接的定義logger的方式。當log輸出產生日誌的logger名稱時,這種命名策略使得識別log訊息的來源變得很簡單。然而,這只是一個可能的,普通的,命名logger的策略。Log4j並不會限制,開發者可以自由命名。

用自己class的名稱來命名logger是通用的習慣,所以LogManager.getLogger()非常的方便,可以自動使用class名稱作為Logger的名稱。

雖然如此,以logger所在的class來命名logger,看起來還是目前最好的策略。

LoggerConfig

日誌配置中宣告Logger的時候,LoggerConfig物件被建立。LoggerConfig包含了一組Filter,在LogEvent傳遞給任何的appender之前,他必須允許LogEvent通過。他含有一組用於處理event的appender引用。

Log Level

LoggerConfig會被指定Log Level。內建的Level包含 TRACE,DEBUG,INFO,WARN,ERROR,及FATAL。Log4j 2也支援客製化log level。另外一種得到更細粒度的機制是使用Markers 代替。

Log4j 1.x及Logback都有"Level繼承" 的概念,在Log4j 2中,Logger及LoggerConfig是兩個不同的物件。所以這個概念實現的不太一樣。每個Logger關聯到適合的LoggerConfig上,LoggerConfig可以反過來關聯到他的父親上,這可以達到同樣的效果。

下面五張表中,展示了很多給定的level值,以及會造成的關聯到每個Logger上的level。注意下面所有的情況,如果root LoggerConfig沒有被配置,預設的層級將會被指定給它。

Logger 名稱 被分配的 LoggerConfig LoggerConfig Level Logger Level
root root DEBUG DEBUG
X root DEBUG DEBUG
X.Y root DEBUG DEBUG
X.Y.Z root DEBUG DEBUG

在上面的例1中,只有root logger被配置了,並且有log level。所有其他的Logger關聯到root LoggerConfig上,並且使用它的level

Logger 名稱 被分配的 LoggerConfig LoggerConfig Level Logger Level
root root DEBUG DEBUG
X X ERROR ERROR
X.Y X.Y INFO INFO
X.Y.Z X.Y.Z WARN WARN

例2中,所有的logge都有配置的LoggerConfig,並且從它那裡獲取level

Logger 名稱 被分配的 LoggerConfig LoggerConfig Level Logger Level
root root DEBUG DEBUG
X X ERROR ERROR
X.Y X ERROR ERROR
X.Y.Z X.Y.Z WARN WARN

例3中,root、X、X.Y.Z分別有配置的同名LoggerConfig。X.Y Logger沒有配置匹配名稱的LoggerConfig,所以使用了X這個LoggerConfig,因為這個LoggerConfig的名稱最長匹配了Logger的名稱的開始。

Logger 名稱 被分配的 LoggerConfig LoggerConfig Level Logger Level
root root DEBUG DEBUG
X X ERROR ERROR
X.Y X ERROR ERROR
X.Y.Z X ERROR ERROR

例4中,root和X logger都有自己配置的同名LoggerConfig。X.Y、X.Y.Z沒有配置的LoggerConfig,所以她們的level來自於分配給它們的X這個LoggerConfig。因為他的名稱最長匹配了logger名稱的開始

Logger 名稱 被分配的 LoggerConfig LoggerConfig Level Logger Level
root root DEBUG DEBUG
X X ERROR ERROR
X.Y X.Y INFO INFO
X.YZ X.Y ERROR ERROR

例5中,root、X、X.Y都有各自配置好的同名LoggerConfig。X.YZ這個logger沒有配置好的LoggerConfig,所以他的level來自於指定給他的loggerConfig X。因為X名稱最長匹配了logger名稱的開始。他並沒有關聯到X.Y這個loggertConfig,因為句點後的字元並沒有精確匹配。

Logger 名稱 被分配的 LoggerConfig LoggerConfig Level Logger Level
root root DEBUG DEBUG
X X ERROR ERROR
X.Y X.Y ERROR
X.Y.Z X.Y ERROR

例6中,X.Y這個LoggerConfig沒有配置level,所以他繼承了X這個LoggerConfig的level。X.Y.Z這個logger使用X.Y這個LoggerConfig,因為他沒有同名匹配的LoggerConfig。他也從LoggerConfig X那裡繼承了他的日誌level。

下面的表格表明了Level過濾是如何工作的。表各種縱向表頭是logEvent的level,橫向的表頭是關聯了相應level的LoggerConfig。相交點指出了是否LogEvent允許被傳遞做進一步處理,還是被丟棄。

Event Level LoggerConfig Level
TRACE DEBUG INFO WARN ERROR FATAL OFF
ALL YES YES YES YES YES YES NO
TRACE YES NO NO NO NO NO NO
DEBUG YES YES NO NO NO NO NO
INFO YES YES YES NO NO NO NO
WARN YES YES YES YES NO NO NO
ERROR YES YES YES YES YES NO NO
FATAL YES YES YES YES YES YES NO
OFF NO NO NO NO NO NO NO

Filter

在之前的章節,額外的自動日誌過濾會如我們所想而發生,Log4j提供Filter可以應用在把控制傳遞給任何LoggerConfig之前。在控制傳遞給LoggerConfig之後但是呼叫任何appender之前,在控制傳遞給LoggerConfig之前,但是在呼叫指定的Apeender之前。方式上很像防火牆filter,每個filter可以返回三種結果,Accept、Deny、Neutral。Accept含義是其他的Filter都不用再呼叫了,event應該被處理。Deny意味著event要立即被忽略,控制返回給呼叫者。Neutral表明event要被傳遞給其他的Filter。如果這裡沒有其他的filter,那麼event將被處理。

儘管event可能被filter接收,但是仍舊有可能不被記錄。這可能發生在,當event被前置的LoggerConfig Filter所接受,但是被LoggerConfig的filter所拒絕或者被所有的appender所拒絕。

Appender

基於logger配置,選擇性啟用或者禁用日誌請求的能力只是整體的一小部分。Log4j允許日誌請求來記錄日誌到不同的目的地。在Log4j中的叫法,輸出的目的地稱為Appender。當前,Appender有console,file,remote scoket伺服器,Apache Flume,JMS,remote UNIX Syslog daemon,還有很多種DB的API。學習 Appenders 章節來獲取更多可用型別的資訊。一個Logger可以分配不止一個Appender。

Appender可以被新增進logger,通過呼叫當前configuration的addLoggerAppender 方法。如果匹配Logger名稱的LoggerConfig並不存在,那麼會建立一個,Appender會被附上,之後所有Logger都會被通知更新他們的LoggerConfig引用。

對於給定的logger,任何啟用的日誌請求,都會被傳送給這個Logger的LoggerConfig中所有的appender,同樣傳遞給LoggerConfig的父親的appender。換句話講,Appender通過LoggerConfig的層級被追加繼承。舉個例子,如果console Appender被新增進root Logger,那麼所有啟用的日誌請求都會至少在console中列印。如果一個額外的檔案appender被新增到一個叫做C的LoggerConfig中,那麼對於C及C的孩子,所有的可用的日誌請求都會被記錄到檔案裡及console中。我們也可以通過在配置檔案中,關於Logger的宣告部分設定addtivity="false",以此來重寫預設的行為。

支配appender追加屬性的規則總結如下:

Appender追加性

Logger L的日誌輸出會進入和L關聯的LoggerConfig以及這個loggerConfig祖先的所有Appender。這就是appender追加性的含義。

然而,如果一個Logger L關聯的LoggerConfig的祖先,稱之為P吧,他的additivity標識被設為了false,之後L的輸出將會直接給所有L的loggerConfig中的appender,也會給向上直到P的祖先的appender,包含P。但是並不會給P的任何祖先的appender。

Logger的additivity標識預設設為true

下表展示了一個例子:

Logger Name Added Appenders Additivity Flag Output Targets Comment
root A1 not applicable A1 The root logger has no parent so additivity does not apply to it.
x A-x1, A-x2 true A1, A-x1, A-x2 Appenders of "x" and root.
x.y none true A1, A-x1, A-x2 Appenders of "x" and root. It would not be typical to configure a Logger with no Appenders.
x.y.z A-xyz1 true A1, A-x1, A-x2, A-xyz1 Appenders in "x.y.z", "x" and root.
security A-sec false A-sec No appender accumulation since the additivity flag is set to false.
security.access none true A-sec Only appenders of "security" because the additivity flag in "security" is set to false.

Layout

通常,使用者想要客製化的不僅僅是輸出的目的地,還有輸出的格式。這可以通過給appender關聯layout來達成。Layout負責根據使用者希望的那樣來格式化LogEvent,反之,appender負責把格式好的輸出傳送到目的地。PatternLayout,log4j標準釋出版中的一部分,可以讓使用者指定輸出的格式,通過類似C語言print函式中約定的pattern。

例如,使用轉換pattern的PatternLayout "%r [%t] %-5p %c - %m%n" 將會輸出類似下面的內容:

176 [main] INFO org.foo.Bar - Located nearest gas station.

第一個欄位是程式啟動後,經過的毫秒數。第二個欄位是產生日誌的執行緒。第三個欄位是日誌片段的等級,第四個欄位是logger關聯的名稱。‘-’後面的文字是訊息片段。

Log4j 帶有許多不同的Layout,用於各種情況,如JSON、XML、HTML及Syslog。其他database聯結器appender,需要輸入制定的欄位,而不是普通的文字layout。

重要的是,log4j可以渲染 log訊息的內容,通過使用者指定的條件。例如,如果你需要頻繁的記錄Oranges,你當前專案中的一種物件型別。你可以建立Orangemessage,它可以接受Orange的實體並且把它傳遞給Log4j。這樣在需要的時候,Orange物件可以被格式化適合的byte陣列。

StrSubstitutor and StrLookup

StrSubstitutor 類 和 StrLookup 介面,其實是借自 Apache Commons Lang ,然後經過修改後支援LogEvent的求值。另外Interpolator class也是借自Apache Commons Configuration,來讓StrSubstitutor通過各種StrLookup去查變數的值。他也被修改來支援logEvent求值。放在一起,它們提供了一個機制,允許相關變數的配置來自於System Properties,配置檔案,ThreadContext Map,LogEvent中的StructuredData。在配置處理或者在每個event被處理的時候,如果元件有處理變數的能力,那麼變數將被賦值。檢視Lookups 獲取更多資訊。