背景
對於有經驗的開發者來說,日誌記錄的重要性顯而易見。例如程序中的異常處理和安 全性都依賴於Logging的功能來幫助履行它們的指責。應用程序中的日誌記錄主要基於三個目的:監視代碼中變量的變化情況,週期性的記錄到文件中供其他 應用進行統計分析工作;跟蹤代碼運行時軌跡,作爲日後審計的依據;擔當集成開發環境中的調試器的作用,向文件或控制檯打印代碼的調試信息。經驗表明日誌記 錄是開發週期中的重要組成部分。
最簡單的做法就是在代碼中嵌入許多的打印語句,但是這樣打印語句會充斥代碼的主體,顯然不是一個好方法。因此,使用成熟的框架例如Log4j,則會更具靈活性。
Log4j簡介
Log4j 框架是用 Java 語言編寫的標準日誌記錄框架。作爲 Jakarta 項目的一部分,它在 Apache 軟件許可證(Apache Software License)下分發,以速度和靈活性爲中心概念:Log4j 環境是完全可配置的,通過使用Log4j,我們可以控制日誌信息輸送的目的地是控制檯、文件、GUI組件、甚至是套接口服務器、NT的事件記錄器、 UNIX Syslog守護進程等;我們也可以控制每一條日誌的輸出格式;通過定義每一條日誌信息的級別,我們能夠更加細緻地控制日誌的生成過程。
Log4j由三個重要的部件構成:記錄器(Loggers)、輸出源(Appenders)和佈局(Layouts)。
記錄器按照佈局中指定的格式把日誌信息寫入一個或多個輸出源。輸出源可以是控制檯、文本文件、XML文件或Socket,甚至還可以把信息寫入到Windows事件日誌或通過電子郵件發送。我們可以通過配置文件來部署這些組件。
其實您也可以完全不使用配置文件,而是在代碼中配置Log4j環境。但是,使用配置文件將使您的應用程序更加靈活。本文從描述 log4j 體系結構的主要組件着手。然後是描述基本用法和配置的簡單示例。
定義配置文件
Log4j支持兩種配置文件格式,一種是XML格式的文件,一種是Java特性文件(鍵=值)。下面我們介紹使用Java特性文件做爲配置文件的方法:
一、 配置記錄器。
Log4j允許程序員定義多個記錄器,每個記錄器有自己的名字。但有一個記錄器叫根記錄器,它永遠存在,且不能通過名字檢索或引用,在配置文件中,可以如下定義根記錄器:
log4j.rootLogger = [ level ] , appenderName, appenderName, …
Level是記錄器的級別,它是日誌記錄的優先級,分爲OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定義的級別。Log4j建議只使用四個級別:ERROR、WARN、INFO、DEBUG:
DEBUG < INFO < WARN < ERROR < FATAL
右邊的級別比左邊的高。如果一條log信息的級別,大於等於記錄器的級別值,那麼記錄器就會記錄它。例如level被設置爲INFO級別,那麼應用程序中所有的DEBUG的日誌信息將不被打印出來。通過在這裏定義的級別,您可以控制到應用程序中相應級別的日誌信息的開關。
appenderName是輸出源的名字,它指定日誌信息輸出到哪個地方。您可以爲一個記錄器指定多個輸出源。
在一些配置文件中,你可能會看到下面的語句:
log4j.rootCategory = [ level ] , appenderName, appenderName, …
在早期的Log4j版本中,org.apache.Category實現了記錄器的功能,爲了提高向後兼容 性,Logger擴展了Category,因此rootCategory和rootLogger是可以互換的,但最後Category將從類庫中刪除,因 此請使用Logger類。
除了根記錄器之外,log4j允許程序員定義多個記錄器,每個記錄器有自己的名字:
log4j.logger.loggerName = [ level ] , appenderName, appenderName, …
在Log4J中Logger是具有層次關係的,Log4j支持配置的記錄器之間的“父子關係”,記錄器之間 通過名字來表明隸屬關係(或家族關係),它們有一個共同的根,位於最上層,其它Logger遵循類似包的層次:記錄器a.b,與記錄器a.b.c之間是父 子關係,而記錄器a與a.b.c之間是祖先與後代的關係。例如:
static Logger root = Logger.getRootLogger();
static Logger log1 = Logger.getLogger("cc");
static Logger log2 = Logger.getLogger("cc.ejb");
static Logger log3 = Logger.getLogger("cc.ejb.my.TestApp");
上面代碼中,log1是log2的父親,是log3的祖先,而root是所有log1、log2、 log3的祖先,它們都從root中繼承。所以,一般情況下,僅需要配置好rootLogger,其它子記錄器都會從中繼承rootLogger的配置。 如果修改了rootLogger的配置,其它所有的子記錄器也會繼承這種變化。這樣就大大地方便了配置。
如果一個應用中包含了上千個類都需要日誌,那麼我們是否需要配置上千個Logger呢?我們通過一個簡 單的辦法來解決這個問題: 用每一個java類文件名(包含該類的包名)定義一個記錄器,這是一種有用並且直觀的記錄器實例名的定義方式。例如在配置文件 中定義了一個com.foo的記錄器:
log4j.logger.com.foo=WARN
在com.foo中的一個java類bar,我們通過其本類的名字獲得一個記錄器“com.foo.Bar”:
package com.foo;
class Bar{
static Logger log=Logger.getLogger(bar.Class.getName());
.....
}
由於記錄器com.foo.Bar 沒有指定的級別,它從com.foo(在配置文件中其級別設置成WARN) 繼承級別。並且這樣我們就能方便的從大量log信息中判斷出它們各自的來源。當然了,這不是硬性規定的,實際上Log4j沒有對設置記錄器的實例名做什麼 限制,程序員可以根據自己的喜好隨意定義。
二、日誌信息輸出源Appender
log4j 還允許日誌記錄請求打印到多個輸出目的地,按 log4j 的叫法是輸出源。一個記錄器可以有多個輸出源。一條log信息如果可被這個記錄器處理,則該記錄器會把這條信息送往每個它所擁有的輸出源,以及層次結構中 更高級的輸出源。例如,根記錄器以控制檯作爲輸出源,則所有可被紀錄的日誌都將至少打印到控制檯。
配置日誌信息輸出源,其語法爲:
log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1
…
log4j.appender.appenderName.option = valueN
Log4j提供的appender有以下幾種:
- org.apache.log4j.ConsoleAppender(控制檯)
- org.apache.log4j.FileAppender(文件)
- org.apache.log4j.DailyRollingFileAppender(每天產生一個日誌文件)
- org.apache.log4j.RollingFileAppender(文件大小到達指定尺寸的時候產生一個新的文件)
- org.apache.log4j.WriterAppender(將日誌信息以流格式發送到任意指定的地方)
- org.apache.log4j.SocketAppender (Socket)
- org.apache.log4j.NtEventLogAppender (NT的Event Log)
- org.apache.log4j.JMSAppender (電子郵件)
請注意,可以通過覆蓋缺省行爲,這樣就不再附加累積的輸出源:
log4j.additivity.loggerName=false
注意,不要把一個輸出源附加到多個記錄器上,否則會得到“Attempted to append to closed appender named xxx”的信息。
三、配置日誌信息的格式(佈局),其語法爲:
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1
…
log4j.appender.appenderName.layout.option = valueN
其中,Log4j提供的layout有以下幾種:
- org.apache.log4j.HTMLLayout(以HTML表格形式佈局)
- org.apache.log4j.PatternLayout(可以靈活地指定佈局模式)
- org.apache.log4j.SimpleLayout(包含日誌信息的級別和信息字符串)
- org.apache.log4j.TTCCLayout(包含日誌產生的時間、線程、類別等等信息)
如果採用了PatternLayout, 則Log4J採用類似C語言中的printf函數的打印格式格式化日誌信息,打印參數如下:
- %m 輸出代碼中指定的消息
- %p 輸出優先級,即DEBUG,INFO,WARN,ERROR,FATAL
- %r 輸出自應用啓動到輸出該log信息耗費的毫秒數
- %c 輸出所屬的類目,通常就是所在類的全名
- %t 輸出產生該日誌事件的線程名
- %n 輸出一個回車換行符,Windows平臺爲“/r/n”,Unix平臺爲“/n”
- %d 輸出日誌時間點的日期或時間,默認格式爲ISO8601,也可以在其後指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},輸出類似:2002年10月18日 22:10:28,921
- %l 輸出日誌事件的發生位置,包括類目名、發生的線程,以及在代碼中的行數。舉例:Testlog4.main(TestLog4.java:10)
四、例子
下面是一個完整的Log4j配置文件,這個配置文件指定了兩個輸出源stdout和R。前 者把日誌信息輸出到控制檯,後者是一個輪轉日誌文件。最大的文件是100KB,當一個日誌文件達到最大尺寸時,Log4J會自動把example.log 重命名爲example.log.1,然後重建一個新的example.log文件,依次輪轉。
log4j.rootLogger=debug, stdout, R
log4j.appender.stdout=org.apache.log4j.FileAppender
log4j.appender.stdout.File=System.out
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
log4j.logger.cc.ejb.my=error,out
log4j.appender.out=org.apache.log4j.ConsoleAppender
log4j.appender.out.layout=org.apache.log4j.PatternLayout
log4j.appender.out.layout.ConversionPattern=%p %t %c - %m%n
log4j.logger.cc.ejb.my.son=debug
log4j.additivity.cc.ejb.my.son=false
在代碼中使用Log4j
一、得到記錄器
使用Log4j,第一步就是獲取日誌記錄器,這個記錄器將負責控制日誌信息。其語法爲:
public static Logger getLogger( String name)
通過指定的名字獲得記錄器,如果必要的話,則爲這個名字創建一個新的記錄器。Name一般取本類的名字,比如:
static Logger logger = Logger.getLogger ( ServerWithLog4j.class.getName () )
二、讀取配置文件
當獲得了日誌記錄器之後,第二步將配置Log4j環境,其語法爲:
//自動快速地使用缺省Log4j環境。
BasicConfigurator.configure ();
//讀取使用Java的特性文件編寫的配置文件
PropertyConfigurator.configure ( String configFilename);
//讀取XML形式的配置文件
DOMConfigurator.configure ( String filename );
三、插入記錄信息(格式化日誌信息)
當上兩個必要步驟執行完畢,您就可以輕鬆地使用不同優先級別的日誌記錄語句插入到您想記錄日誌的任何地方,其語法如下:
Logger.debug ( Object message ) ;
Logger.info ( Object message ) ;
Logger.warn ( Object message ) ;
Logger.error ( Object message ) ;
四、例子
我們通過下面這個簡單的例子,來演示在程序如何使用Log4j,您可以修改配置文件以得到不同日誌信息。
package cc.ejb.my;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import my.son.Foo;
public class TestApp {
static Logger logger=Logger.getLogger(TestApp.class.getName());
public static void main(String[] args) {
PropertyConfigurator.configure("log4j.properties");
logger.info("Applcaiton Starts");
logger.warn("Bar Starts");
Bar bar=new Bar();
logger.error("Bar Errors");
bar.doIt();
logger.warn("Bar Exits");
logger.info("Foo Starts");
Foo foo=new Foo();
logger.error("Foo Errors");
foo.doit();
logger.warn("Foo exits ");
logger.info("Applcaition Exits");
}
}
class Bar
{
static Logger logger = Logger.getLogger(Bar.class.getName());
public void doIt() {
logger.debug("Did it again!");
}
}
package cc.ejb.my.son;
import org.apache.log4j.Logger;
public class Foo {
private Logger log=Logger.getLogger(Foo.class.getName());
public Foo() {
log.info("Foo Initialzie");
}
public void doit()
{
log.debug("Do it in Foo");