二 動(dòng)態(tài)配置log4j 1 配置外部配置文件來配置的基本步驟 1.1 一個(gè)運(yùn)用配置文件的實(shí)例 Log4j之所以能成功的原因之一是它的靈活性。但如果只是簡單的調(diào)用BasicConfigurator.configure()來進(jìn)行配置工作,那么所有的配置都是在函數(shù)中寫死的,以后修改配置就要修改原代碼,這就不能體現(xiàn)出log4j的靈活性了,所以基本上不會(huì)通過BasicConfigurator.configure()來進(jìn)行配置工作的。 為了增加軟件的靈活性,最常用的做法就是使用配置文件,如web.xml之于J2EE,struts-config.xml之于struts一樣,log4j也提供了讓我們把配置信息從程序轉(zhuǎn)移到配置文件中的方法。Log4j提供了兩種方式的配置文件:XML文件和Java的property配置文件。通過把配置信息轉(zhuǎn)移到外部文件中,當(dāng)我們要修改配置信息時(shí),就可以直接修改配置文件而不用去修改代碼了,下面,我們就來完成一個(gè)通過配置文件來實(shí)現(xiàn)log4j的實(shí)例。 例2-a: package TestLog4j; import org.apache.log4j.Logger; import org.apache.log4j.BasicConfigurator; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.Priority; public class TestLog4j { static Logger logger = Logger.getLogger(TestLog4j.class.getName()); public TestLog4j(){}
public static void main(String[] args) { //通過BasicConfigurator類來初始化 //BasicConfigurator.configure(); //(1)通過配置文件來初始化 PropertyConfigurator.configure("F:\\nepalon\\log4j.properties");
logger.debug("Start of the main() in TestLog4j"); //代碼(2) logger.info("Just testing a log message with priority set to INFO"); logger.warn("Just testing a log message with priority set to WARN"); logger.error("Just testing a log message with priority set to ERROR"); logger.fatal("Just testing a log message with priority set to FATAL"); logger.log(Priority.WARN, "Testing a log message use a alternate form"); logger.debug(TestLog4j.class.getName()); //代碼(2) } } 在這個(gè)例子中,我們用PropertyConfigurator.configure("F:\\nepalon\\log4j.properties")代替BasicConfigurator.configure()進(jìn)行配置。PropertyConfigurator.configure()函數(shù)的參數(shù)可以是一個(gè)properties文件所在路徑的String對(duì)象,可以是一個(gè)properties文件所在路徑的URL對(duì)象,也可以是一個(gè)properties對(duì)象。通過PropertyConfigurator.configure()可以通過指定的properties文件來配置信息。如果要用XML文件進(jìn)行信息配置,可以在代碼中調(diào)用DOMConfigurator()函數(shù)來進(jìn)行配置工作。在這里,我們只以properties文件來完成例子。接著,我們來看一下log4j.properties文件中都有些什么東西: 例2-b: log4j.rootLogger = DEBUG, A1 log4j.appender.A1 = org.apache.log4j.ConsoleAppender log4j.appender.A1.layout = org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern = %-4r [%t] %-5p %c %x - %m%n 運(yùn)行這個(gè)實(shí)例,運(yùn)行結(jié)果為 0 [main] DEBUG TestLog4j.TestLog4j - Start of the main() in TestLog4j 20 [main] INFO TestLog4j.TestLog4j - Just testing a log message with priority set to INFO 20 [main] WARN TestLog4j.TestLog4j - Just testing a log message with priority set to WARN 20 [main] ERROR TestLog4j.TestLog4j - Just testing a log message with priority set to ERROR 20 [main] FATAL TestLog4j.TestLog4j - Just testing a log message with priority set to FATAL 180 [main] WARN TestLog4j.TestLog4j - Testing a log message use a alternate form 180 [main] DEBUG TestLog4j.TestLog4j - TestLog4j.TestLog4j 下面,我們分析一下這個(gè)配置文件。 1) 由于每一個(gè)Logger對(duì)旬都有一個(gè)級(jí)別,文件的第一行就是定義了一個(gè)Logger及其級(jí)別。在這里定義了一個(gè)根記錄器(root logger),這涉及到記錄器的層次問題,在些暫時(shí)不深入討論,在后面的章節(jié)再進(jìn)行討論。 2) 第二行定義了一個(gè)名為A1的輸出流,這個(gè)流就是控制臺(tái),所以通過Logger對(duì)象打印的信息會(huì)在控制臺(tái)輸出。 3) 第三行定義了打印信息的布局。在這里我們用PatternLayout作為此記錄器的布局,PatternLayout允許你以靈活的格式來打印信息。 4) 第四行指定的打印信息的具體格式,從結(jié)果可知,這個(gè)實(shí)例的打印格式為:當(dāng)前打印語句所使用的時(shí)間 [日志所在的線程] 打印的級(jí)別 當(dāng)前日志所在的類的全名 日志信息。 現(xiàn)在我們來修改一下這個(gè)記錄器的級(jí)別,把第一行的DEBUG改為INFO,再運(yùn)行程序,結(jié)果將變?yōu)椋?BR>0 [main] INFO TestLog4j.TestLog4j - Just testing a log message with priority set to INFO 10 [main] WARN TestLog4j.TestLog4j - Just testing a log message with priority set to WARN 10 [main] ERROR TestLog4j.TestLog4j - Just testing a log message with priority set to ERROR 10 [main] FATAL TestLog4j.TestLog4j - Just testing a log message with priority set to FATAL 10 [main] WARN TestLog4j.TestLog4j - Testing a log message use a alternate form 由于這個(gè)Logger的級(jí)別變?yōu)镮NFO,而代碼(2)是調(diào)用debug()函數(shù)來輸出日志信息時(shí)只能當(dāng)記錄器級(jí)別為DEBUG時(shí)才輸出信息,所以代碼(2)將不輸出信息。 1.2 實(shí)例原理 1.2.1 初始化配置信息 如果要通過JAVA的properties文件來配置信息,那么在代碼中就要通過PropertyConfigurator.configure()函數(shù)從properties文件中加載配置信息,這個(gè)函數(shù)有三種參數(shù)形式:一個(gè)properties文件所在路徑的String對(duì)象,可以是一個(gè)properties文件所在路徑的URL對(duì)象,也可以是一個(gè)properties對(duì)象。如果要用XML文件來配置信息,則可用類型的 DOMConfigurator()函數(shù)來從一個(gè)XML文件中加載配置信息。 1.2.2 輸出端Appender 在上面的例子中,我們都是簡單的把日志信息輸出到控制臺(tái)中。其實(shí)在log4j中還可以把日志信息輸出到其它的輸出端,對(duì)于同一個(gè)日志信息,我們還可以讓它同時(shí)輸出到多個(gè)輸出端中,如同時(shí)在控制臺(tái)和文件中進(jìn)行打印。一個(gè)輸出端就是一個(gè)appender。要在配置文件中定義一個(gè)appender有三步: 1) 在定義一個(gè)記錄器的同時(shí)定義出該記錄器的輸出端appender。在例2的配置文件的第一句log4j.rootLogger = DEBUG, A1中,我們定義了一個(gè)根記錄器,它的級(jí)別為DEBUG,它有一個(gè)appender名為A1。定義根記錄器的格式為log4j.rootLogger = [ level ], appendName1, appendName2, …appendNameN。同一個(gè)記錄器可有多個(gè)輸出端。 2) 定義appender的輸出目的地。定義一個(gè)appender的輸出目的地的格式為log4j.appender.appenderName = fully.qualified.name.of.appender.class。log4j提供了以下幾種常用的輸出目的地: org.apache.log4j.ConsoleAppender,將日志信息輸出到控制臺(tái) org.apache.log4j.FileAppender,將日志信息輸出到一個(gè)文件 org.apache.log4j.DailyRollingFileAppender,將日志信息輸出到一個(gè),并且每天輸出到一個(gè)新的日志文件 org.apache.log4j.RollingFileAppender,將日志信息輸出到一個(gè)文件,通過指定文件的的尺寸,當(dāng)文件大小到達(dá)指定尺寸的時(shí)候會(huì)自動(dòng)把文件改名,如名為example.log的文件會(huì)改名為example.log.1,同時(shí)產(chǎn)生一個(gè)新的example.log文件。如果新的文件再次達(dá)到指定尺寸,又會(huì)自動(dòng)把文件改名為example.log.2,同時(shí)產(chǎn)生一個(gè)example.log文件。依此類推,直到example.log. MaxBackupIndex,MaxBackupIndex的值可在配置文件中定義。 org.apache.log4j.WriterAppender,將日志信息以流格式發(fā)送到任意指定的地方。 org.apache.log4j.jdbc.JDBCAppender,通過JDBC把日志信息輸出到數(shù)據(jù)庫中。 在例2中,log4j.appender.A1 = org.apache.log4j.ConsoleAppender定義了名為A1的appender的輸出目的地為控制臺(tái),所以日志信息將輸出到控制臺(tái)。 3) 定義與所選的輸出目的地相關(guān)的參數(shù),定義格式為: log4j.appender.appenderName.optionName1 = value1 …… log4j.appender.appenderName.optionNameN = valueN 其中一個(gè)最常用的參數(shù)layout將在下面介紹。 1.2.3 輸出格式(布局)layout 通過appender可以控制輸出的目的地,而如果要控制輸出的格式,就可通過log4j的layout組件來實(shí)現(xiàn)。通過配置文件定義一個(gè)appender的輸出格式,也通常需要兩個(gè)步驟: 1) 定義appender的布局模式。定義一個(gè)appender的布局模式的格式為log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class。Log4j提供的布局模式有以下幾種: org.apache.log4j.HTMLLayout,以HTML表格形式布局 org.apache.log4j.PatternLayout,可以靈活地指定布局模式 org.apache.log4j.SimpleLayout,包含日志信息的級(jí)別和信息字符串 在例2 中l(wèi)og4j.appender.A1.layout = org.apache.log4j.PatternLayout定義了名為A1的appender的布局模式為PatternLayout。 2) 定義與所選的布局模式相關(guān)的設(shè)置信息,定義格式為: log4j.appender.appenderName.layout.optionName1 = value1 …… log4j.appender.appenderName.layout.optionNameN = valueN 選擇了不同的布局模式可能會(huì)有不同的設(shè)置信息。實(shí)例2所選的布局模式PatternLayout的一個(gè)PatternLayout為ConversionPattern ,通過定義這個(gè)PatternLayout的值,我們可以指定輸出信息的輸出格式。在例2的配置文件中的定義如下log4j.appender.A1.layout.ConversionPattern = %-4r [%t] %-5p %c %x - %m%n。在下面,我們將介紹布局模式PatternLayout的參數(shù)ConversionPattern的各個(gè)值代表的含義。 1.2.4 ConversionPattern參數(shù)的格式含義 格式名 含義 %c 輸出日志信息所屬的類的全名 %d 輸出日志時(shí)間點(diǎn)的日期或時(shí)間,默認(rèn)格式為ISO8601,也可以在其后指定格式,比如:%d{yyy-MM-dd HH:mm:ss },輸出類似:2002-10-18- 22:10:28 %f 輸出日志信息所屬的類的類名 %l 輸出日志事件的發(fā)生位置,即輸出日志信息的語句處于它所在的類的第幾行 %m 輸出代碼中指定的信息,如log(message)中的message %n 輸出一個(gè)回車換行符,Windows平臺(tái)為“\r\n”,Unix平臺(tái)為“\n” %p 輸出優(yōu)先級(jí),即DEBUG,INFO,WARN,ERROR,F(xiàn)ATAL。如果是調(diào)用debug()輸出的,則為DEBUG,依此類推 %r 輸出自應(yīng)用啟動(dòng)到輸出該日志信息所耗費(fèi)的毫秒數(shù) %t 輸出產(chǎn)生該日志事件的線程名 1.3 定義多個(gè)輸出目的地的實(shí)例 從上面的實(shí)例原理中我們已經(jīng)知道,同一個(gè)日志信息可以同時(shí)輸出到多個(gè)輸出目的地,在這個(gè)例子中,我們將實(shí)現(xiàn)一個(gè)把日志信息同時(shí)輸出到控制器、一個(gè)文件中的實(shí)例和數(shù)據(jù)庫中。這個(gè)實(shí)例的Java代碼我們沿用例2中的代碼,我們只需修改配置文件即可。這也體現(xiàn)了log4j的靈活性。 例3-a: create table log4j( logID int primary key identity, message varchar(1024), priority varchar(10), milliseconds int, category varchar(256), thread varchar(100), NDC varchar(256), createDate datetime, location varchar(256), caller varchar(100), method varchar(100), filename varchar(100), line int ) 例3-b: #1 定義了兩個(gè)輸出端 log4j.rootLogger = INFO, A1, A2,A3
#2 定義A1輸出到控制器 log4j.appender.A1 = org.apache.log4j.ConsoleAppender #3 定義A1的布局模式為PatternLayout log4j.appender.A1.layout = org.apache.log4j.PatternLayout #4 定義A1的輸出格式 log4j.appender.A1.layout.ConversionPattern = %-4r [%t] %-5p %c - %m%n
#5 定義A2輸出到文件 log4j.appender.A2 = org.apache.log4j.RollingFileAppender #6 定義A2要輸出到哪一個(gè)文件 log4j.appender.A2.File = F:\\nepalon\\classes\\example3.log #7 定義A2的輸出文件的最大長度 log4j.appender.A2.MaxFileSize = 1KB #8 定義A2的備份文件數(shù) log4j.appender.A2.MaxBackupIndex = 3 #9 定義A2的布局模式為PatternLayout log4j.appender.A2.layout = org.apache.log4j.PatternLayout #10 定義A2的輸出格式 log4j.appender.A2.layout.ConversionPattern = %d{yyyy-MM-dd hh:mm:ss}:%p %t %c - %m%n
#11區(qū) 定義A3輸出到數(shù)據(jù)庫 log4j.appender.A3 = org.apache.log4j.jdbc.JDBCAppender log4j.appender.A3.BufferSize = 40 log4j.appender.A3.Driver = com.microsoft.jdbc.sqlserver.SQLServerDriver log4j.appender.A3.URL = jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=nepalon log4j.appender.A3.User = sa log4j.appender.A3.Password = log4j.appender.A3.layout = org.apache.log4j.PatternLayout log4j.appender.A3.layout.ConversionPattern = INSERT INTO log4j (createDate, thread, priority, category, message) values(getdate(), ‘%t‘, ‘%-5p‘, ‘%c‘, ‘%m‘) 配置文件中的6、7、8行顯示了輸出端為RollingFileAppender的特有參數(shù)及其運(yùn)用的方法。11區(qū)顯示了輸出端為JDBCAppender的特有參數(shù)及其運(yùn)用方法。在這著重講解一下6、7、8行的作用。6行指定日志信息輸出到哪個(gè)文件,7行指定日志文件的最大長度,最后要詳細(xì)介紹8行。第8行的參數(shù)是設(shè)置備份文件的個(gè)數(shù)的參數(shù),在這里我們?cè)O(shè)置為3,表示最多有3個(gè)備份文件,具體作用為: 1) 當(dāng)example3.log文件的大小超過K時(shí),就把文件改名為example3.log.1,同時(shí)生成一個(gè)新的example3.log文件 2) 當(dāng)example3.log文件的大小再次超過1K,又把文件改名為example3.log.1。但由于此時(shí)example3.log.1已存在,則先把example3.log.1更名為example3.log.2,再把example3.log文件改名為example3.log.1 3) 同理,當(dāng)example3.log文件的大小再次超過1K,先把example3.log.2文件更名為example3.log.3,把example3.log.1文件更名為example3.log.2,再把example3.log文件改名為example3.log.1 4) 當(dāng)example3.log文件的大小再次超過1K,先把example3.log.2文件更名為example3.log.3,舊的example3.log.3文件將被覆蓋;把example3.log.1文件更名為example3.log.2,舊的example3.log.2文件被覆蓋;最后把example3.log文件改名為example3.log.1并覆蓋掉舊的example3.log.1文件。 運(yùn)行結(jié)果將分為兩部分 在控制器中: 0 [main] INFO TestLog4j.TestLog4j - Just testing a log message with priority set to INFO 11 [main] WARN TestLog4j.TestLog4j - Just testing a log message with priority set to WARN 21 [main] ERROR TestLog4j.TestLog4j - Just testing a log message with priority set to ERROR 21 [main] FATAL TestLog4j.TestLog4j - Just testing a log message with priority set to FATAL 21 [main] WARN TestLog4j.TestLog4j - Testing a log message use a alternate form 在文件example3.log中: 2003-12-18 04:23:02:INFO main TestLog4j.TestLog4j - Just testing a log message with priority set to INFO 2003-12-18 04:23:02:WARN main TestLog4j.TestLog4j - Just testing a log message with priority set to WARN 2003-12-18 04:23:02:ERROR main TestLog4j.TestLog4j - Just testing a log message with priority set to ERROR 2003-12-18 04:23:02:FATAL main TestLog4j.TestLog4j - Just testing a log message with priority set to FATAL 2003-12-18 04:23:02:WARN main TestLog4j.TestLog4j - Testing a log message use a alternate form 1.4 配置log4j的總結(jié) 這個(gè)教程到這里,關(guān)于配置log4j的配置文件的基本原理已經(jīng)講完了,而且通過例3我們已經(jīng)可以完成基本的日志工作了?,F(xiàn)在,我們就做一個(gè)總結(jié)。配置一個(gè)配置文件的基本步驟如下: 1) 定義一個(gè)Logger。在定義Logger時(shí)指定該Logger的級(jí)別級(jí)其輸出目的地。定義Logger的格式為 log4j.rootLogger = [ level ], appendName1, appendName2, …appendNameN。 2) 定義appender的輸出目的地。定義一個(gè)appender的輸出目的地的格式為 log4j.appender.appenderName = fully.qualified.name.of.appender.class。 log4j提供的輸出端有ConsoleAppender、FileAppender 、DailyRollingFileAppender、RollingFileAppender和WriterAppender。 3) 定義appender的除布局模式外的其它相關(guān)參數(shù),如例3中第6、7、8定義了A2的相關(guān)參數(shù)。定義格式為 log4j.appender.appenderName.optionName1 = value1 …… log4j.appender.appenderName.optionNameN = valueN 如果除了布局模式外不需要定義別的參數(shù),可跳過這一步(如例3中的A1)。 4) 定義appender的布局模式。定義一個(gè)appender的布局模式的格式為 log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class。 布局模式其實(shí)也是步驟3)中的一個(gè)部分,只是布局模式參數(shù)是每一個(gè)appender必須定義的參數(shù)。Log4j提供的布局模式有HTMLLayout、PatternLayout和SimpleLayout。 5) 定義與所選的布局模式相關(guān)的設(shè)置信息,定義格式為 og4j.appender.appenderName.layout.optionName1 = value1 …… log4j.appender.appenderName.layout.optionNameN = valueN 2 記錄器的層次Logger hierarchy 2.1 何為記錄器的層次hierarchy 首先,我們先看一下何為層次,以我們最熟悉的繼承為例,下面是一張類圖
在這個(gè)繼承體系中,類B是類C的父類,類A是類C的祖先類,類D是類C的子類。這些類之間就構(gòu)成一種層次關(guān)系。在這些具有層次關(guān)系的類中,子類都可繼承它的父類的特征,如類B的對(duì)象能調(diào)用類A中的非private實(shí)例變量和函數(shù);而類C由于繼承自類B,所以類B的對(duì)象可以同時(shí)調(diào)用類A和類B中的非private實(shí)例變量和函數(shù)。 在log4j中,處于不同層次中的Logger也具有象類這樣的繼承關(guān)系。 2.2 記錄器的層次 如果一個(gè)應(yīng)用中包含了上千個(gè)類,那么也幾乎需要上千個(gè)Logger實(shí)例。如何對(duì)這上千個(gè)Logger實(shí)例進(jìn)行方便地配置,就是一個(gè)很重要的問題。Log4J采用了一種樹狀的繼承層次巧妙地解決了這個(gè)問題。在Log4J中Logger是具有層次關(guān)系的。它有一個(gè)共同的根,位于最上層,其它Logger遵循類似包的層次。下面我們將進(jìn)行介紹。 2.2.1 根記錄器root logger 就象一個(gè)Java中的Object類一樣,log4j中的logger層次中有一個(gè)稱之為根記錄器的記錄器,其它所有的記錄器都繼承自這個(gè)根記錄器。根記錄器有兩個(gè)特征: 1) 根記錄器總是存在。就像Java中的Object類一樣,因?yàn)橛胠og4j輸出日志信息是通過記錄器來實(shí)現(xiàn)的,所以只要你應(yīng)用了log4j,根記錄器就肯定存在的。 2) 根記錄器沒有名稱,所以不能通過名稱來取得根記錄器。但在Logger類中提供了getRootLogger()的方法來取得根記錄器。 2.2.2 記錄器的層次 Logger遵循類似包的層次。如 static Logger rootLog = Logger.getRootLogger(); static Logger log1 = Logger.getLogger("test4j"); static Logger log2 = Logger.getLogger("test4j.test4j2"); static Logger log3 = Logger.getLogger("test4j.test4j2.test4j2"); 那么rootLog是log2的祖先子記錄器,log1是log2的父子記錄器,log3是log2的子記錄器。記錄器象Java中的類繼承一樣,子記錄器可以繼承父記錄器的設(shè)置信息,也可以可以覆寫相應(yīng)的信息。 首先先看一下記錄器層次中的繼承有什么用處。假設(shè)程序中的每個(gè)包都具有一些基本的日志信息,而包中的不同包可能會(huì)有些額外的日志信息要輸出,這種情況就可以象處理Java中的類一樣,運(yùn)用Logger中的層次關(guān)系來達(dá)到目的。假設(shè)有個(gè)名為A的包,我包下的所有類都要把日志信息輸出到控制臺(tái)中;A.B包除了輸出到控制臺(tái)外還要輸出到文件中;A.C包除了輸出到控制臺(tái)中還要輸出到HTML文檔中。這樣我們就可以通過定義一個(gè)父記錄器A,它負(fù)責(zé)把日志信息輸出到控制臺(tái)中;定義一個(gè)A的子記錄器A.B,它負(fù)責(zé)把日志信息輸出到文件中;定義一個(gè)A的子記錄器A.C,它負(fù)責(zé)把日志信息輸出到HTML文檔中。 記錄器遵循的是類似包的層次,這樣做為我們帶來了大大的方便。Logger類中的getLogger()方法可以取得Logger對(duì)象,這個(gè)方法有三種參數(shù)形式String、Class和URL,其實(shí)不論是用哪一種,最終都是通過記錄器的名字來取得記錄器對(duì)象的。如果要取得一個(gè)名為A.B的記錄器對(duì)象,我們可以Logger.getLogger(“A.B”)。但從上面的例子中,我們都是通過Logger.getLogger(TestLog4j.class.getName())這種方法來取得記錄器對(duì)象。這是為什么呢?現(xiàn)在我們假設(shè)A.B的包下有一個(gè)類BClass,那么我們調(diào)用BClass.class.getName()得到的是這個(gè)類的全名A.B.BClass。所以當(dāng)調(diào)用Logger.getLogger(BClass.class.getName())時(shí),最理想的情況是返回名為A.B.BClass的記錄器對(duì)象。但是如果不存在名為A.B.BClass的記錄器時(shí)它會(huì)怎樣呢?其實(shí)通過Logger類的getLogger方法取得記錄器時(shí)存在下面兩種情況: 1) 如果存在與所要找的名字完全相同的記錄器,則返回相應(yīng)的記錄器對(duì)象。 當(dāng)調(diào)用Logger.getLogger(BClass.class.getName())時(shí),如果定義了名為A.B.BClass的記錄器,它就返回該記錄器的對(duì)象。 2) 但如果找不到,它會(huì)嘗試返回在記錄器層次中與所要找的記錄器最接近的記錄器對(duì)象。 當(dāng)調(diào)用Logger.getLogger(BClass.class.getName())時(shí),如果沒有定義了名為A.B.BClass的記錄器,那會(huì)嘗試返回名為A.B的記錄器的對(duì)象;如果又沒有定義名為A.B的記錄器,它會(huì)嘗試返回名為A的記錄器的對(duì)象;如果也沒定義名為A的記錄器,它就會(huì)返回根記錄器的對(duì)象,而根記錄器是必須存在的,所以你總能得到一個(gè)記錄器對(duì)象。 好了,現(xiàn)在我們回到前面的問題,我們?yōu)槭裁纯傄ㄟ^Logger.getLogger(BClass.class.getName())這種以類全名作為參數(shù)來取得記錄器對(duì)象呢?其實(shí)這是為了管理方便。因?yàn)槲覀冊(cè)诙x設(shè)計(jì)Logger時(shí)也遵循類似包的規(guī)則,使設(shè)計(jì)器的名稱與程序中的類包對(duì)應(yīng)。如上面的假設(shè)中我們的程序中有A包,A包下有B包和C包,B包下又有類BClass,那么我們就可使設(shè)計(jì)器的名為A、A.B、A.C、A.B.BClass,以此類推。那么當(dāng)我們通過類命名來取得設(shè)計(jì)器對(duì)象時(shí),總能取到與所要的設(shè)計(jì)器最接近的設(shè)計(jì)器對(duì)象。 2.3 如何應(yīng)用記錄器的層次 2.3.1 如果定義及獲取不同層次的記錄器 任何一個(gè)記錄器的使用都有兩個(gè)步驟: 1) 在配置文件中定義相應(yīng)的記錄器。 在配置文件中定義記錄器的格式有兩種 定義根記錄器的格式為 log4j.rootLogger = [ level ], appendName1, appendName2, …appendNameN 定義一個(gè)非根記錄器的格式為 log4j.logger.loggerName1 = [ level ], appendName1,…appendNameN …… log4j.logger.loggerNameM = [ level ], appendName1, …appendNameN 我們可以定義任意個(gè)非根記錄器。 2) 在代碼中調(diào)用Logger類的取得記錄器方法取得相應(yīng)的記錄器對(duì)象。 要取得根記錄器對(duì)象可通過Logger.getRootLogger()函數(shù),要取得非根記錄器可通過Logger.getLogger()函數(shù)。 理論知道就講到這里,紙上得來終覺淺,下面,我們來小小演練一下。 例4-a: package TestLog4j; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.Priority; import TestLog4j.TestLog4j2.TestLog4j2;
public class TestLog4j { static Logger logger = Logger.getLogger(TestLog4j.class.getName()); //(2) public TestLog4j(){}
public static void main(String[] args) { //同時(shí)輸出到控制臺(tái)和一個(gè)文件的實(shí)例并實(shí)現(xiàn)了Logger的繼承 PropertyConfigurator.configure("F:\\nepalon\\log4j2.properties");
logger.debug("Start of the main() in TestLog4j"); logger.info("Just testing a log message with priority set to INFO"); logger.warn("Just testing a log message with priority set to WARN"); logger.error("Just testing a log message with priority set to ERROR"); logger.fatal("Just testing a log message with priority set to FATAL"); logger.log(Priority.WARN, "Testing a log message use a alternate form"); logger.debug(TestLog4j.class.getName()); TestLog4j2 testLog4j2 = new TestLog4j2(); //(1) testLog4j2.testLog(); } } 在類TestLog4j中我們調(diào)用了另一個(gè)類TestLog4j2,下面看一下類TestLog4j2的代碼。 例4-b: package TestLog4j.TestLog4j2; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.Priority;
public class TestLog4j2 { static Logger logger = Logger.getLogger(TestLog4j2.class.getName()); //(1) public TestLog4j2(){}
public void testLog() { //同時(shí)輸出到控制臺(tái)和一個(gè)文件的實(shí)例 PropertyConfigurator.configure("F:\\nepalon\\log4j2.properties"); logger.debug("2Start of the main()"); logger.info("2Just testing a log message with priority set to INFO"); logger.warn("2Just testing a log message with priority set to WARN"); logger.error("2Just testing a log message with priority set to ERROR"); logger.fatal("2Just testing a log message with priority set to FATAL"); logger.log(Priority.DEBUG, "Testing a log message use a alternate form"); logger.debug("2End of the main()"); } } 最后我們來看一下配置文件。 例4-c: log4j2.properties文件內(nèi)容 #1區(qū) #### Use two appenders, one to log to console, another to log to a file log4j.rootLogger = debug, stdout
#2區(qū) #Print only messages of priority WARN or higher for your category log4j.logger.TestLog4j= , R log4j.logger.TestLog4j.TestLog4j2=WARN
#3區(qū) #### First appender writes to console 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
#4區(qū) #### Second appender writes to a file log4j.appender.R=org.apache.log4j.RollingFileAppender log4j.appender.R.File=F:\\nepalon\\classes\\TestLog4j\\example.log
# Control the maximum log file size log4j.appender.R.MaxFileSize=100KB # Archive log files (one backup file here) log4j.appender.R.MaxBackupIndex=1
log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd hh:mm:ss}:%p %t %c - %m%n 先看一下運(yùn)行結(jié)果。 在控制臺(tái)中的結(jié)果為: DEBUG [main] (?:?) - Start of the main() in TestLog4j INFO [main] (?:?) - Just testing a log message with priority set to INFO WARN [main] (?:?) - Just testing a log message with priority set to WARN ERROR [main] (?:?) - Just testing a log message with priority set to ERROR FATAL [main] (?:?) - Just testing a log message with priority set to FATAL WARN [main] (?:?) - Testing a log message use a alternate form DEBUG [main] (?:?) - TestLog4j.TestLog4j WARN [main] (?:?) - 2Just testing a log message with priority set to WARN ERROR [main] (?:?) - 2Just testing a log message with priority set to ERROR FATAL [main] (?:?) - 2Just testing a log message with priority set to FATAL 輸出文件的結(jié)果為: 2003-12-19 04:19:44:DEBUG main TestLog4j.TestLog4j - Start of the main() in TestLog4j 2003-12-19 04:19:44:INFO main TestLog4j.TestLog4j - Just testing a log message with priority set to INFO 2003-12-19 04:19:44:WARN main TestLog4j.TestLog4j - Just testing a log message with priority set to WARN 2003-12-19 04:19:44:ERROR main TestLog4j.TestLog4j - Just testing a log message with priority set to ERROR 2003-12-19 04:19:44:FATAL main TestLog4j.TestLog4j - Just testing a log message with priority set to FATAL 2003-12-19 04:19:44:WARN main TestLog4j.TestLog4j - Testing a log message use a alternate form 2003-12-19 04:19:44:DEBUG main TestLog4j.TestLog4j - TestLog4j.TestLog4j 2003-12-19 04:19:44:WARN main TestLog4j.TestLog4j2.TestLog4j2 - 2Just testing a log message with priority set to WARN 2003-12-19 04:19:44:ERROR main TestLog4j.TestLog4j2.TestLog4j2 - 2Just testing a log message with priority set to ERROR 2003-12-19 04:19:44:FATAL main TestLog4j.TestLog4j2.TestLog4j2 - 2Just testing a log message with priority set to FATAL
首先,先來看一下配置文件都有些什么東西。 1) 在1區(qū)中定義了一個(gè)根記錄器。這個(gè)根記錄器具有DEBUG級(jí)別并有一個(gè)名稱為stdout的輸出端appender。 2) 2區(qū)中的內(nèi)容是這一節(jié)的重點(diǎn),也是應(yīng)用到記錄器層次的地方,但其實(shí)也只有兩句,充分體現(xiàn)了log4j的簡單性。在這里,我們定義了兩個(gè)名稱分別為TestLog4j和TestLog4j.TestLog4j2設(shè)計(jì)器。 在定義TestLog4j記錄器時(shí)沒有指定級(jí)別,所以它的級(jí)別繼承自它的父記錄器,即要記錄器,所以它的級(jí)別也為DEBUG。在定義TestLog4j記錄器時(shí)又定義了一個(gè)名稱為R的輸出端,所以它的輸出端有兩個(gè),一個(gè)從根記錄器繼承而來的名為stdout的輸出端,另一個(gè)為在此定義的名為R的輸出端。在此需要注意的是,在定義記錄器時(shí)必須先定義記錄器的級(jí)別,然后才是記錄器的輸出端。如果只想定義輸出端而不定義級(jí)別,則雖然級(jí)別可以為空,但逗號(hào)分隔符不能省略。如定義TestLog4j記錄器的做法。 在定義TestLog4j.TestLog4j2記錄器時(shí)又指定了它的級(jí)別,由于一個(gè)記錄器的級(jí)別只能有一個(gè),所以新指定的級(jí)別將覆寫掉它的父記錄器的級(jí)別(這就象Java中的多態(tài))。我們沒有定義TestLog4j.TestLog4j2記錄器的輸出端,所以它的輸出端將從它的父記錄器中繼承而來。它的父記錄器為estLog4j記錄器,所以它和estLog4j記錄器一樣具有兩個(gè)名稱分別為stdout和R的輸出端。 3) 剩下的3區(qū)和4區(qū)分別設(shè)置了兩個(gè)輸出端的參數(shù)值。 接下來,回到我們的代碼,看一下是如何取得記錄器,在取記錄器時(shí)又發(fā)生了什么。 1) 例4-a中的代碼(2)中,語句Logger.getLogger()中的參數(shù)TestLog4j.class.getName()的值為TestLog4j. TestLog4j,所以此語句的結(jié)果是取得一個(gè)名為TestLog4j. TestLog4j的記錄器的對(duì)象。但在配置文件中并沒有定義這樣的記錄器,所以最終將返回與所需的名稱TestLog4j. TestLog4j最接近的記錄器對(duì)象,即名為TestLog4j的記錄器的對(duì)象。 2) 例4-b中的代碼(1)的原理與例4-a中的代碼(2)相似,期望取得的是名為TestLog4j.TestLog4j2. TestLog4j2的記錄器對(duì)象,但最終返回的是TestLog4j.TestLog4j2記錄器的對(duì)象。 三 log4j與J2EE的結(jié)合 到目前為止,這篇文章講的都是如何在application中應(yīng)用log4j,而Java現(xiàn)在的應(yīng)用主流是J2EE和J2ME。現(xiàn)在,我們來看一下要如何在J2EE開發(fā)中應(yīng)用log4j。其實(shí)在Web application中應(yīng)用log4j也很簡單,與在application中應(yīng)用log4j不同之處就是要在所有應(yīng)用log4j的代碼之前對(duì)log4j進(jìn)行初始化。所以,我們?cè)趙eb application中就要把log4j的初始化工作獨(dú)立出來,把它放在Servlet中。下面,我們看一個(gè)例子。 例5-a: 進(jìn)行初始化的Servlet: import org.apache.log4j.PropertyConfigurator; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
/** * log4j.jar的初始化類,參考web.xml */ public class Log4jInit extends HttpServlet { public void init() { //通過web.xml來動(dòng)態(tài)取得配置文件 String prefix = getServletContext().getRealPath("/"); String file = getInitParameter("log4j-init-file");
// 如果沒有給出相應(yīng)的配置文件,則不進(jìn)行初始化 if(file != null) { PropertyConfigurator.configure(prefix+file); //(1) } }
public void doGet(HttpServletRequest req, HttpServletResponse res) {} } 下面來看一下這個(gè)Servlet在web.xml中的定義。 例5-b: <servlet> <servlet-name>log4j-init</servlet-name> <servlet-class>TestLog4j.Log4jInit</servlet-class> <init-param> <param-name>log4j-init-file</param-name> <param-value>sort.properties</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> 因?yàn)閘og4j的初始化要在所有的log4j調(diào)用之前完成,所以在web.xml文件中,我們一定要把對(duì)應(yīng)的Servlet定義的load-on-startup應(yīng)設(shè)為1,以便在Web容器啟動(dòng)時(shí)即裝入該Servlet。 完成了這兩個(gè)步驟這后,我們就可以象在application開發(fā)中一樣在web application任何地方應(yīng)用log4j了。下面是在javabean中的應(yīng)用的一個(gè)例子。 例5-c: import org.apache.log4j.Logger;
public class InfoForm { static Logger logger = Logger.getLogger(InfoForm.class);
protected String title; protected String content;
public InfoForm() {}
public void setTitle(Object value) { logger.debug("nepalon:title = " + title); title = value; }
public String getTitle() { logger.debug("nepalon:title = " + title); return title; }
public void setContent(String value) { content = value; logger.debug("nepalon: content() = " + content); }
public String getContent() { logger.debug("nepalon: content = \n" + content); return content; } }
| |