1. 程式人生 > >Log4j 2.x的體系架構詳解

Log4j 2.x的體系架構詳解

本譯文主要內容分為以下兩部分:
  1. Log4j 2.x的層次結構
  2. Log4j 2.x的主要元件詳解
更多有關Log4j的配置詳解及例子詳見Log4j 2.x 配置詳解及詳細配置例子

主要元件

  Log4j使用下圖中顯示的類。
類圖
  使用Log4j 2 API的應用程式將向LogManager請求具有特定名稱的Logger。LogManager將找到相應的LoggerContext,然後從中獲取Logger。如果必須建立Logger,則它將與LoggerConfig相關聯,該LoggerConfig包含 a)與Logger相同的名稱,b)父包的名稱,或者c)根LoggerConfig。
  LoggerConfig物件是從配置中的Logger宣告中建立的。LoggerConfig與實際傳遞LogEvent的Appender相關聯。

Logger層次結構

  任何日誌API優於樸素的System.out.println的優勢在於它能夠禁用某些日誌語句,同時允許其他語句無阻礙地進行輸出。這種效能基於記錄空間,即所有可能的記錄語句的空間,按照一些開發人員選擇的標準進行分類。

  在Log4j 1.x中,記錄器層次結構是通過記錄器之間的關係來維護的。在Log4j 2中,這種關係不再存在。相反,這種層次結構通過LoggerConfig物件之間的關係來維持。

  Loggers 和LoggerConfigs是已命名的實體。Logger名稱區分大小寫,它們遵循分層命名規則:

命名層次結構
  如果一個LoggerConfig的名字+‘.’是後代名稱的字首, 則稱該LoggerConfig是另一個LoggerConfig 的祖先。
  如果一個LoggerConfig和後代LoggerConfig之間沒有祖先,此LoggerConfig被稱為是子 LoggerConfig 的父級。

  例如,名為"com.foo" 的LoggerConfig是名為"com.foo.Bar"的LoggerConfig的父級。類似地,"java""java.util"的父級和"java.util.Vector"的祖先。這個命名方案應該是大多數開發人員都熟悉的。

  根LoggerConfig位於LoggerConfig層次結構的頂部。作為唯一一個例外,它始終存在,並且是每個層次的一部分。直接連結到根LoggerConfig的Logger可以如下獲得:

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

  或者

Logger logger = LogManager.getRootLogger();

  所有其他的Logger可以使用 LogManager.getLogger 靜態方法通過傳遞所需記錄器的名稱來檢索。有關Logging API的更多資訊可以在Log4j 2 API中找到。

LoggerContext

  LoggerContext 在記錄系統裡充當錨點的作用。但根據具體情況,應用程式中可能會有多個活動的LoggerContext。LoggerContext的更多細節在Log Separtion部分。

配置

  每個LoggerContext都有一個活動的 Configuration。該配置包含所有Appender,覆蓋上下文的Filter,LoggerConfig,幷包含對StrSubstitutor的引用。在重新配置期間,兩個配置物件將同時存在。一旦所有Logger重定向到新的配置,舊的配置將被停止並丟棄。

Logger

  如上所述,Logger是通過呼叫LogManager.getLogger建立的 。Logger本身不執行直接操作。它只是一個名字,並與一個LoggerConfig相關聯。它擴充套件了 AbstractLogger 並實現了所需的方法。如果配置被修改,Logger可能會與不同的LoggerConfig關聯,從而導致其行為被修改。

檢索Logger

  使用相同名稱去呼叫LogManager.getLogger方法將始終返回對完全相同的Logger物件的引用。如:

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

xy指的是完全相同的Logger物件。

  Log4j環境的配置通常在應用程式初始化時完成。首選的方法是讀取配置檔案。這在配置中討論。

  Log4j可以很容易地通過軟體元件來命名Logger。這可以通過在每個類中例項化一個Logger來完成,Logger名稱等於該類的完全限定名稱。這是定義Logger的有用和直接的方法。由於日誌輸出帶有生成日誌記錄器的名稱,因此該命名策略可以輕鬆識別日誌訊息的來源。然而,這只是一種可能的,儘管是通用的命名記錄器的策略。Log4j不限制Logger命名規則。開發人員可以根據需要自由地命名Logger。

  由於在擁有類之後命名“記錄器”是一種常見習慣用法,所以提供了便捷方法 LogManager.getLogger()來自動使用呼叫類的完全限定類名作為記錄器名稱。

  儘管如此,以類的名稱來命名Logger似乎是目前已知的最好的策略。

LoggerConfig

  LoggerConfig 物件是在日誌記錄配置中宣告Logger時建立的。LoggerConfig包含一組過濾器,任何LogEvent必須經過過濾後再傳遞給Appender。它還包含一組Appender(應用於處理事件)的引用。

日誌級別

  LoggerConfig將被分配一個日誌級別。內建級別包括TRACE,DEBUG,INFO,WARN,ERROR和FATAL。Log4j 2還支援自定義日誌級別。獲得更多粒度的另一個機制是使用標記

  Log4j 1.xLogback 都有“級別繼承”的概念。在Log4j 2中,Logger和LoggerConfig是兩個不同的物件,所以這個概念的實現方式有所不同。每個Logger引用相應的LoggerConfig,這個LoggerConfig又可以引用它的父代,從而達到相同的效果。

  以下是五個具有各種指定級別值的表格以及與每個Logger關聯的結果級別。請注意,在所有這些情況下,如果未配置根LoggerConfig,則將為其分配預設級別。

Logger Name Assigned LoggerConfig LoggerCf Level Logger Level
root root DEBUG DEBUG
X root DEBUG DEBUG
X.Y root DEBUG DEBUG
X.Y.Z root DEBUG DEBUG

在上述示例1中,只有根Logger被配置並且具有日誌級別。所有其他的Logger引用根LoggerConfig並使用它的Level。

Logger Name Assigned LoggerConfig LoggerCf 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中,所有Logger都有一個已配置的LoggerConfig並從中獲取它們的Level。

Logger Name Assigned LoggerConfig LoggerCf 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中,Logger root、X 和 X.Y.Z 都有一個名稱相同的LoggerConfig。Logger X.Y 沒有配置的具有匹配名稱的LoggerConfig,因此使用LoggerConfig X的配置, 因為其名稱與Logger的字首名稱具有最長匹配。

Logger Name Assigned LoggerConfig LoggerCf Level Logger Level
root root DEBUG DEBUG
X X ERROR ERROR
X.Y X ERROR ERROR
X.Y.Z X ERROR ERROR

在上述示例4中,Logger root和X 和都有一個名稱相同的LoggerConfig。Logger X.Y 和X.Y.Z沒有配置的具有匹配名稱的LoggerConfig,因此使用LoggerConfig X的配置, 因為其名稱與Logger的字首名稱具有最長匹配。

Logger Name Assigned LoggerConfig LoggerCf Level Logger Level
root root DEBUG DEBUG
X X ERROR ERROR
X.Y X.Y INFO INFO
X.YZ X ERROR ERROR

在上述示例5中,Logger root、X 和 X.Y 都有一個名稱相同的LoggerConfig。Logger X.YZ 沒有配置的具有匹配名稱的LoggerConfig,因此使用LoggerConfig X的配置, 因為其名稱與Logger的字首名稱具有最長匹配。它不與LoggerConfig X.Y關聯, 因為令牌必須完全匹配

Logger Name Assigned LoggerConfig LoggerCf Level Logger Level
root root DEBUG DEBUG
X X ERROR ERROR
X.Y X.Y ERROR
X.Y.Z X.Y ERROR

在上述示例6中,LoggerConfig X.Y沒有配置級別,所以它從LoggerConfig X繼承它的級別。Logger X.Y.Z使用LoggerConfig X.Y,因為它沒有名稱完全匹配的LoggerConfig。它也從LoggerConfig X繼承它的日誌級別。

級別過濾原理

  下表說明了級別過濾的工作原理。在表中,垂直標題顯示LogEvent的級別,而水平標題顯示與相應的LoggerConfig關聯的級別。交叉點標識是否允許LogEvent進行進一步處理(YES)或丟棄(NO)。

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還提供過濾器,可以在控制權傳遞給任何LoggerConfig之前應用;或在控制權傳遞給LoggerConfig之後但在呼叫任何Appender之前應用;或在控制權傳遞給LoggerConfig之後,但在呼叫特定的Appender之前應用;以及給每個Appender新增過濾。以與防火牆過濾器非常相似的方式,每個過濾器可以返回三個結果之一:Accept, DenyNeutralAccept的響應意味著不應該呼叫其他過濾器,並且事件將被處理。Deny的迴應意味著事件應該立即被忽略,控制權應該返回給呼叫者。Neutral的響應表示該事件應該傳遞給其他過濾器。如果沒有其他過濾器,事件將被處理。

注:一個事件可能被一個過濾器接受,但事件仍然可能不會被記錄。這種情況發生在事件被pre-LoggerConfig過濾器接受,但是被LoggerConfig過濾器拒絕,或被所有Appender拒絕。

Appender

  根據logger選擇性地啟用或禁用記錄請求的能力只是Log4j的其中一個作用。Log4j允許記錄請求列印到多個目的地。在Log4j中,輸出目標被稱為 Appender。目前,控制檯,檔案,遠端套接字伺服器,Apache Flume,JMS,遠端UNIX Syslog守護程式以及各種資料庫API都有其對應的appender。更多詳細資訊,請參閱Appender部分 。一個Logger可以連線多個Appender。

  可以通過呼叫當前配置的addLoggerAppender方法將Appender新增到Logger中 。如果與Logger名稱匹配的LoggerConfig不存在,則將建立一個LoggerConfig,將Appender附加到該LoggerConfig,然後所有Logger被通知去更新其LoggerConfig引用。

  對於給定的logger,每個啟用的記錄請求將被轉發給Logger的LoggerConfig中的所有appender以及LoggerConfig父級的Appender。 換句話說,Appender是從LoggerConfig層次繼承的。例如,如果將控制檯appender新增到根日誌記錄器,則所有啟用的日誌記錄請求將至少在控制檯上列印。如果此時一個file appender新增到名為C的LoggerConfig,那啟用日誌請求時C和C的子級將在一個檔案和在控制檯上列印。可以重寫此預設行為,通過在配置檔案的Logger宣告中設定additivity = "false",可使Appender累積功能不再具有可加性。

以下總結了Appender可加性的規則。

Appender的可加性
  Logger L的日誌語句的輸出將傳遞到LoggerConfig L 自身祖先中關聯的所有Appender。這就是“appender additivity”的意思。
  
  但是,如果與Logger L關聯的LoggerConfig祖先 P,將其可加性標誌設為false,那麼L的輸出將被傳遞到LoggerConfig L和它的祖先直至P(包括P)中所有的appender,但不會傳遞至P的祖先中所關聯的Appenders 。
  
  記錄器預設情況下將其可加性標誌設定為true

Logger Name Added Appenders Activity Flag Output Targets Comment
root A1 不適用 A1 根記錄器沒有父物件,所以可加性不適用於它。
x A-x1, A-x2 true A1, A-x1, A-x2 “x”和root的Appenders。
x.y true A1, A-x1, A-x2 “x”和root的Appenders,沒有配置Appender的logger是不常見的。
x.y.z A-xyz1 true A1, A-x1, A-x2, A-xyz1 “x.y.z”、”x”和root的Appenders。
security A-sec false A-sec 由於可加性標誌設定為false,所以沒有appender的積累。
security.access true A-sec 只有”security”的appender,因為”security”中的可加性標誌被設定為false

Layout

  多數情況下,使用者希望不僅自定義輸出目標,而且還要自定義輸出格式。這是通過將Layout與Appender關聯來實現的 。佈局負責根據使用者的意願格式化LogEvent,而appender負責將格式化的輸出傳送到目的地。所述的PatternLayout,是log4j分發標準的一部分,能讓使用者根據類似於C語言的printf函式的轉換模式來指定輸出格式。

  例如,具有轉換模式“%r [%t]%-5p%c - %m%n”的PatternLayout將輸出類似於:

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

  第一個欄位是程式啟動以來經過的毫秒數。第二個欄位是處理日誌請求的執行緒。第三個欄位是日誌語句的級別。第四個欄位是與日誌請求關聯的logger的名稱。“ - ”後面的文字是該陳述的訊息。

  Log4j 為各種用例(如JSON,XML,HTML和Syslog(包括新的RFC 5424版本))提供了許多不同的佈局。其他appender(如資料庫聯結器)將填充指定的欄位而不是特定的文字佈局。

  同樣重要的是,log4j將根據使用者指定的標準呈現日誌訊息的內容。例如,如果您經常需要記錄當前專案中使用的物件型別Oranges,則可以建立一個接受Orange例項的OrangeMes​​sage類並將其傳遞給Log4j,以便在將Orange物件格式化為合適的位元組陣列時需要。

StrSubstitutor和StrLookup

  該 StrSubstitutor 類和 StrLookup 介面是從Apache Commons Lang中借用的,然後加以修改來支援評估LOGEVENTS。另外 Interpolator 類是從Apache Commons Configuration借用來允許StrSubstitutor評估來自多個StrLookups的變數。它也被修改為支援評估LogEvents。上述程式碼提供了一種機制,允許配置引用來自系統屬性,配置檔案,LogEvent中的ThreadContext Map,StructuredData的變數。如果元件能夠處理它,則可以在處理配置時或處理每個事件時解析變數。請參閱 Lookups 瞭解更多資訊。