国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
log4j manual

Copyright© 2000-2004 The Apache Software Foundation. 版權(quán)所有。Log4j軟件是在遵守Apache Software License 1.1版的條例下發(fā)行的,Apache Software License的復(fù)制件被包括在log4j發(fā)布的LICENSE.txt文件里。這個(gè)簡短手冊也借用了The complete log4j manual 里的一些內(nèi)容,The complete log4j manual包含最新的更為詳盡的信息。   The complete log4j manual

摘要

這個(gè)文檔資料描述了log4j API,它的獨(dú)特的特性和設(shè)計(jì)原理。Log4j是由許多作者共同參與的開放源代碼項(xiàng)目。它允許開發(fā)人員以任意的精細(xì)程度控制哪些日志說明被輸出。通過使用外部的配置文件,可以在運(yùn)行時(shí)配置它。最好的是,log4j 開發(fā)包很容易上手。注意,它也可能會使一些開發(fā)人員著迷。

簡 介

幾乎每個(gè)大的應(yīng)用程序都有它自己的日志和跟蹤程序的API。順應(yīng)這一規(guī)則,E.U. SEMPER項(xiàng)目組決定編寫它自己的程序跟蹤API(tracing API)。這開始于1996年早期。經(jīng)過無數(shù)的工作,更改和性能加強(qiáng),這個(gè)API終于成為一個(gè)十分受歡迎的Java日志軟件包,那就是log4j。這個(gè)軟件包的發(fā)行遵守open source動(dòng)議認(rèn)證的Apache Software License。最新的log4j版本包括全部的源代碼,類文件和文檔資料,可以在 http://logging.apache.org/log4j/找到它們。另外,log4j已經(jīng)被轉(zhuǎn)換成 C, C++, C#, Perl, Python, Ruby, 和 Eiffel 語言。

把log statements插入到你的代碼中是一種排錯(cuò)的低技能辦法。這也許是唯一的方法,因?yàn)榕佩e(cuò)工具并不總是可以被使用或者適用于你的程序。對于多線程的應(yīng)用程序和多數(shù)發(fā)行的應(yīng)用程序,通常就是這樣的情形。

經(jīng)驗(yàn)告訴我們logging是開發(fā)過程中重要的一環(huán)。它具有多種優(yōu)點(diǎn)。首先,它能精確地提供運(yùn)行時(shí)的上下文( context )。一旦在程序中加入了Log 代碼,它就能自動(dòng)的生成并輸出logging信息而不需要人為的干預(yù)。另外,log信息的輸出可以被保存到一個(gè)固定的地方,以備以后研究。除了在開發(fā)過程中發(fā)揮它的作用外,一個(gè)性能豐富的日志記錄軟件包能當(dāng)作一個(gè)審計(jì)工具(audit tool)使用。

Brian W. Kernighan 和 Rob Pike 在他們的"The Practice of Programming" 書中這樣寫到:  "The Practice of Programming"

作為個(gè)人的選擇,除了得到一大堆程序跟蹤信息或一兩個(gè)變量值以外,我們傾
向於不使用排錯(cuò)器。一個(gè)原因是在詳細(xì)而復(fù)雜的數(shù)據(jù)結(jié)構(gòu)和控制流程中很容易
迷失;我們發(fā)現(xiàn)認(rèn)真思考并在關(guān)鍵處加入自我檢查代碼和輸出指令,比起一步
步看程序要效率高。在日志說明里查找比在明智地放置自我檢查代碼后的輸出
里查找要費(fèi)時(shí)。而決定在哪里放置打印指令要比在日志說明里一步步找到關(guān)鍵
的代碼要省時(shí)間。更重要的是,自我檢查的排錯(cuò)指令和程序并存;而排錯(cuò)
sessions是暫時(shí)的。

Logging確實(shí)也有它的缺陷。它降低了程序運(yùn)行的速度。它太冗長,查看時(shí)很容易錯(cuò)過。為了減少這些負(fù)面影響,log4j 被設(shè)計(jì)得可靠,高效和靈活。因?yàn)椋涗浫罩竞苌偈且粋€(gè)應(yīng)用程序的主要焦點(diǎn),log4j API 盡量做到容易被理解和使用。

Loggers, Appenders and Layouts

Log4j 有三個(gè)主要組件: loggers , appenders layouts 。這三類組件一起應(yīng)用,可以讓開發(fā)人員能夠根據(jù)日志的類型和級別進(jìn)行記錄,并且能在程序運(yùn)行時(shí)控制log信息輸出的格式和往什么地方輸出信息。

Logger hierarchy

任何logging API 與簡單的System.out.println輸出調(diào)試信息方法比較,最主要的優(yōu)點(diǎn)在于它能夠關(guān)閉一些調(diào)試信息輸出而不影響其他人的調(diào)試。這種能力的實(shí)現(xiàn)是假設(shè)這些logging空間,也就是所有的可能發(fā)生的日志說明空間,可以根據(jù)程序開發(fā)人員選擇的標(biāo)準(zhǔn)進(jìn)行分類。這一觀察以前使得我們選擇了 category 作為這個(gè)軟件包的中心概念。但是,在log4j 1.2版本以后,Logger類取代了Category類。對于那些熟悉早先版本的log4j的開發(fā)人員來說,Logger類只不過是Category類的一個(gè)別名。

Loggers是被命名的實(shí)體。Logger的名字大小寫有區(qū)別(case-sensitive),并且它們遵守階層式的命名規(guī)則:

Named Hierarchy

如果一個(gè)logger 的名字后面跟著一個(gè)點(diǎn)號(dot),它就是點(diǎn)號(dot)后面的那個(gè)logger的前輩( ancestor ),是這個(gè)晚輩( descendant ) 的前綴。如果在它自己和這個(gè)晚輩之間沒有其它的前輩,它和這個(gè)晚輩之間就是 關(guān)系。

例如,叫做"com.foo"的logger是叫做 "com.foo.Bar"的logger的父輩 。同樣地,"java"是"java.util" 的父輩,是"java.util.Vector"的前輩。大多數(shù)開發(fā)人員都熟悉這種命名方法。  "com.foo"  "com.foo.Bar"  "java"  "java.util"  "java.util.Vector"

根(root)logger 位于logger 階層的最上層。它在兩個(gè)方面很特別:

  1. 它總是存在的,
  2. 不能通過使用它的名字直接得到它。

通過這個(gè)類的靜態(tài)方法Logger.getRootLogger得到它(指RootLogger)。所有其他的loggers是通過靜態(tài)方法Logger.getLogger來實(shí)例化并獲取的。這個(gè)方法Logger.getLogger把所想要的logger的名字作為參數(shù)。 Logger類的一些其它基本方法在下面列出:

package org.apache.log4j; 
public class Logger {

// Creation and retrieval methods:
public static Logger getRootLogger();
public static Logger getLogger(String name);

// printing methods:
public void debug(Object message);
public void info(Object message);
public void warn(Object message);
public void error(Object message);
public void fatal(Object message);

// generic printing method:
public void log(Level l, Object message);

}

Loggers 可以 被指派優(yōu)先級別。Level.html#DEBUG">DEBUG, INFO, WARN, ERROR 和FATAL這組級別在org.apache.log4j.Level類中有定義。你也 可以 通過Level類的子類去定義你自己的優(yōu)先級別,盡管我們不鼓勵(lì)你這樣做。在后面我們會講到一個(gè)更好的方法。

如果一個(gè)logger沒有被指定優(yōu)先級別,它將繼承最接近的祖先所被指定的優(yōu)先級別。下面是更多關(guān)于優(yōu)先級別的信息:

Level Inheritance

對于一個(gè)給定的logger C,它 繼承的級別 等于logger階層里,從C開始往root logger上去的第一個(gè)non-null級別。

要保證所有的loggers最終都繼承一個(gè)優(yōu)先級別,root logger總是有一個(gè)被指派的優(yōu)先級。

下面是具有各種指派優(yōu)先級別值的四個(gè)表格,以及根據(jù)上面的規(guī)則所得出的繼承優(yōu)先級別。

Logger
name(名稱)
指派
級別
繼承
級別
Proot Proot
X none Proot
X.Y none Proot
X.Y.Z none Proot
例子

在上面的示例1中,只有root logger被指派了級別。這個(gè)級別的值,Proot,被其它的loggers X, X.YX.Y.Z繼承了。

Logger
name(名稱)
指派
級別
繼承
級別
Proot Proot
X Px Px
X.Y Pxy Pxy
X.Y.Z Pxyz Pxyz
例子

在上面的示例2中,所有的loggers都有一個(gè)指派的級別值。不需要級別繼承。

Logger
name(名稱)
指派
級別
繼承
級別
Proot Proot
X Px Px
X.Y none Px
X.Y.Z Pxyz Pxyz
例子

在示例3中,loggers root, X 和 X.Y.Z 分別被指派級別值Proot, PxPxyz。Logger X.Y 從它的父輩X那里繼承它的級別值。

Logger
name(名稱)
指派
級別
繼承
級別
Proot Proot
X Px Px
X.Y none Px
X.Y.Z none Px
例子

在示例4中,loggers root和X 分別被指派級別值ProotPx。Logger X.YX.Y.Z繼承它們最接近的父輩X的被指派的級別值。

日志請求是通過調(diào)用一個(gè)日志實(shí)例的打印方法(之一)而產(chǎn)生的。這些打印方法是 info, warn, error, fatal 和 log。

根據(jù)定義,打印方法決定一個(gè)日志請求的級別。例如,如果c是一個(gè)日志實(shí)例,那么語句c.info("..") 就是級別為INFO的一個(gè)日志請求。  c.info("..")

只有一個(gè)日志請求(A logging request)的級別高于或等于它的logger級別的時(shí)候才 能夠被執(zhí)行 。否則,則被認(rèn)為這個(gè)日志請求 不能被執(zhí)行 。一個(gè)沒有被定義優(yōu)先級別的logger將從層次關(guān)系中的前輩那里繼承優(yōu)先級別。這個(gè)規(guī)則總結(jié)如下:

Basic Selection Rule

在一個(gè)級別為q(被指定的或繼承的)的logger里,一個(gè)級別為p的日志請求,只有在p >= q 時(shí)才能夠被執(zhí)行。

這個(gè)規(guī)則是log4j的核心。它假設(shè)級別是有先后順序的。對于標(biāo)準(zhǔn)的優(yōu)先級別來說,DEBUG < INFO < WARN < ERROR < FATAL。

這里是一個(gè)關(guān)于這個(gè)規(guī)則的例子:

   // get a logger instance named "com.foo"
Logger logger = Logger.getLogger( "com.foo" );

// Now set its level. Normally you do not need to set the
// level of a logger programmatically. This is usually done
// in configuration files.
logger .setLevel( Level.INFO );

Logger barlogger = Logger.getLogger( "com.foo.Bar" );

// This request is enabled, because WARN >= INFO .
logger. warn ("Low fuel level.");

// This request is disabled, because DEBUG < INFO .
logger. debug ("Starting search for nearest gas station.");

// The logger instance barlogger, named "com.foo.Bar",
// will inherit its level from the logger named
// "com.foo" Thus, the following request is enabled
// because INFO >= INFO .
barlogger. info ("Located nearest gas station.");

// This request is disabled, because DEBUG < INFO .
barlogger. debug ("Exiting gas station search");

以一樣的叁數(shù)名字調(diào)用getLogger方法,返回的reference總是指向完全相同的logger對象。

例如,在這里:

   Logger x = Logger.getLogger("wombat");
Logger y = Logger.getLogger("wombat");
x和y指向 完全 相同的logger對象。

因此,通過這種方式可以配置一個(gè)logger,而不需要傳遞references就能在其他地方得到相同的實(shí)例。在生物的父子關(guān)系中父母總是排放在孩子們前面, log4j loggers與此有相互矛盾的地方,那就是log4j loggers可以以任何順序被產(chǎn)生和配置。特別的是,一個(gè)"parent" logger 會找到并連接他的后代,即使他是在他們之后被定義。

Log4j環(huán)境通常是在程序被初始化的時(shí)候被配置的。最好的方式是通過閱讀一個(gè)配置文件去配置。我們會馬上討論到這方面的內(nèi)容。

Log4j使得通過軟件組件的名稱去定義loggers的名字很容易。這可以通過在每個(gè)類中靜態(tài)地instantiating一個(gè)logger,讓logger的名字與這個(gè)合格的java類文件名相同來完成。這是一種有用并且直觀的定義loggers的方式。因?yàn)槿罩镜妮敵鰩в挟a(chǎn)生它們的logger的名字,這種命名策略使我們能夠很方便地識別這些log信息的來源。不過,盡管這是通用的一種loggers命名策略,Log4j沒有限制怎樣對loggers進(jìn)行命名。開發(fā)程序員可以根據(jù)自己的喜好隨意定義 loggers。   software component

當(dāng)然,至今所知的最好的命名策略還是以它們所在的類的名稱來命名 loggers。

Appenders and Layouts

基于自身的logger選擇性地使用或不使用日志請求(logging requests )的能力僅僅整個(gè)Log4j能力的一部分。Log4j允許將log信息輸出到許多不同的輸出設(shè)備中。用log4j的語言來說,一個(gè)log信息輸出目的地就叫做一個(gè) appender 。目前,log4j 的appenders可以將log信息輸出到console,files,GUI components,remote socket servers, JMS,NT Event Loggers,和 remote UNIX Syslog daemons。它還可以同時(shí)將log信息輸出到多個(gè)輸出設(shè)備中。   NT Event Loggers

多個(gè)appenders可以和一個(gè)logger連接在一起。

使用addAppender方法把一個(gè)appender加入到給定的logger上。一個(gè)給定的 logger的每一個(gè)被允許的日志請求都會被傳遞給這個(gè)logger的所有appenders,以及階層中高級別的appenders。換句話說appenders是從logger階層中不斷添加地被繼承的。例如,一個(gè) console appender加給了root logger,那么,這個(gè)root logger所有被允許輸出的日志信息將被輸出到console。如果你又給一個(gè)名字為C的logger添加了一個(gè) file appender,那么C 以及C的子輩的所有被允許的日志信息將被同時(shí)輸出到 file appender console appender??梢酝ㄟ^把a(bǔ)dditivity flag設(shè)置為false來覆蓋這個(gè)默認(rèn)的行為從而使appender的繼承關(guān)系不再是添加性的。  Each enabled logging request for a given logger will be forwarded to all the appenders in that logger as well as the appenders higher in the hierarchy.  setting the additivity flag

支配appender添加性的規(guī)則總結(jié)如下:

Appender Additivity

Logger C的log輸出信息將被輸出到C的所有appenders和它的前輩的 appenders。這就是"appender additivity"的意思。

但是,如果logger C的前輩,比如說P,P的additivity flag被設(shè)置為 false,那 么,C的輸出信息將被輸出到C的所有appenders中去,以及它的前輩的——截 止在P那里,包括P在內(nèi)的,appenders中去,但是不會輸出到P的前輩的 appenders中去。

默認(rèn)情況下,Loggers的additivity flag設(shè)置為true。

下面的表格顯示一個(gè)示例:

Logger
name(名稱)
添加的
Appenders
Additivity
旗標(biāo)
輸出目標(biāo) 注釋
A1 not applicable A1 Root logger是無名的,但是可以通過Logger.getRootLogger() 來訪問。Root logger沒有附帶默認(rèn)的appender。
x A-x1, A-x2 true A1, A-x1, A-x2 "x" 和root logger里的Appenders。
x.y none true A1, A-x1, A-x2 "x" 和root logger里的Appenders。
x.y.z A-xyz1 true A1, A-x1, A-x2, A-xyz1 "x.y.z", "x" 和root logger里的Appenders。
安全 A-sec false A-sec 因?yàn)閍dditivity flag被設(shè)置為 false,所以沒有appender繼承積累。
security.access none true A-sec 因?yàn)?security" logger里的additivity flag被設(shè)置為false,所以僅僅只 有"security" logger的appenders。

通常,用戶不僅希望自己指定log信息的輸出目的地,而且,他們還希望指定 log信息的輸出格式。這可以通過和appender相關(guān)的 layout 實(shí)現(xiàn)。Layout負(fù)責(zé)根據(jù)用戶的需要去格式化log信息的輸出,而appender負(fù)責(zé)將一個(gè)格式化過的 log信息輸出到它的目的地。PatternLayout 是標(biāo)準(zhǔn)log4j發(fā)行包中的一部分,它讓用戶根據(jù)和C語言中的printf方法相似的轉(zhuǎn)換模式指定輸出格式。

例如,具有"%r [%t] %-5p %c - %m%n" 轉(zhuǎn)換格式的PatternLayout 將輸出以下的式樣:

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

第一個(gè)區(qū)域是從程序開始運(yùn)行到輸出日志信息所用的毫秒數(shù)。第二個(gè)區(qū)域是產(chǎn)生日志請求的線程。第三個(gè)區(qū)域是這個(gè)log語句的優(yōu)先級別。第四個(gè)區(qū)域是和日志請求相關(guān)聯(lián)的logger名字。在'-' 之后的文字是這個(gè)log信息的內(nèi)容。

同樣重要的是,log4j 將根據(jù)用戶指定的標(biāo)準(zhǔn)來表達(dá)log信息的內(nèi)容。例如,如果你經(jīng)常需要日志記錄Oranges,Oranges是你當(dāng)前項(xiàng)目中使用的一個(gè)對象類型,那么你可以注冊一個(gè)OrangeRenderer,這樣每當(dāng)需要日志記錄一個(gè) orange時(shí),OrangeRenderer就會被調(diào)用。

對象的表達(dá)遵照類階層(class hierarchy)形式。例如,假設(shè)oranges是 fruits,你注冊了一個(gè)FruitRenderer,那么,包括oranges在內(nèi)的所有的fruits 都將由FruitRenderer來表達(dá),除非你自己為orange注冊了一個(gè)特定的 OrangeRenderer。

Object renderers必須實(shí)施ObjectRenderer界面。

配 置

在程序代碼中插入這些日志請求需要相當(dāng)大的工作量。調(diào)查顯示,大約%4左右的代碼是logging。因此,即便是中等大小的應(yīng)用程序也需要在它們的代碼中至少包含有幾千行的log語句。就從這個(gè)數(shù)目來看,管理這些log語句而不用人工地去修改它們是十分重要的。

Log4j環(huán)境是完全能夠通過編程來配置的。但是使用配置文件去配置則更靈活。目前,Log4j的配置文件是以XML格式和JAVA properties (key=value) 格式編寫的。

假設(shè)我們有個(gè)叫MyApp的程序使用log4j,讓我們來看看這是怎樣做到的:

 import com.foo.Bar;

// Import log4j classes.
import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;


public class MyApp {

// Define a static logger variable so that it references the
// Logger instance named "MyApp".
static Logger logger = Logger.getLogger(MyApp.class);

public static void main(String[] args) {

// Set up a simple configuration that logs on the console.
BasicConfigurator.configure();

logger.info("Entering application.");
Bar bar = new Bar();
bar.doIt();
logger.info("Exiting application.");
}
}

MyApp類首先引入log4j的相關(guān)類,然后定義一個(gè)命名為MyApp的靜態(tài)logger變量,而這個(gè)名字恰好和MyApp的類名一樣。

MyApp類還使用了被定義在com.foo包中的Bar類:

 package com.foo;
import org.apache.log4j.Logger;

public class Bar {
static Logger logger = Logger.getLogger(Bar.class);

public void doIt() {
logger.debug("Did it again!");
}
}

通過調(diào)用BasicConfigurator.configure 方法產(chǎn)生一個(gè)相當(dāng)簡單的log4j的設(shè)置。這個(gè)方法將一個(gè) ConsoleAppender添加到root logger,從而讓log信息輸出到 console。通過把PatternLayout設(shè)置為 %-4r [%t] %-5p %c %x - %m%n來確定輸出格式。

注意,默認(rèn)的root logger被指派為Level.DEBUG。

MyApp的輸出是這樣的:

0    [main] INFO  MyApp  - Entering application.
36 [main] DEBUG com.foo.Bar - Did it again!
51 [main] INFO MyApp - Exiting application.

下面的圖形描繪了在調(diào)用BasicConfigurator.configure方法之后,MyApp的對象圖表。

 

注意,log4j 的子代loggers只和它們現(xiàn)有的前輩鏈接。在這里,名字叫 com.foo.Bar的logger直接和root logger鏈接,因此繞過了沒有被使用的com 或com.foo loggers。這樣極大地提高了log4j的性能并減少了內(nèi)存(memory)的使用。

通過調(diào)用BasicConfigurator.configure方法來配置MyApp類。其它的類只需要引入org.apache.log4j.Logger類,獲取它們想要使用的loggers,就可以輸出 log。

先前的例子總是輸出同樣的log信息。幸運(yùn)的是,很容易修改MyApp程序就可以在程序運(yùn)行時(shí)對log輸出進(jìn)行控制。下面是略加修改后的版本:

 import com.foo.Bar;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

public class MyApp {

static Logger logger = Logger.getLogger(MyApp.class.getName());

public static void main(String[] args) {


// BasicConfigurator replaced with PropertyConfigurator.
PropertyConfigurator.configure(args[0]);

logger.info("Entering application.");
Bar bar = new Bar();
bar.doIt();
logger.info("Exiting application.");
}
}

這個(gè)例子中MyApp指示PropertyConfigurator方法去解讀配置文件并設(shè)置相應(yīng)的logging 。

這里是一個(gè)配置文件的示例,這個(gè)配置文件產(chǎn)生和前面BasicConfigurator例子完全一樣的輸出結(jié)果:

# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, A1

# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

假設(shè)我們不再需要com.foo軟件包里任何組件的日志輸出,下面的配置文件展示了達(dá)到這一目的的一種可能的方法:

log4j.rootLogger=DEBUG, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout

# Print the date in ISO 8601 format
log4j.appender.A1.layout.ConversionPattern= %d [%t] %-5p %c - %m%n

# Print only messages of level WARN or above in the package com.foo.
log4j.logger.com.foo=WARN

由這個(gè)文件所配置的MyApp的日志輸出如下:

 2000-09-07 14:07:41,508  [main] INFO  MyApp - Entering application.
2000-09-07 14:07:41,529 [main] INFO MyApp - Exiting application.

因?yàn)閘ogger com.foo.Bar 沒有指定的優(yōu)先級別,它就從com.foo中繼承優(yōu)先級別,而com.foo的優(yōu)先級別在配置文件中被設(shè)置為WARN。 Bar.doIt 方法里的 log語句的級別為DEBUG,比WARN級別低。所以,doIt()方法的日志請求就被壓制住了。

這里是另一個(gè)使用多個(gè)appenders的配置文件。

log4j.rootLogger=debug,  stdout, R 

log4j.appender. stdout =org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

log4j.appender. R =org.apache.log4j.RollingFileAppender
log4j.appender.R.File=example.log

log4j.appender.R.MaxFileSize= 100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=1

log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n

調(diào)用以這個(gè)配置文件增強(qiáng)了的MyApp會把下列輸出信息輸出到控制臺(console)上。

 INFO [main]  (MyApp2.java:12)  - Entering application.
DEBUG [main] (Bar.java:8) - Doing it again!
INFO [main] (MyApp2.java:15) - Exiting application.

另外,當(dāng)root logger增加了第二個(gè)appender時(shí),log信息將同時(shí)也被輸出到 example.log文件中。當(dāng)example.log文件達(dá)到100KB 后,example.log文件將被rolled over。當(dāng)roll-over 發(fā)生時(shí),example.log 的老版本將自動(dòng)被移到 example.log.1中去。

注意,要獲得這些不同的logging行為并不需要重新編譯代碼。我們還可以簡單地通過修改log配置文件把log信息輸出到UNIX Syslog daemon中,把所有 com.foo的日志輸出轉(zhuǎn)指向NT Event logger 中,或者把log事件輸出到遠(yuǎn)程 log4j服務(wù)器中,當(dāng)然它要根據(jù)局部服務(wù)器規(guī)則進(jìn)行l(wèi)og,例如可以把log事件輸出到第二個(gè)log4j服務(wù)器中去。

默認(rèn)的初始化過程

Log4j庫沒有對它的環(huán)境作任何假設(shè)。特別是,沒有默認(rèn)的log4j appenders。不過在一些精細(xì)定義過的情況下,這個(gè)Logger類的靜態(tài)的initializer會試圖自動(dòng)配置log4j。 Java語言確保一個(gè)類的靜態(tài)的initializer在這個(gè)類被裝載到內(nèi)存里時(shí)被調(diào)用一次,而且僅僅一次。這點(diǎn)很重要,要記住不同的classloaders會裝載同一個(gè)類的不同復(fù)制版。這些同一個(gè)類的不同復(fù)制版在JVM看來是完全不相關(guān)的。

默認(rèn)的初始化在這樣的環(huán)境中很有用處,那就是同一個(gè)程序依據(jù)運(yùn)行時(shí)的環(huán)境作不同用處。例如,同樣一個(gè)程序可以在web-server的控制下作為單獨(dú)的程序,作為一個(gè)applet,或者作為一個(gè)servlet被使用。

默認(rèn)的初始化運(yùn)算法則定義如下:

  1. log4j.defaultInitOverride的系統(tǒng)屬性設(shè)置為 "false"以外的任何值將會造成 log4j跳過默認(rèn)的初始化過程。
  2. resource這個(gè)string變量設(shè)置為log4j.configuration系統(tǒng)屬性的值。 最好的方法指定默認(rèn)初始化文件是通過log4j.configuration系統(tǒng)屬性來指定。 在log4j.configuration系統(tǒng)屬性沒有被定義的情況下,把resource這個(gè)string變 量設(shè)置成它的默認(rèn)值"log4j.properties"。
  3. resource變量轉(zhuǎn)換為一個(gè)URL。
  4. 如果這個(gè)resource變量不能轉(zhuǎn)換為一個(gè)URL,例如,因?yàn)?MalformedURLException的緣故,那么就通過調(diào)用 org.apache.log4j.helpers.Loader.getResource(resource, Logger.class)在 classpath上搜尋resource,它會返回一個(gè)URL。注意, string "log4j.properties"是一個(gè)不合式的URL。  org.apache.log4j.helpers.Loader.getResource(resource, Logger.class)

    有關(guān)搜尋地址列單,請參看Loader.getResource(java.lang.String)。

  5. 如果不能找到URL,那就放棄默認(rèn)的初始化。否則,從URL配置log4j 。

    log4j.configuratorClass系統(tǒng)屬 性的值就是你客戶自己的configurator的類名。你指定的客戶configurator 必須 實(shí)施Configurator接口。

配置示例

Tomcat下默認(rèn)的初始化

默認(rèn)的log4j初始化在web-server環(huán)境中特別有用。在Tomcat 3.x and 4.x下,你應(yīng)該把log4j.properties放置在你的網(wǎng)絡(luò)程序的WEB-INF/classes目錄下面。 Log4j自己會去找到屬性文件并初始化。這樣做又簡單又有效。

你可以選擇在Tomcat啟動(dòng)之前設(shè)置系統(tǒng)屬性log4j.configuration 。對于 Tomcat 3.x ,TOMCAT_OPTS 環(huán)境變量被用來設(shè)置命令行選項(xiàng)。對于 Tomcat 4.0,使用CATALINA_OPTS環(huán)境變量而不是TOMCAT_OPTS 。

例子

Unix shell 命令

export TOMCAT_OPTS="-Dlog4j.configuration=foobar.txt"
告訴log4j 使用文件foobar.txt 作為默認(rèn)的配置文件。這個(gè)文件應(yīng)該被放置在你的網(wǎng)絡(luò)應(yīng)用程序的WEB-INF/classes目錄下面。文件將通過 PropertyConfigurator被讀取。每個(gè)網(wǎng)絡(luò)應(yīng)用程序使用不同的默認(rèn)配置文件,因?yàn)槊總€(gè)文件都是和每個(gè)網(wǎng)絡(luò)應(yīng)用程序相關(guān)的。

例子

Unix shell 命令

   export TOMCAT_OPTS="-Dlog4j.debug -Dlog4j.configuration=foobar.xml"
告訴log4j輸出log4j-內(nèi)部排錯(cuò)信息,并使用文件foobar.xml 作為默認(rèn)的配置文件。這個(gè)文件應(yīng)該被放置在你的網(wǎng)絡(luò)應(yīng)用程序的WEB-INF/classes目錄下面。因?yàn)槲募?xml擴(kuò)展符結(jié)尾,將使用DOMConfigurator來讀取。每個(gè)網(wǎng)絡(luò)應(yīng)用程序使用不同的默認(rèn)配置文件,因?yàn)槊總€(gè)文件都是和每個(gè)網(wǎng)絡(luò)應(yīng)用程序相關(guān)的。

例子

Windows shell 命令

   set TOMCAT_OPTS=-Dlog4j.configuration=foobar.lcf -Dlog4j.configuratorClass=com.foo.BarConfigurator
告訴log4j使用文件foobar.lcf 作為默認(rèn)的配置文件。這個(gè)文件應(yīng)該被放置在你的網(wǎng)絡(luò)應(yīng)用程序的WEB-INF/classes 目錄下面。根據(jù)log4j.configuratorClass 系統(tǒng)屬性的定義 ,文件將通過將使用客戶自己的configurator—— com.foo.BarConfigurator被讀取。每個(gè)網(wǎng)絡(luò)應(yīng)用程序使用不同的默認(rèn)配置文件,因?yàn)槊總€(gè)文件都是和一個(gè)網(wǎng)絡(luò)應(yīng)用程序相關(guān)的。

例子

Windows shell 命令

   set TOMCAT_OPTS=-Dlog4j.configuration=file:/c:/foobar.lcf
告訴log4j使用文件c:\foobar.lcf 作為默認(rèn)的配置文件。這個(gè)配置文件完全由 URL file:/c:/foobar.lcf指定。因此,這個(gè)相同的配置文件將被所有網(wǎng)絡(luò)應(yīng)用程序使用。  c:\foobar.lcf

不同的網(wǎng)絡(luò)應(yīng)用程序通過它們各自的classloaders裝載log4j的類。因此,每個(gè) log4j環(huán)境的image會獨(dú)自地,沒有任何相互協(xié)調(diào)地行動(dòng)。例如,在多個(gè)網(wǎng)絡(luò)應(yīng)用程序的配置中,FileAppenders若定義得完全相同,它們就會編寫相同的文件。這樣的結(jié)果就不那么令人滿意。你必須保證不同的網(wǎng)絡(luò)應(yīng)用程序的log4j配置不使用相同的系統(tǒng)資源。

初始化servlet

還可以使用一個(gè)特別的servlet來進(jìn)行l(wèi)og4j初始化。這里就是個(gè)示例:

package com.foo;

import org.apache.log4j.PropertyConfigurator;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.IOException;

public class Log4jInit extends HttpServlet {

public
void init() {
String prefix = getServletContext().getRealPath("/");
String file = getInitParameter("log4j-init-file");
// if the log4j-init-file is not set, then no point in trying
if(file != null) {
PropertyConfigurator.configure(prefix+file);
}
}

public
void doGet(HttpServletRequest req, HttpServletResponse res) {
}
}

在web.xml文件里為你的網(wǎng)絡(luò)應(yīng)用程序定義下面的servlet。

  <servlet>
<servlet-name>log4j-init</servlet-name>
<servlet-class>com.foo.Log4jInit</servlet-class>

<init-param>
<param-name>log4j-init-file</param-name>
<param-value>WEB-INF/classes/log4j.lcf</param-value>
</init-param>

<load-on-startup>1</load-on-startup>
</servlet>

編寫一個(gè)initialization servlet 是最靈活的方式來初始化log4j。不受任何限制,你可以在這個(gè)servlet的init()方法里放入任何代碼。

Nested Diagnostic Contexts

實(shí)際情況下的大多數(shù)系統(tǒng)都需要同時(shí)處理多個(gè)客戶端問題。在這種系統(tǒng)的典型的多線程實(shí)施中,通常是不同的線程去分別處理不同的客戶需求。Logging特別適合于復(fù)雜的程序跟蹤和排錯(cuò)。一個(gè)通常的處理辦法是通過給每個(gè)客戶產(chǎn)生一個(gè)新的分離開的logger來達(dá)到把不同的客戶的日志輸出信息區(qū)分開來。但這促進(jìn)了loggers的增殖,加大了logging的管理負(fù)擔(dān)。

一個(gè)更簡潔的技術(shù)是獨(dú)特地標(biāo)記來自于同一個(gè)客戶的每一個(gè)日志請求。Neil Harrison 在他的書中"Patterns for Logging Diagnostic Messages," in Pattern Languages of Program Design 3, edited by R. Martin, D. Riehle, and F. Buschmann (Addison-Wesley, 1997) 對這個(gè)方法進(jìn)行了描述。   Pattern Languages of Program Design 3

要獨(dú)特地標(biāo)記每個(gè)日志請求,用戶把上下文信息送入NDC,NDC是 Nested Diagnostic Context 的縮寫。NDC類展示如下。

  public class NDC {
// Used when printing the diagnostic
public static String get();

// Remove the top of the context from the NDC.
public static String pop();

// Add diagnostic context for the current thread.
public static void push(String message);

// Remove the diagnostic context for this thread.
public static void remove();
}

NDC類是作為一個(gè)保存線程上下文的 stack 來獨(dú)個(gè)線程(per thread) 管理的。注意,org.apache.log4j.NDC類中所有的方法都是靜態(tài)的。假設(shè)NDC打印功能被打開,每一次若有日志請求,相應(yīng)的log4j組件就把這個(gè)當(dāng)前線程的 整個(gè) NDC stack包括在日志輸出中打印出來。這樣做不需要用戶干預(yù),用戶只需要在代碼中明確指定的幾點(diǎn)通過pushpop方法將正確的信息放到NDC中就行了。相反,per-client logger方法需要在代碼中作很多更改。

為了說明這一點(diǎn),我們舉個(gè)有關(guān)一個(gè)servlet把信息內(nèi)容發(fā)送到多個(gè)客戶的例子。這個(gè)Servlet程序在開始接到客戶端的請求,執(zhí)行其它代碼之前,首先創(chuàng)建一個(gè)NDC。該上下文信息可能是客戶端的主機(jī)名,以及其他請求中固有的信息,通常是包含在cookies中的信息。因此即便這個(gè)Servlet程序可能同時(shí)要服務(wù)于多個(gè)客戶,由相同的代碼啟動(dòng)的這些logs,比如屬于同一個(gè)logger,它們?nèi)匀荒軌虮粎^(qū)分開來,因?yàn)椴煌目蛻舳苏埱缶哂胁煌腘DC stack。這與在客戶請求期間把一個(gè)實(shí)例化的logger傳遞給所有要被執(zhí)行的代碼的復(fù)雜性形成了反差。

然而,一些復(fù)雜的應(yīng)用程序,比如虛擬網(wǎng)絡(luò)服務(wù)器,必須依據(jù)虛擬主機(jī)的上下文語言環(huán)境,以及發(fā)布請求的軟體組件來作不同的log。最近的log4j發(fā)行版支持多階層樹。這一功能的加強(qiáng)允許每個(gè)虛擬主機(jī)擁有它自己的logger階層版本。

性能

一個(gè)經(jīng)常提出的爭議就是logging的運(yùn)算開銷。這種關(guān)注是有道理的,因?yàn)榧幢闶且粋€(gè)中等大小的應(yīng)用程序至少也會產(chǎn)生幾千個(gè)log輸出。許多工作都花費(fèi)在測量和改進(jìn)logging性能上。Log4j聲明它是快速和靈活的:速度第一,靈活性第二。

用戶需要清楚地了解下面這些與性能相關(guān)的問題:

  1. Logging performance when logging is turned off.

    當(dāng)logging被完全關(guān)閉或只是set of levels被關(guān)閉,日志請求的開銷是方法的調(diào) 用和整數(shù)的比較。在一個(gè)233 MHz Pentium II機(jī)器上,這種開銷通常在5 to 50 毫微秒范圍內(nèi)。  set of levels

    不過,方法的調(diào)用包含有參數(shù)的建造上的“隱閉”開銷。

    例如下面的logger cat程序段中:

         logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
    不管message被日志記錄與否,構(gòu)造message參數(shù)的開銷還是有的,比如說, 把整數(shù)i 和數(shù)組entry[i]轉(zhuǎn)化為String,連接中間字串。參數(shù)構(gòu)造的這種開銷可能 很高,它依賴于所介入的參數(shù)數(shù)量有多少。

    為了避免這種參數(shù)構(gòu)造開銷,把以上的代碼段改寫為:

          if(logger.isDebugEnabled() {
    logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
    }

    如果排錯(cuò)功能不被使用,就不會有參數(shù)構(gòu)造上的開銷。但是,另一方面,如果 logger的排錯(cuò)功能被起用,就會有倆倍的開銷用于評估logger是否被起用:一 次是判斷debugEnabled,一次是判斷debug是否被啟用。但這不是極重的負(fù) 擔(dān),因?yàn)樵u估logger的時(shí)間只有整個(gè)log語句執(zhí)行時(shí)間的1%

    在log4j中,把日志請求作為Logger類的實(shí)例。Logger是類而不是接口,這主 要是為了減少程序調(diào)用的開銷,但犧牲了接口所能帶來的靈活性。

    有些用戶使用預(yù)處理或compile-time技術(shù)來編譯所有l(wèi)og語句。這樣logging方面 的性能是很好。但是,因?yàn)閞esulting application binary沒有包含任何log語 句,你不能對這個(gè)二進(jìn)制程序起用logging。在我看來,這是為了小的性能增加 而付出大的代價(jià)。

  2. The performance of deciding whether to log or not to log when logging is turned on.

    本質(zhì)上影響性能的因素是logger的層次關(guān)系。當(dāng)logging功能被打開時(shí),log4j仍 然需要把log請求的級別去與request logger的級別作比較。不過,有些loggers 并沒有指派的優(yōu)先級別,但它可以從它的上一層logger那里繼承優(yōu)先級別。因 此在繼承優(yōu)先級之前,logger可能需要搜索它的ancestors。

    Log4j在這方面做了很大的努力,以便使這種階層的優(yōu)先級別搜尋(hierarchy walk )盡可能的快速。例如,子代loggers僅僅只和它們現(xiàn)有的ancestors鏈 接。在前面的BasicConfigurator示例中,叫做com.foo.Bar的logger 直接與 root logger鏈接,繞過了不存在的com或com.foo loggers。這極大地提高了優(yōu) 先級別搜尋的速度。

    階層的優(yōu)先級搜尋(walking the hierarchy )的開銷在于它比logging完全關(guān)閉 時(shí)要慢三倍。

  3. Actually outputting log messages

    這里講的是log輸出的格式化和把log信息發(fā)送到目標(biāo)所在地的開銷。Log4j在這 方面也下了大力氣讓格式化能盡快執(zhí)行。對appenders也是一樣。通常情況 下,格式化語句的開銷可能是100到300微秒的處理時(shí)間。確切數(shù)字請參看 org.apache.log4.performance.Logging 。

盡管log4j具有許多功能特性,但速度是第一設(shè)計(jì)目標(biāo)。為了提高性能,一些 log4j的部件曾經(jīng)被重寫過許多次。即使這樣,log4j的貢獻(xiàn)者們不斷提出新的優(yōu)化辦法。你應(yīng)該很驚喜地發(fā)現(xiàn)當(dāng)以SimpleLayout來配置時(shí),性能測試顯示使用 log4j日志和使用System.out.println日志同樣快。

結(jié)論

Log4j是用Java編寫的一個(gè)非常流行的logging開發(fā)包。它的一個(gè)顯著特性之一是在loggers里運(yùn)用了繼承的概念。使用這種logger的層次關(guān)系,就可能準(zhǔn)確地控制每一個(gè)log語句的輸出。這樣減少了log信息的輸出量并降低了logging的開銷。

Log4j API的優(yōu)點(diǎn)之一是它的可管理性。一旦log語句被插入到代碼中,他們就能被配置文件控制而無需重新編譯源代碼。Log信息的輸出能夠有選擇地被起用或關(guān)閉,用戶能夠按照自己選擇的格式將這些log信息輸出到許多不同的輸出設(shè)備中。Log4j軟件包的設(shè)計(jì)是在代碼中保留log語句的同時(shí)不造成很大的性能損失。

感謝

Many thanks to N. Asokan for reviewing the article. He is also one of the originators of the logger concept. I am indebted to Nelson Minar for encouraging me to write this article. He has also made many useful suggestions and corrections to this article. Log4j is the result of a collective effort. My special thanks go to all the authors who have contributed to the project. Without exception, the best features in the package have all originated in the user community.
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服