Log4j的中文文檔

譯者聲明:<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

1.       這是根據最新的log4j(jakarta-log4j-1.2.8)的開發包自帶文檔的manual翻譯的

2.       譯者盡力保持原文格式,以便於讀者查找原文

3.       對一些關鍵詞都帶有原文單詞,便於閱讀

4.       原文中比較生僻的單詞,譯者都是參考金山詞霸,對找不到的組合詞都是保持原文。

5.       對比較拗口的說法,譯者儘量採用意譯的方法,同時保持原文。

6.       有幾個地方是log4j知識的深入探討,翻譯的不是很好,以後會重新修改

王建

2004618

譯文:

版權所有2000-2002Apache軟件組織。保留一切權利。這個軟件是在Apache軟件許可證1.1版本條款的保護下發布的。這個協議的內容都包含在LICENSE.txt文件中隨log4j一起發佈的。這份文檔是基於2000年11月JavaWorld發表的"Log4j delivers control over logging"文章。然而,目前這篇文章包含更詳細的內容和最近的信息。這篇文章也從同一個作者所著的"The complete log4j manual"借鑑了一些內容。

摘要

這篇文章描述了Log4j的API、獨一無二的特色和設計原理。Log4j是一個聚集了許多作者勞動成果的開源軟件項目。它允許開發人眼以任意的粒度輸出日誌描述信息。它利用外部的配置文件,在運行時是完全可配置的。最厲害的是,log4j有一條平滑的學習曲線。當心:從用戶的反饋表明,它是很容易上癮的。

介紹

幾乎每個大型的應用程序都包含有自己的日誌記錄或跟蹤API。與這個原則一致,E.U. SEMPER項目決定寫自己的跟蹤API。這事發生在1996年。在多次改進以後,經過幾次演化和大量的工作使之逐漸變成了log4j,一個流行java日誌包。這個軟件包是在apache軟件許可證的保護下發布的,開源組織主動性保證了這是一個完整的開源許可證。最新的log4j版本包含了源代碼、類文件和可以在http://jakarta.apache.org/log4j/找到的文檔。順便說一下,log4j已經被發展到了C, C++, C#, Perl, Python, Ruby,和Eiffel語言。

在代碼裏插入日誌描述代碼是一種低級的調試方法。由於調試器並不總是可用的或者可應用的,因此這可能是唯一的方法。這對多線程應用和分佈式應用來說是非常普遍的現象。

經驗表明日誌是開發環節中一個重要的組件。它提供了好多的有點。對一個正在運行的應用程序而言,它可以提供準確的環境信息。一旦插入了代碼,日誌輸出就不需要認爲的干涉。還有,日誌輸出可以保存在永久的媒體中,供以後研究。包括它在開發環節的作用,一個高效的功能豐富的日誌包可以被看作一款審計工具。

就像Brian W. Kernighan和Rob Pike在他們的扛鼎之作《編程實踐》中寫下的

 

日誌代碼有它自己的缺點。它可能會導致應用程序運行變慢。假如輸出太詳細,可能會導致屏幕閃動(scrolling blindness)。爲了減輕這些影響,log4j被設計爲可依賴的,更快的和可擴展的。由於日誌很少是應用程序關注的焦點,所以log4j API力爭做到簡單並易於理解和使用。

記錄器(Loggers),輸出源(Appenders)和佈局器(Layouts)

Log4j包含三個首要組件:記錄器,輸出源和佈局器。這三類組件一起工作使開發者可以按消息的類別和等級來輸出消息,並且控制在運行時這些消息怎麼格式化和在哪裏輸出這些信息。

記錄器層次

任意一個log4j API最大的優點是平滑了System.out.println固有的能力,當允許其他人不受妨礙的打印時使某些日誌語句不起作用。這個能力假定日誌空間,也就是所有的可能的日誌語句的空間,是可以按照開發者的標準來分類的。這個觀察資料以前已經引導我們選擇類別作爲包的中心概念。然而,自從 log4j的1.2版本,記錄器(Logger)類已經取代了範圍(Category)類,對那些熟悉log4j早期版本的人來說,記錄器(Logger)類可以被認爲僅僅是範圍(Category)類的別名(alias)。

記錄器被命名爲實體(Loggers are named entities),記錄器(Logger)的命名是事件敏感的(case-sensitive),並且他們遵循層次的(hierarchical)命名規則:

按層次命名

假如一個記錄器的名稱後面跟着一個被認爲是子記錄器前綴的“.”號,那麼它就被認爲是另一個記錄器的祖先

例如,名稱爲“com.foo”的記錄器是名稱爲“com.foo.Bar”的父。相似的是,“java”是“java.util”的父,是“java.util.Vector”的祖先。這個命名規則對大多數的開發人員來說應該是很熟悉的

根記錄器(root logger)處於記錄器層次的頂端.在兩種情況下,它是意外的。

1.       它總是存在

2.       它不可以通過名稱獲得

調用類的靜態方法Logger.getRootLogger獲得根類.所有其他類都被實例化,並且用類的靜態方法Logger.getLogger獲得這些實例。這個方法用期望的記錄器作爲參數。記錄器類的一些基本方法如下:

記錄器可以被設置級別。可能的級別包括DEBUG, INFO, WARN, ERROR和FATAL,這些級別被定義在org.apache.log4j.Level類中。儘管我們不鼓勵,但是你還是可以通過子類化級別類來定義你自己的級別。一個更好的方法將在後面介紹

假如一個給定的記錄器沒有被設置級別,它可以集成一個最近的帶有指定級別的祖先。更正式地:

Level Inheritance 級別繼承

繼承的級別被指定給記錄器類C,在記錄器層次中它是和第一個非空級別相等的

爲了保證所有的記錄器最終可以繼承一個級別,根記錄器總是有一個被指定的記錄器。

下面是四個表,這些錶帶有不同指定級別值和參照上面規則的繼承級別的結果

記錄器名稱

指定的級別

繼承的級別

root

Proot

Proot

X

none

Proot

X.Y

none

Proot

X.Y.Z

none

Proot

範例 1

在上面的範例1中,僅僅根記錄器被指定了級別。這個級別的值是Proot,它被其它的記錄器X, X.Y和X.Y.Z繼承

記錄器名稱

指定的級別

繼承的級別

root

Proot

Proot

X

Px

Px

X.Y

Pxy

Pxy

X.Y.Z

Pxyz

Pxyz

範例 2

在範例2中所有的記錄器都有一個指定的級別值,這就沒有必要繼承級別值了。

記錄器名稱

指定的級別

繼承的級別

root

Proot

Proot

X

Px

Px

X.Y

none

Px

X.Y.Z

Pxyz

Pxyz

範例 3

在範例3中,所有的記錄器,包括X 和 X.Y.Z都被分別指定記錄器值爲Proot、Px和Pxyz。記錄器X.Y從它的父X繼承它的級別值

記錄器名稱

指定的級別

繼承的級別

root

Proot

Proot

X

Px

Px

X.Y

none

Px

X.Y.Z

none

Px

範例 4

 

在範例4中,記錄器root和X分別被指定級別值爲Proot和Px。記錄器X.YX.Y.Z從最接近它們的父X繼承它們的級別值,這個父有一個指定的級別值…

通過調用一個記錄器實例的打印方法來處理日誌請求。這些打印方法是debug, info, warn, error, fatallog.

通過定義,打印方法決定一個日誌請求的級別。例如,假如c是一個記錄器實例,語句c.info("..")是一個帶有INFo級別的日誌請求。

若日誌請求的級等於或者大於日誌記錄器的級別,那麼這個日誌請求就是可行的,相反,請求將不能輸出。一個沒有被指定級別的日誌記錄器將從層次(hierarchy)繼承。這些規則在下面總結。

基本的選擇規則

在一個具有q級別的日誌記錄器中(指定和繼承都是合適的)有一個具有p級別的日誌請求,若p>=q,則這個日誌請求是可以輸出的。

這個規則是log4j的核心。假定級別是排序的,對標準的級別來說,我們設定DEBUG < INFO < WARN < ERROR < FATAL。

用同一個名稱調用getLogger方法將返回一個指向同一個記錄器對象的引用

例如,

xy指向同一個日誌記錄器對象

因此,配置一個日誌記錄器,不用在代碼裏轉換引用就可以獲得相同的實例是可能的。在生物學父時代的基本矛盾裏面,總是可以preceed他們的孩子,log4j的日誌記錄器可以按一定的規則創建和配置。尤其是,即使一個“父”日誌記錄器在它的子孫後面被實例化,它仍然可以發現並連接到它的子孫

在應用程序初始化的時候,Log4j的配置被執行。最好的方法是通過讀取一個配置文件。很快就會討論這個方法

通過使用軟件組件,log4j很容易命名日誌記錄器。這可以通過在每個類中靜態實例化日誌記錄器來完成,日誌記錄器名是和完整的類名相同的。這是一個直截了當地定義日誌記錄器的有用方法。由於日誌輸出帶有產生該日誌的日誌記錄器的名稱,命名策略讓辨認產生日誌消息的源頭很容易。然而,這僅僅是是一個可能,雖然命名日後子記錄器的策略很普通(However, this is only one possible, albeit common, strategy for naming loggers)。Log4j沒有約束日誌記錄器可能趨勢(Log4j does not restrict the possible set of loggers)。開發者可以很自由的根據需要命名日誌記錄器。

不過,在類後面命名日誌記錄器好像是目前所知最好的策略

輸出源和佈局器

基於日誌記錄器有選擇地讓日誌請求是否起作用地能力僅僅描述(picture)地一部分.log4j允許日誌請求輸出到多個目標。在log4j地聲明中,輸出目的地被成爲輸出源(appender).最近,輸出源包括控制然臺、文件、GUI組件、遠程套接字服務器(remote socket servers)、JMS、NT事件記錄器(NT Event Loggers)和遠程UNIX Syslog守護進程(remote UNIX Syslog daemons)。它也可以異步地記錄日誌。

一個日誌記錄器可以有多個輸出源。

AddAppender方法加一個輸出源到給定的日誌記錄器。對應給定的日誌記錄器每個激活的日誌請求都將被轉向到所有的輸出源,因爲這些輸出源是和層次 (hierarchy) 中更高級別的輸出源一樣的。換句話說,輸出源是被從日誌記錄器的層次附加繼承的(appenders are inherited additively from the logger hierarchy)。例如,假如有一個控制檯輸出源被加到一個根日誌記錄器(root logger),最後所有被激活的日誌請求都將打印在控制檯上,另外,假如一個文件輸出源被加到日誌記錄器,叫做C,然後激活到C和C的孩子的日誌請求將輸出到一個文件和控制檯。覆蓋這個默認的行爲是可能,以便於通過設定附加標識爲假,輸出源的聚集不再是附加的。

管理輸出源附加行爲的規則將在下面總結。

輸出源的附加特性

日誌記錄器C的日誌語句的輸出將定向到C和它的祖先中的所有的輸出源。這是條款“輸出源的附加特性(appender additivity)”的意圖.

然而,假如有一個日誌記錄器C的祖先,叫做P,有一個附加標識被設置爲false,然後C的輸出將被定向到C和直到C的祖先P(包括P)中的所有的輸出源,但是不包括P的祖先的中的任何輸出源。

日誌記錄器有它自己附加特性,該特性被默認設置爲true

下表展示了一個例子:

日誌記錄器

添加的輸出源

附加特性標識

輸出目標

評論

root

A1

not applicable

A1

根日誌記錄器是匿名的,但是可以用Logger.getRootLogger()方法來存取。根日誌記錄器沒有默認輸出源。

x

A-x1, A-x2

true

A1, A-x1, A-x2

x和根的輸出源

x.y

none

true

A1, A-x1, A-x2

x和根的輸出源

x.y.z

A-xyz1

true

A1, A-x1, A-x2, A-xyz1

x.y.z x和根的輸出源

security

A-sec

false

A-sec

由於附加標識被設置爲false,沒有輸出源聚集

security.access

none

true

A-sec

由於安全中附加標識被設置爲false,所以僅僅有安全的輸出源。

時常,用戶不僅希望自定義目的地,而且包括輸出格式的自定義。這是通過給輸出源設定一個佈局器(layout)來到達目的地。

例如,帶有"%r [%t] %-5p %c - %m%n"轉換格式的PatternLayout佈局器將輸出和下面的內容類似。

第一個字段是自從程序開始到目前花費的時間。第二個字段是發出日誌請求的線程。第三個字段是日誌語句的級別。第四個是和該日誌請求關聯的日誌記錄器的名稱。緊接着“-”符號後面的內容是日誌語句的消息。

正像這樣重要,log4j將按用戶指定的標準修飾(render,這樣翻譯,不知是否合適)日誌信息的內容。例如,假如你經常需要記錄Oranges,這是一個在你當前項目中使用的對象類別,你可以註冊一個OrangeRenderer類,在某個orang需要記錄日誌的時候將調用OrangeRenderer類

對象的修飾(Object rendering)遵循類層次。例如,假定oranges是水果,若你註冊了一個FruitRenderer類,包括所有的oranges水果都將被FruitRenderer類修飾,除非你給orange指定一個OrangeRenderer。

renderer對象必須實現ObjectRenderer接口

配置

插入應用程序代碼的日誌請求需要相當大的準備和努力。觀察表明大約有4%的代碼是用來輸出日誌的。結果,即使適度大小的應用程序也有數以千計的日誌語句被嵌在代碼中。給定他們數字,管理這些語句變成了急迫的事情,而不需要沒有手工修改的。

Log4j的環境是完全參數化的配置。然而,用配置文件配置log4j是非常靈活的。目前,配置文件可以使用XML或者java屬性文件(鍵值)格式

然我們嘗試一下怎樣用log4j 配置一個虛構的應用程序MyApp

MyApp從導入相關類開始。它然後使用MyApp定義了一個靜態的日誌記錄器變量,這個MyApp恰好是一個完整的類名。

MyApp用到了定義在com.foo包中的Bar類

BasicConfigurator.configure方法的調用創建了一個比較簡單的log4j設置。這個方法是硬連線(hardwired)地添加到根日誌記錄器的ConsoleAppender輸出源。輸出將使用佈局器PatternLayout來格式化,佈局器PatternLayout被設定爲"%-4r [%t] %-5p %c %x - %m%n"的格式。

注意默認值,根日誌記錄器被設定爲Level.DEBUG級別。

MyApp的輸出是:

下面這個圖描述了在調用BasicConfigurator.configure 方後之後MyApp的對象圖

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />

作爲一個側面的注意點,我要提及的是log4j中的子類僅僅連接到他們存在的祖先。特別的,名爲com.foo.Bar的日誌記錄器被直接連接到根日誌記錄器,因而圍繞在未使用的com或者com.foo日誌記錄器。這個顯著地提高了性能,並且減少了log4j地內存消耗(footprint)

MyApp類通過調用BasicConfigurator.configure方法來配置log4j。其他類僅僅需要導入org.apache.log4j.Logger,取回他們想要的日誌記錄器,並且在遠處記錄。

前面的例子總是輸出相同的日誌信息。幸運的是,很容易修改MyApp,以便可以在運行是控制日誌輸出。下面是一個稍微修改的版本

這個版本的MyApp構造了PropertyConfigurator類來解析一個配置文件,因此建立日誌

下面是一個配置文件的實例,這個配置文件導致輸出和前面也基於這個實例的BasicConfigurator類的輸出完全相同的。

假定我們不再對com.foo包中任何組件的輸出感興趣。下面的配置文件展示了一個可能方法,利用這個方法可以完成這個任務。

用這個文件配置的MyApp的輸出如下所示。

 

由於日誌記錄器com.foo.Bar沒有給定級別,它從com.foo繼承它的級別,在配置文件中com.foo被設定爲WARN。Bar.doIt方法的日誌語句有DEBUG級別,這比日誌記錄器的級別的WARN低。因此doIt()方法的日誌請求被禁止

下面是有多個輸出源的配置文件。

用這個配置文件調用增強後的MyApp將在控制檯上輸出如下內容。

In addition, as the root logger has been allocated a second appender, output will also be directed to the example.log file. This file will be rolled over when it reaches 100KB. When roll-over occurs, the old version of example.log is automatically moved to example.log.1.

另外,由於根日誌記錄器被分配給第二個輸出源,輸出將定向到example.log文件。當這個文件增展100KB時,將會翻轉(rolled over)這個文件,當翻轉發生時,老版本的example.log將被自動移到example.log.1文件

注意,爲獲得這些不同的日誌行爲,我們不必重編譯代碼。我們可以很簡單記錄UNIX Syslog進程,重定向com.foo所有的輸出到一個NT事件記錄器,或者定向日誌事件到一個遠程log4j服務器,這些都可以依照一個本地服務器規則記錄日誌,例如,可以通過定向一個日誌事件到第二個log4j服務器。

默認的初始化進程

Log4j類庫沒有對它的環境作任何假設。特別是,log4j沒有默認的輸出源。然而,在某些定義明確的環境下,日誌記錄器類的靜態的初始化器將嘗試自動配置log4j。java語言保證在往內存中裝載類時,類的靜態初始化器僅僅可以被調用一次。不同類裝載器可能裝載相同類的不同拷貝,記住這是很重要的。Java虛擬機認爲這些相同類的拷貝是完全不相關的。

在依賴運行環境的應用程序的正確入口處,默認的初始化是非常有用的。例如,在web服務器(web-server)的控制下,相同的應用程序可以被當作一個獨立的應用程序、applet或者servlet。

下面定義的是確切的默認注視化算法:

1.    設定log4j.defaultInitO覆蓋系統屬性的爲任何其它值,“false”將導致log4j忽略默認的初始化過程(這個過程)。

2.       設定資源字符串變量爲log4j.configuration的系統屬性值。指定默認初始化文件的最好方法是通過log4j.configuration的系統屬性。萬一系統屬性log4j.configuration沒有定義,可以設定字符串變量資源到它默認值“log4j.properties”。

3.       嘗試轉換資源變量爲URL

4.    假如資源變量不能轉換爲URL,例如由於一個MalformedURLException異常,然後通過調用返回值爲URL的org.apache.log4j.helpers.Loader.getResource(resource, Logger.class)方法在classpath中查找資源。注意,字符串"log4j.properties"包含(constitutes)一個醜陋的URL。

參考Loader.getResource(java.lang.String)方法,獲得查找路徑的列表。

5.    假如沒有URL,忽略默認的初始化。其它,通過URL來配置log4j。

常常使用PropertyConfigurator類解析URL來配置log4j,若URL以“.xml”後綴名結束,將使用DOMConfigurator來解釋。你可以有選擇指定自定義的配置器。log4j.configuratorClass的系統屬性值被當作你自定義配置器的完整類名。你所指定的自定義配置器必須實現Configurator接口。

配置實例

Tomcat下的默認配置

web服務器環境中,Log4j默認的初始化是特別有用的。在Tomcat 3.x 和 4.x下,你應該把log4j.properties放置在你web應用程序的WEB-INF/classes目錄下。Log4會發現這個屬性文件並自己進行初始化。這是很容易做的。

tomcat啓動之前,你也可以選擇設置系統屬性log4j.configuration。Tomcat 3.x的環境變量TOMCAT_OPTS被用來設置命令行選項。Tomcat 4.0設置CATALINA_OPTS環境變量替代TOMCAT_OPTS。

實例 1

Unix shell命令

輸出TOMCAT_OPTS="-Dlog4j.configuration=foobar.txt"告訴log4j用文件foobar.txt作爲默認的配置文件。這個文件應該被放置在你應用程序WEB-INF/classes的目錄下。每個web應用程序將用一個不同的默認配置文件,因爲每個文件都是和相對web應用程序的

實例 2

Unix shell命令

輸出TOMCAT_OPTS="-Dlog4j.debug -Dlog4j.configuration=foobar.xml"告訴log4j輸出log4j的內部調試信息,並用文件foobar.xml作爲默認的配置文件。這個文件應該被放置在你應用程序WEB-INF/classes的目錄下。由於文件是以.xml後綴結束的,所以它將使用DOMConfigurator來讀文件。每個web應用程序將用一個不同的默認配置文件,因爲每個文件都是和相對web應用程序的

 

實例 3

Windows shell命令

設置TOMCAT_OPTS=-Dlog4j.configuration=foobar.lcf -Dlog4j.configuratorClass=com.foo.BarConfigurator告訴log4j用文件foobar.lcf作爲默認的配置文件。由於log4j.configuratorClass這個系統屬性的定義,文件將用com.foo.BarConfigurator定義的配置器(configurator)來讀取。每個web應用程序將用一個不同的默認配置文件,因爲每個文件都是和相對web應用程序的

實例 4

Windows shell命令

設置TOMCAT_OPTS=-Dlog4j.configuration=file:/c:/foobar.lcf告訴log4j用文件c:/foobar.lcf作爲默認的配置文件。配置文件被完整指定通過用URL文件:/c:/foobar.lcf。因此所有的應用程序都使用相同的配置文件。

不同的web應用程序將通過他們各自的類加載器(classloaderss)來加載log4j類。因此,log4j環境的每個映象(image)是獨立執行的,並且不需要任何相互同步。例如,FileAppenders在多個web應用程序配置中定義相同的方法,這將全部寫入相同的文件。這個結果很可能是不太讓人滿意的。你必須確保不同web應用程序的log4j配置不用相同的潛在系統資源。

初始化servlet

用一個特殊的servlet平日之log4j也是可能的。下面是一個例子

在你的web應用程序的web.xml文件中定義下面servlet

寫一個初始化的servlet是初始化log4j的最靈活方法

嵌套診斷環境

大多數的現實世界的系統必須能併發地處理多個客戶端。在這樣一個典型的多線程系統中,不同線程將處理不同客戶端。記錄日誌是特別適於跟蹤和調試複雜的分佈式應用程序。從一個客戶端區分出另一個客戶端的日誌輸出的簡單方法是給每個客戶端實例化以日誌記錄器。這將導致日誌記錄器的增加,並且增加了整個日誌的維護工作。

一個簡單的技術是對每個從相同客戶端交互發出日誌請求都有獨一無二的時間戳。Neil Harrison在《Patterns for Logging Diagnostic Messages》書的Pattern Languages of Program Design 3edited by R. Martin, D. Riehle, and F. Buschmann (Addison-Wesley, 1997)中描述了這個方法。

每個請求都有唯一的時間戳,使用者把環境信息壓進NDC,NDC是Nested Diagnostic Context的縮寫。NDC類如下所示。

每個線程把NDC當作環境信息堆棧來管理。注意,org.apache.log4j.NDC類的所有的方法都是靜態的。假定NDC打印是被證明了的,每次一個日誌請求被處理,在日誌輸出信息中,適當的log4j組件將包含當前線程的完整NDC堆棧。這個在沒有用戶感受的情況下完成,這個用戶負責的僅僅是通過在代碼的某些定義明確的點使用push和pop方法,在NDC中放置正確的信息,

爲了說明這一點,讓我們舉一個例子,這個例子是servlet分發內容到許多客戶端。在執行其他代碼之前Servlet可以在請求開始的時候編譯NDC。環境信息可能是客戶端主機的名字和請求內在的其他信息,典型的是包含cookie的信息。因此,即使sevlet在同時服務多個客戶端,日誌也會通過相同的代碼開始記錄,也就是說,即使依附於相同的日誌記錄器,由於每個客戶頓請求包含不同的NDC堆棧,日誌仍然是可以區分的。

然而,一些健壯的應用程序,例如虛擬主機web服務器,必須根據不同的虛擬主機環境和請求中的軟件組件,記錄不同不同信息。最近log4j發佈的版本開始支持多層次樹。這個提高允許每個虛擬主機擁有它自己的日誌記錄器層次的拷貝

性能

經常提到的放對日誌記錄的是計算消耗。這是一個合理的關注,即使適度大小的應用程序也可能產生數以千計的日誌請求。大量工作被花費在調節和提高日誌的性能上。Log4j主張快和靈活:速度第一,靈活性第二。

The user should be aware of the following performance issues.

用戶應該注意下面的性能問題

1.       當關閉日誌時的日誌性能。

當日志被完全關掉的時候,或者恰恰設置爲一個級別,日誌請求的消耗包括一個方法調用和一個整型比較。在以233 MHz Pentium II的機子上,消耗在5到50納秒範圍內。

然後,方法調用隱含了參數構造的“隱形”消耗。

例如,一些日誌記錄器,如下,

構造消息參數導致消耗,也就是,轉換整型I和數組entry[i]爲字符串,還有連接媒介字符串,不論消息是否記錄。這個參數轉換的消耗可能時非常高的,並且依賴於轉換參數的大小。

避免構造參數消耗的寫法:

這個將不會導致構造參數的消耗,假如調試器是不可用的。另一方面,假如日誌記錄器是可調試的,無論日誌記錄器是否可用,它將導致兩次計算消耗。這是一個無關緊要的費用(overhead),因爲評估(evaluating)一個日誌記錄器的消耗大約是日誌記錄器真正消耗的1%。

log4j裏面,日誌請求是由日誌記錄器實例處理的。日誌記錄器是一個類而不是一個接口。這就顯著地減少方法調用的消耗,當然這是以靈活性爲代價的(This measurably reduces the cost of method invocation at the cost of some flexibility)。

某些用戶利用預處理或者實時編譯技術編譯所有的日誌語句。這將導向優秀的性能效率而不影響日誌記錄。然而,由於應用程序二進制不包含任何日誌語句,日誌記錄不可能變成二進制(logging cannot be turned on for that binary)。我的觀點,這是一個不成比例的代價,爲獲得小性能的提高。

2.       當日志記錄開始啓動,決定是否記錄日誌的性能

跨越日誌記錄器層次的性能是潛在的。當日志記錄開始時,log4j將需要比較日誌請求和日誌請求處理器的級別。然而,日誌記錄器不可以有指定的級別;他們可以從日誌記錄器層次繼承級別。因此。在繼承一個級別前,日誌記錄器可能需要搜索它的祖先。

已經花費大量努力讓跨越這個層次儘可能的快。例如,子日誌記錄器僅僅連接到他們存在的祖先。在早先展示的BasicConfigurator實例中,名爲com.foo.Bar的日誌記錄器被直接連接到根日誌記錄器,因此迴避了com和com.foo的不存在。這顯著地提高了跨越地速度,特別是在“稀疏的”層次關係

跨越層次的典型消耗比日誌記錄被完全關閉的時候大約慢3倍。

3.       真正地輸出信息

這是格式化日誌輸出並把它發向目標的消耗。在這兒,大量的努力再次被花費在讓佈局器(格式化器)執行儘可能地快。這是和輸出源相同地。真正地記錄日誌的典型消耗大約從100到300微秒。參考org.apache.log4.performance.Logging獲得準確數字。

儘管log4j有許多特點,但是它的首要目標是速度。爲了提高性能,一些log4j組件已經被重寫了好多次了。然而,貢獻者還在不斷地提出新地優化。你應該很高興,當你知道使用SimpleLayout配置記錄日誌時,測試顯示log4j和System.out.println地速度一樣快。

結論

Log4j是一個用java寫的優秀日誌軟件包。它與衆不同地特色之一是繼承日誌記錄器概念。用用日誌記錄器層次,使按任意的粒度控制日誌語句輸出成爲可能。這有助於減少日誌輸出量,並最小化日誌消耗。

Log4j API的特色之一是它的易管理性。一旦日誌語句被插入代碼,他們可以用配置文件來控制。他們可以有選擇的激活或不激活。Log4j包被設計以便於日誌語句可以以漂碼(in shipped code)的方式保存,而不會致使大量的性能消耗。

感謝

非常感謝N. Asokan審閱了這篇文章。他logger概念的創立者之一。感謝Nelson Minar鼓勵我寫這片文章。他也對這篇文章提出了許多有用的建議和修正。Log4j是集體努力的結果。謝謝所有對這個項目作出貢獻的作者。毫無例外,在包中最好的特色已經在用戶社羣中廣泛創建了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章