Log4j2架構及概念簡介

此文爲讀log4j2 user guaid時的翻譯及筆記。log4j2與log4j在Logger的繼承關係和配置方式上都作出了修改。我的感受比較有意思的是Logger對象與 LoggerConfig解耦的設計,以及Filter中的傳遞機制,有點像網絡包分發,不過多了不少可調控性。java

前言

log4j2能夠按照開發人員預先的設定,在指定的位置和狀況下打印log語句,而且能夠酌情關閉某些log語句,如開發階段debug類型的語句等。而且,可使用layout來定義輸出語句的格式,像C語言的printf函數同樣。如:服務器

要實現這樣標準化的日誌輸出,只須要在工程中引入log4j2的相關jar包,並向LogManager對象申請一個Logger對象的引用,而後調用該對象的相應方法便可,如:網絡

在log4j2中,一共有五種log level,分別爲TRACE, DEBUG,INFO, WARN, ERROR 以及FATAL運維

 

  • FATAL:用在極端的情形中,即必須立刻得到注意的狀況。這個程度的錯誤一般須要觸發運維工程師的尋呼機。socket

  • ERROR:顯示一個錯誤,或一個通用的錯誤狀況,但還不至於會將系統掛起。這種程度的錯誤通常會觸發郵件的發送,將消息發送到alert list中,運維人員能夠在文檔中記錄這個bug並提交。函數

  • WARN:不必定是一個bug,可是有人可能會想要知道這一狀況。若是有人在讀log文件,他們一般會但願讀到系統出現的任何警告。spa

  • INFO用於基本的、高層次的診斷信息。在長時間運行的代碼段開始運行及結束運行時應該產生消息,以便知道如今系統在幹什麼。可是這樣的信息不宜太過頻繁。線程

  • DEBUG:用於協助低層次的調試。翻譯

  • TRACE:用於展示程序執行的軌跡。debug

 

Log4j2類圖

 

經過類圖可用看到:

每個log上下文對應一個configuration,configuration中詳細描述了log系統的各個 LoggerConfig、Appender(輸出目的地)、EventLog過濾器等。每個Logger又與一個LoggerConfig相關聯。另 外,能夠看到Filter的種類不少,有聚合在Configuration中的filter、有聚合在LoggerConfig中的filter也有聚合 在Appender中的filter。不一樣的filter在過濾LogEvent時的行爲和判斷依據是不一樣的,具體可參加本文後面給出的文檔。

應用程序經過調用log4j2API並傳入一個特定的名稱來向LogManager請求一個Logger實例。LogManager會定位到適當的 LoggerContext 而後經過它得到一個Logger。若是LogManager不得不新建一個Logger,那麼這個被新建的Logger將與LoggerConfig相關聯,這個LoggerConfig的名稱中包含以下信息中的一種:①與Logger名稱相同的②父logger的名稱③ root 。當一個LoggerConfig的名稱與一個Logger的名稱能夠徹底匹配時,Logger將會選擇這個LoggerConfig做爲本身的配置。若是不能徹底匹配,那麼Logger將按照最長匹配串來選擇本身所對應的LoggerConfigLoggerConfig對象是根據配置文件來建立的。LoggerConfig會與Appenders相關聯,Appenders用來決定一個log request將被打印到那個目的地中,可選的打印目的地不少,如console、文件、遠程socket server等。。LogEvent是由Appenders來實際傳遞到最終輸出目的地的,而在EvenLog到達最終被處理以前,還須要通過若干filter的過濾,用來判斷該EventLog應該在何處被轉發、何處被駁回、何處被執行。

Log4j中各個概念的簡要介紹

Logger間的層次關係

相比於純粹的System.out.println方式,使用logging API的最首要以及最重要的優點是能夠在禁用一些log語句塊的同時容許其餘的語句塊的輸出。這一能力創建在一種假設之上,即全部在應用中可能出現的logging語句能夠按照開發者定義的標準分紅不一樣的類型。

在 Log4j 1.x版本時,Logger的層次是靠Logger類之間的關係來維護的。但在Log4j2中, Logger的層次則是靠LoggerConfig對象之間的關係來維護的。

LoggerLoggerConfig均是有名稱的實體。Logger的命名是大小寫敏感的,而且服從以下的分層命名規則。(與java包的層級關係相似)。例如:com.foocom.foo.Bar的父級;javajava.util的父級,是java.util.vector的祖先。

 root LoggerConfig位於LoggerConfig層級關係的最頂層。它將永遠存在與任何LoggerConfig層次中。任何一個但願與root LoggerConfig相關聯的Logger能夠經過以下方式得到:

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

其餘的Logger實例能夠調用LogManager.getLogger 靜態方法並傳入想要獲得的Logger的名稱來得到。

 

LoggerContext

LoggerContextLogging System中扮演了錨點的角色。根據狀況的不一樣,一個應用可能同時存在於多個有效的LoggerContext中。在同一LoggerContext下,log system是互通的。如:Standalone ApplicationWeb ApplicationsJava EE Applications"Shared" Web Applications REST Service Containers,就是不一樣廣度範圍的log上下文環境。

 

Configuration

每個LoggerContext都有一個有效的ConfigurationConfiguration包含了全部的Appenders、上下文範圍內的過濾器、LoggerConfigs以及StrSubstitutor.的引用。在重配置期間,新與舊的Configuration將同時存在。當全部的Logger對象都被重定向到新的Configuration對象後,舊的Configuration對象將被停用和丟棄。

 

Logger

如前面所述, Loggers 是經過調用LogManager.getLogger方法得到的。Logger對象自己並不實行任何實際的動做。它只是擁有一個name 以及與一個LoggerConfig相關聯。它繼承了AbstractLogger類並實現了所需的方法。當Configuration改變時,Logger將會與另外的LoggerConfig相關聯,從而改變這個Logger的行爲。

得到Logger

使用相同的名稱參數來調用getLogger方法將得到來自同一個Logger的引用。如:

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

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

xy指向的是同一個Logger對象。

log4j環境的配置是在應用的啓動階段完成的。優先進行的方式是經過讀取配置文件來完成。

log4j使採用類名(包括完整路徑)來定義Logger 名變得很容易。這是一個頗有用且很直接的Logger命名方式。使用這種方式命名能夠很容易的定位這個log message產生的類的位置。固然,log4j也支持任意string的命名方式以知足開發者的須要。不過,使用類名來定義Logger名仍然是最爲推崇的一種Logger命名方式。

 

LoggerConfig

Loggerconfiguration中被描述時,LoggerConfig對象將被建立。LoggerConfig包含了一組過濾器。LogEvent在被傳往Appender以前將先通過這些過濾器。過濾器中包含了一組Appender的引用。Appender則是用來處理這些LogEvent的。

Log層次:

每個LoggerConfig會被指定一個Log層次。可用的Log層次包括TRACE, DEBUG,INFO, WARN, ERROR 以及FATAL。須要注意的是,在log4j2中,Log的層次是一個Enum型變量,是不能繼承或者修改的。若是但願得到跟多的分割粒度,可用考慮使用Markers來替代。

Log4j 1.x Logback 中都有「層次繼承」這麼個概念。可是在log4j2中,因爲LoggerLoggerConfig是兩種不一樣的對象,所以「層次繼承」的概念實現起來跟Log4j 1.x Logback不一樣。具體狀況下面的五個例子。

例子一:

可用看到,應用中的LoggerConfig只有root這一種。所以,對於全部的Logger而言,都只能與該LoggerConfig相關聯而沒有別的選擇。

例子二:

在例子二中能夠看到,有5種不一樣的LoggerConfig存在於應用中,而每個Logger都被與最匹配的LoggerConfig相關聯着,而且擁有不一樣的Log Level

例子三:

能夠看到Logger rootXX.Y.Z都找到了與各類名稱相同的LoggerConfig。而LoggerX.Y沒有與其名稱相徹底相同的LoggerConfig。怎麼辦呢?它最後選擇了X做爲它的LoggerConfig,由於X LoggerConfig擁有與其最長的匹配度。

例子四:

能夠看到,如今應用中有兩個配置好的LoggerConfigrootX。而Logger有四個:rootXX.YX.Y.Z。其中,rootX都能找到徹底匹配的LoggerConfig,而X.YX.Y.Z則沒有徹底匹配的LoggerConfig,那麼它們將選擇哪一個LoggerConfig做爲本身的LoggerConfig呢?由圖上可知,它們都選擇了X而不是root做爲本身的LoggerConfig,由於在名稱上,X擁有最長的匹配度。

例子五

能夠看到,如今應用中有三個配置好的LoggerConfig,分別爲:rootXX.Y。同時,有四個Logger,分別爲:rootXX.Y以及X.YZ。其中,名字能徹底匹配的是rootXX.Y。那麼剩下的X.YZ應該匹配X仍是匹配X.Y呢?答案是X。由於匹配是按照標記點(即「.」)來進行的,只有兩個標記點之間的字串徹底匹配纔算,不然將取上一段徹底匹配的字串的長度做爲最終匹配長度。

 

Filter

與防火牆過濾的規則類似,log4j2的過濾器也將返回三類狀態:Accept(接受), Deny(拒絕) 或Neutral(中立)。其中,Accept意味着不用再調用其餘過濾器了,這個LogEvent將被執行;Deny意味着立刻忽略這個event,並將此event的控制權交還給過濾器的調用者;Neutral則意味着這個event應該傳遞給別的過濾器,若是再沒有別的過濾器能夠傳遞了,那麼就由如今這個過濾器來處理。

 

Appender

logger的不一樣來決定一個logging request是被禁用仍是啓用只是log4j2的情景之一。log4j2還容許將logging requestlog信息打印到不一樣的目的地中。在log4j2的世界裏,不一樣的輸出位置被稱爲Appender。目前,Appender能夠是console、文件、遠程socket服務器、Apache FlumeJMS以及遠程 UNIX 系統日誌守護進程。一個Logger能夠綁定多個不一樣的Appender

能夠調用當前ConfigurationaddLoggerAppender函數來爲一個Logger增長。若是不存在一個與Logger名稱相對應的LoggerConfig,那麼相應的LoggerConfig將被建立,而且新增長的Appender將被添加到此新建的LoggerConfig中。爾後,全部的Loggers將會被通知更新本身的LoggerConfig引用(PS:一個LoggerLoggerConfig引用是根據名稱的匹配長度來決定的,當新的LoggerConfig被建立後,會引起一輪配對洗牌)。

在某一個Logger中被啓用的logging request將被轉發到該Logger相關聯的的全部Appenders上,而且還會被轉發到LoggerConfig的父級的Appenders上。

這樣會產生一連串的遺傳效應。例如,對LoggerConfig B來講,它的父級爲AA的父級爲root。若是在root中定義了一個Appenderconsole,那麼全部啓用了的logging request都會在console中打印出來。另外,若是LoggerConfig A定義了一個文件做爲Appender,那麼使用LoggerConfig ALoggerConfig Blogger logging request都會在該文件中打印,而且同時在console中打印。

若是想避免這種遺傳效應的話,能夠在configuration文件中作以下設置:

additivity="false"

這樣,就能夠關閉Appender的遺傳效應了。具體解釋見:

 

Layout

一般,用戶不止但願能定義log輸出的位置,還但願能夠定義輸出的格式。這就能夠經過將Appender與一個layout相關聯來實現。Log4j中定義了一種相似C語言printf函數的打印格式,如"%r [%t] %-5p %c - %m%n" 格式在真實環境下會打印相似以下的信息:

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

其中,各個字段的含義分別是:

%r 指的是程序運行至輸出這句話所通過的時間(以毫秒爲單位);

%t 指的是發起這一log request的線程;

%c 指的是loglevel

%m 指的是log request語句攜帶的message

%n 爲換行符。

相關文章
相關標籤/搜索