limq

          rainman
          隨筆 - 19, 文章 - 2, 評(píng)論 - 115, 引用 - 1
          數(shù)據(jù)加載中……

          log4j使用進(jìn)階

          二 動(dòng)態(tài)配置log4j
          1?配置外部配置文件來(lái)配置的基本步驟
          1.1 一個(gè)運(yùn)用配置文件的實(shí)例
          Log4j之所以能成功的原因之一是它的靈活性。但如果只是簡(jiǎn)單的調(diào)用BasicConfigurator.configure()來(lái)進(jìn)行配置工作,那么所有的配置都是在函數(shù)中寫(xiě)死的,以后修改配置就要修改原代碼,這就不能體現(xiàn)出log4j的靈活性了,所以基本上不會(huì)通過(guò)BasicConfigurator.configure()來(lái)進(jìn)行配置工作的。
          為了增加軟件的靈活性,最常用的做法就是使用配置文件,如web.xml之于J2EE,struts-config.xml之于struts一樣,log4j也提供了讓我們把配置信息從程序轉(zhuǎn)移到配置文件中的方法。Log4j提供了兩種方式的配置文件:XML文件和Java的property配置文件。通過(guò)把配置信息轉(zhuǎn)移到外部文件中,當(dāng)我們要修改配置信息時(shí),就可以直接修改配置文件而不用去修改代碼了,下面,我們就來(lái)完成一個(gè)通過(guò)配置文件來(lái)實(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)
          {
          //通過(guò)BasicConfigurator類來(lái)初始化
          //BasicConfigurator.configure();
          //(1)通過(guò)配置文件來(lái)初始化
          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ì)象。通過(guò)PropertyConfigurator.configure()可以通過(guò)指定的properties文件來(lái)配置信息。如果要用XML文件進(jìn)行信息配置,可以在代碼中調(diào)用DOMConfigurator()函數(shù)來(lái)進(jìn)行配置工作。在這里,我們只以properties文件來(lái)完成例子。接著,我們來(lái)看一下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),這涉及到記錄器的層次問(wèn)題,在些暫時(shí)不深入討論,在后面的章節(jié)再進(jìn)行討論。
          2)?第二行定義了一個(gè)名為A1的輸出流,這個(gè)流就是控制臺(tái),所以通過(guò)Logger對(duì)象打印的信息會(huì)在控制臺(tái)輸出。
          3)?第三行定義了打印信息的布局。在這里我們用PatternLayout作為此記錄器的布局,PatternLayout允許你以靈活的格式來(lái)打印信息。
          4)?第四行指定的打印信息的具體格式,從結(jié)果可知,這個(gè)實(shí)例的打印格式為:當(dāng)前打印語(yǔ)句所使用的時(shí)間?[日志所在的線程]?打印的級(jí)別?當(dāng)前日志所在的類的全名?日志信息。
          現(xiàn)在我們來(lái)修改一下這個(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ù)來(lái)輸出日志信息時(shí)只能當(dāng)記錄器級(jí)別為DEBUG時(shí)才輸出信息,所以代碼(2)將不輸出信息。
          1.2 實(shí)例原理
          1.2.1 初始化配置信息
          如果要通過(guò)JAVA的properties文件來(lái)配置信息,那么在代碼中就要通過(guò)PropertyConfigurator.configure()函數(shù)從properties文件中加載配置信息,這個(gè)函數(shù)有三種參數(shù)形式:一個(gè)properties文件所在路徑的String對(duì)象,可以是一個(gè)properties文件所在路徑的URL對(duì)象,也可以是一個(gè)properties對(duì)象。如果要用XML文件來(lái)配置信息,則可用類型的
          DOMConfigurator()函數(shù)來(lái)從一個(gè)XML文件中加載配置信息。
          1.2.2 輸出端Appender
          在上面的例子中,我們都是簡(jiǎn)單的把日志信息輸出到控制臺(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è)文件,通過(guò)指定文件的的尺寸,當(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,通過(guò)JDBC把日志信息輸出到數(shù)據(jù)庫(kù)中。
          在例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
          通過(guò)appender可以控制輸出的目的地,而如果要控制輸出的格式,就可通過(guò)log4j的layout組件來(lái)實(shí)現(xiàn)。通過(guò)配置文件定義一個(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?,通過(guò)定義這個(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ā)生位置,即輸出日志信息的語(yǔ)句處于它所在的類的第幾行
          %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ù)庫(kù)中。這個(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的輸出文件的最大長(zhǎng)度
          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ù)庫(kù)
          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行指定日志文件的最大長(zhǎng)度,最后要詳細(xì)介紹8行。第8行的參數(shù)是設(shè)置備份文件的個(gè)數(shù)的參數(shù),在這里我們?cè)O(shè)置為3,表示最多有3個(gè)備份文件,具體作用為:
          1)?當(dāng)example3.log文件的大小超過(guò)K時(shí),就把文件改名為example3.log.1,同時(shí)生成一個(gè)新的example3.log文件
          2)?當(dāng)example3.log文件的大小再次超過(guò)1K,又把文件改名為example3.log.1。但由于此時(shí)example3.log.1已存在,則先把example3.log.1更名為example3.log.2,再把example3.log文件改名為example3.log.1
          3)?同理,當(dāng)example3.log文件的大小再次超過(guò)1K,先把example3.log.2文件更名為example3.log.3,把example3.log.1文件更名為example3.log.2,再把example3.log文件改名為example3.log.1
          4)?當(dāng)example3.log文件的大小再次超過(guò)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)講完了,而且通過(guò)例3我們已經(jīng)可以完成基本的日志工作了。現(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ù),可跳過(guò)這一步(如例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è)很重要的問(wèn)題。Log4J采用了一種樹(shù)狀的繼承層次巧妙地解決了這個(gè)問(wèn)題。在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輸出日志信息是通過(guò)記錄器來(lái)實(shí)現(xiàn)的,所以只要你應(yīng)用了log4j,根記錄器就肯定存在的。
          2)?根記錄器沒(méi)有名稱,所以不能通過(guò)名稱來(lái)取得根記錄器。但在Logger類中提供了getRootLogger()的方法來(lái)取得根記錄器。
          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è)置信息,也可以可以覆寫(xiě)相應(yīng)的信息。
          首先先看一下記錄器層次中的繼承有什么用處。假設(shè)程序中的每個(gè)包都具有一些基本的日志信息,而包中的不同包可能會(huì)有些額外的日志信息要輸出,這種情況就可以象處理Java中的類一樣,運(yùn)用Logger中的層次關(guān)系來(lái)達(dá)到目的。假設(shè)有個(gè)名為A的包,我包下的所有類都要把日志信息輸出到控制臺(tái)中;A.B包除了輸出到控制臺(tái)外還要輸出到文件中;A.C包除了輸出到控制臺(tái)中還要輸出到HTML文檔中。這樣我們就可以通過(guò)定義一個(gè)父記錄器A,它負(fù)責(zé)把日志信息輸出到控制臺(tái)中;定義一個(gè)A的子記錄器A.B,它負(fù)責(zé)把日志信息輸出到文件中;定義一個(gè)A的子記錄器A.C,它負(fù)責(zé)把日志信息輸出到HTML文檔中。
          記錄器遵循的是類似包的層次,這樣做為我們帶來(lái)了大大的方便。Logger類中的getLogger()方法可以取得Logger對(duì)象,這個(gè)方法有三種參數(shù)形式String、Class和URL,其實(shí)不論是用哪一種,最終都是通過(guò)記錄器的名字來(lái)取得記錄器對(duì)象的。如果要取得一個(gè)名為A.B的記錄器對(duì)象,我們可以Logger.getLogger("A.B")。但從上面的例子中,我們都是通過(guò)Logger.getLogger(TestLog4j.class.getName())這種方法來(lái)取得記錄器對(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í)通過(guò)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í),如果沒(méi)有定義了名為A.B.BClass的記錄器,那會(huì)嘗試返回名為A.B的記錄器的對(duì)象;如果又沒(méi)有定義名為A.B的記錄器,它會(huì)嘗試返回名為A的記錄器的對(duì)象;如果也沒(méi)定義名為A的記錄器,它就會(huì)返回根記錄器的對(duì)象,而根記錄器是必須存在的,所以你總能得到一個(gè)記錄器對(duì)象。
          好了,現(xiàn)在我們回到前面的問(wèn)題,我們?yōu)槭裁纯傄ㄟ^(guò)Logger.getLogger(BClass.class.getName())這種以類全名作為參數(shù)來(lái)取得記錄器對(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)我們通過(guò)類命名來(lái)取得設(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ì)象可通過(guò)Logger.getRootLogger()函數(shù),要取得非根記錄器可通過(guò)Logger.getLogger()函數(shù)。
          理論知道就講到這里,紙上得來(lái)終覺(jué)淺,下面,我們來(lái)小小演練一下。
          例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()");
          }
          }
          最后我們來(lái)看一下配置文件。
          例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

          首先,先來(lái)看一下配置文件都有些什么東西。
          1)?在1區(qū)中定義了一個(gè)根記錄器。這個(gè)根記錄器具有DEBUG級(jí)別并有一個(gè)名稱為stdout的輸出端appender。
          2)?2區(qū)中的內(nèi)容是這一節(jié)的重點(diǎn),也是應(yīng)用到記錄器層次的地方,但其實(shí)也只有兩句,充分體現(xiàn)了log4j的簡(jiǎn)單性。在這里,我們定義了兩個(gè)名稱分別為TestLog4j和TestLog4j.TestLog4j2設(shè)計(jì)器。
          ??在定義TestLog4j記錄器時(shí)沒(méi)有指定級(jí)別,所以它的級(jí)別繼承自它的父記錄器,即要記錄器,所以它的級(jí)別也為DEBUG。在定義TestLog4j記錄器時(shí)又定義了一個(gè)名稱為R的輸出端,所以它的輸出端有兩個(gè),一個(gè)從根記錄器繼承而來(lái)的名為stdout的輸出端,另一個(gè)為在此定義的名為R的輸出端。在此需要注意的是,在定義記錄器時(shí)必須先定義記錄器的級(jí)別,然后才是記錄器的輸出端。如果只想定義輸出端而不定義級(jí)別,則雖然級(jí)別可以為空,但逗號(hào)分隔符不能省略。如定義TestLog4j記錄器的做法。
          ??在定義TestLog4j.TestLog4j2記錄器時(shí)又指定了它的級(jí)別,由于一個(gè)記錄器的級(jí)別只能有一個(gè),所以新指定的級(jí)別將覆寫(xiě)掉它的父記錄器的級(jí)別(這就象Java中的多態(tài))。我們沒(méi)有定義TestLog4j.TestLog4j2記錄器的輸出端,所以它的輸出端將從它的父記錄器中繼承而來(lái)。它的父記錄器為estLog4j記錄器,所以它和estLog4j記錄器一樣具有兩個(gè)名稱分別為stdout和R的輸出端。
          3)?剩下的3區(qū)和4區(qū)分別設(shè)置了兩個(gè)輸出端的參數(shù)值。
          接下來(lái),回到我們的代碼,看一下是如何取得記錄器,在取記錄器時(shí)又發(fā)生了什么。
          1)?例4-a中的代碼(2)中,語(yǔ)句Logger.getLogger()中的參數(shù)TestLog4j.class.getName()的值為TestLog4j.?TestLog4j,所以此語(yǔ)句的結(jié)果是取得一個(gè)名為TestLog4j.?TestLog4j的記錄器的對(duì)象。但在配置文件中并沒(méi)有定義這樣的記錄器,所以最終將返回與所需的名稱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)在,我們來(lái)看一下要如何在J2EE開(kāi)發(fā)中應(yīng)用log4j。其實(shí)在Web?application中應(yīng)用log4j也很簡(jiǎn)單,與在application中應(yīng)用log4j不同之處就是要在所有應(yīng)用log4j的代碼之前對(duì)log4j進(jìn)行初始化。所以,我們?cè)趙eb?application中就要把log4j的初始化工作獨(dú)立出來(lái),把它放在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()?
          {
          //通過(guò)web.xml來(lái)動(dòng)態(tài)取得配置文件
          String?prefix?=?getServletContext().getRealPath("/");
          String?file?=?getInitParameter("log4j-init-file");

          //?如果沒(méi)有給出相應(yīng)的配置文件,則不進(jìn)行初始化
          if(file?!=?null)?
          {
          PropertyConfigurator.configure(prefix+file); //(1)
          }
          }

          public?void?doGet(HttpServletRequest?req,?HttpServletResponse?res)?
          {}?
          }
          下面來(lái)看一下這個(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開(kāi)發(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;
          }
          }

          posted on 2006-04-10 23:11 limq 閱讀(5636) 評(píng)論(3)  編輯  收藏

          評(píng)論

          # re: log4j使用進(jìn)階  回復(fù)  更多評(píng)論   

          不錯(cuò)!
          2006-11-23 15:13 | mayilance

          # re: log4j使用進(jìn)階[未登錄](méi)  回復(fù)  更多評(píng)論   

          2007-04-12 10:09 | dd

          # re: log4j使用進(jìn)階  回復(fù)  更多評(píng)論   

          寫(xiě)的很好。學(xué)習(xí)了非常的感謝。感謝!!!!!
          2010-05-07 16:03 | lsj304

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 兴业县| 新兴县| 西乡县| 两当县| 响水县| 望谟县| 航空| 虹口区| 德格县| 大化| 新龙县| 柘荣县| 新巴尔虎左旗| 沛县| 兴海县| 绩溪县| 扶风县| 天等县| 宜丰县| 芷江| 长丰县| 梓潼县| 宁蒗| 永城市| 江都市| 珲春市| 岳阳市| 阿克苏市| 叙永县| 潮安县| 湄潭县| 武威市| 云林县| 景宁| 和政县| 深圳市| 竹溪县| 南昌县| 岫岩| 毕节市| 慈溪市|