通過使用Log4j,我們可以更自如控制日志資訊。
Log4j是Apache的一個開放源代碼項目,通過使用Log4j,我們可以控制日志資訊輸送的;我們也可以控制每一條日志的輸出格式;通過定義每一條日志資訊的級别,我們能夠更加細緻地控制日志的生成過程。最令人感興趣的就是,這些可以通過一個配置檔案來靈活地進行配置,而不需要修改應用的代碼。
log4j的好處在于:
1) 通過修改配置檔案,就可以決定log資訊的目的地——控制台、檔案、GUI元件、甚至是套接口伺服器、NT的事件記錄器、UNIX Syslog守護程序等
2) 通過修改配置檔案,可以定義每一條日志資訊的級别,進而控制是否輸出。在系統開發階段可以列印詳細的log資訊以跟蹤系統運作情況,而在系統穩定後可以關閉log輸出,進而在能跟蹤系統運作情況的同時,又減少了垃圾代碼(System.out.println(......)等)。
3) 使用log4j,需要整個系統有一個統一的log機制,有利于系統的規劃。
Log4j由三個重要的元件構成:日志資訊的優先級,日志資訊的輸出目的地,日志資訊的輸出格式。日志資訊的優先級從高到低有FATAL、ERROR、WARN、INFO、DEBUG,分别用來指定這條日志資訊的重要程度;日志資訊的輸出目的地指定了日志将列印到控制台還是檔案中;而輸出格式則控制了日志資訊的顯示内容。
分為OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定義的級别。
Log4j建議隻使用四個級别,優先級從高到低分别是ERROR、WARN、INFO、DEBUG。通過在這裡定義的級别,您可以控制到應用程式中相應級别的日志資訊的開關。
假如在一個級别為q的Logger中發生一個級别為p的日志請求,如果p>=q,那麼請求将被啟用。這是Log4j的核心原則。
比如在這裡定義了INFO級别,則應用程式中所有DEBUG級别的日志資訊将不被列印出來;
有選擇的能用或者禁用日志請求僅僅是Log4j的一部分功能。Log4j允許日志請求被輸出到多個輸出源。用Log4j的話說,一個輸出源被稱做一個Appender。
Appender包括console(控制台), files(檔案), GUI components(圖形的元件), remote socket servers(socket 服務), JMS(java資訊服務), NT Event Loggers(NT的事件日志), and remote UNIX Syslog daemons(遠端UNIX的背景日志服務)。它也可以做到異步記錄。
一個logger可以設定超過一個的appender。
用addAppender 方法添加一個appender到一個給定的logger。對于一個給定的logger它每個生效的日志請求都被轉發到該logger所有的appender上和該logger的父輩logger的appender上。
如果使用ConsoleAppender,那麼log資訊将寫到Console。效果等同于直接把資訊列印到System.out上了。
使用FileAppender,那麼log資訊将寫到指定的檔案中。這應該是比較經常使用到的情況。
相應地,在配置檔案中應該指定log輸出的檔案名。如下配置指定了log檔案名為dglog.txt
log4j.appender.A2.File=dglog.txt
注意将A2替換為具體配置中Appender的别名。
使用FileAppender可以将log資訊輸出到檔案中,但是如果檔案太大了讀起來就不友善了。這時就可以使用DailyRollingAppender。DailyRollingAppender可以把Log資訊輸出到按照日期來區分的檔案中。配置檔案就會每天産生一個log檔案,每個log檔案隻記錄當天的log資訊:
log4j.appender.A2=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A2.file=dglog
log4j.appender.A2.DatePattern='.'yyyy-MM-dd
log4j.appender.A2.layout=org.apache.log4j.PatternLayout
log4j.appender.A2.layout.ConversionPattern= %5r %-5p %c{2} - %m%n
檔案大小到達指定尺寸的時候産生一個新的檔案。
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File= ../logs/dglog.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=%p %t %c - %m%n
這個配置檔案指定了輸出源R,是一個輪轉日志檔案。最大的檔案是100KB,當一個日志檔案達到最大尺寸時,Log4J會自動把example.log重命名為dglog.log.1,然後重建一個新的dglog.log檔案,依次輪轉。
将日志資訊以流格式發送到任意指定的地方。
Layout指定了log資訊輸出的樣式。
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以靈活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志資訊的級别和資訊字元串),
org.apache.log4j.TTCCLayout(包含日志産生的時間、線程、類别等等資訊)
%m 輸出代碼中指定的消息
%p 輸出優先級,即DEBUG,INFO,WARN,ERROR,FATAL
%r 輸出自應用啟動到輸出該log資訊耗費的毫秒數
%c 輸出所屬的類目,通常就是所在類的全名
%t 輸出産生該日志事件的線程名
%n 輸出一個回車換行符,Windows平台為"rn",Unix平台為"n"
%d 輸出日志時間點的日期或時間,預設格式為ISO8601,也可以在其後指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},輸出類似:2002年10月18日 22:10:28,921
%l 輸出日志事件的發生位置,包括類目名、發生的線程,以及在代碼中的行數。舉例:Testlog4.main(Test Log4.java:10)
例子1:顯示日期和log資訊
log4j.appender.A2.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %m%n
列印的資訊是:
2002-11-12 11:49:42,866 SELECT * FROM Role WHERE 1=1 order by createDate desc
例子2:顯示日期,log發生地方和log資訊
log4j.appender.A2.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %l "#" %m%n
2002-11-12 11:51:46,313 cn.net.unet.weboa.system.dao.RoleDAO.select(RoleDAO.java:409) "#"
SELECT * FROM Role WHERE 1=1 order by createDate desc
例子3:顯示log級别,時間,調用方法,log資訊
log4j.appender.A2.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS}
method:%l%n%m%n
log資訊:
[DEBUG] 2002-11-12 12:00:57,376
method:cn.net.unet.weboa.system.dao.RoleDAO.select(RoleDAO.java:409)
log4j.rootLogger=DEBUG
#将DAO層log記錄到DAOLog,allLog中
log4j.logger.DAO=DEBUG,A2,A4
#将邏輯層log記錄到BusinessLog,allLog中
log4j.logger.Businesslog=DEBUG,A3,A4
#A1--列印到螢幕上
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-5p [%t] %37c %3x - %m%n
#A2--列印到檔案DAOLog中--專門為DAO層服務
log4j.appender.A2.file=DAOLog
#A3--列印到檔案BusinessLog中--專門記錄邏輯處理層服務log資訊
log4j.appender.A3=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A3.file=BusinessLog
log4j.appender.A3.DatePattern='.'yyyy-MM-dd
log4j.appender.A3.layout=org.apache.log4j.PatternLayout
log4j.appender.A3.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS}
#A4--列印到檔案alllog中--記錄所有log資訊
log4j.appender.A4=org.apache.log4j.DailyRollingFileAppender
log4j.appender.A4.file=alllog
log4j.appender.A4.DatePattern='.'yyyy-MM-dd
log4j.appender.A4.layout=org.apache.log4j.PatternLayout
log4j.appender.A4.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS}
log4j使用步驟有3個:
預設的Log4j initialization典型的應用是在web-server 環境下。在tomcat3.x和tomcat4.x下,你應該将配置檔案Log4j.properties放在你的web應用程式的WEB-INF/classes 目錄下。
Log4j将發現屬性檔案,并且以此初始化。這是使它工作的最容易的方法。
你也可以選擇在運作tomcat前設定系統屬性Log4j.configuration 。對于tomcat 3.x,TOMCAT_OPTS 系統變量是用來設定指令行的選項。對于tomcat4.0,用系統環境變量CATALINA_OPTS 代替了TOMCAT_OPTS。
UNIX 指令行
export TOMCAT_OPTS="-DLog4j.configuration=foobar.txt"
告訴Log4j用檔案foobar.txt作為預設的配置檔案。這個檔案應該放在WEB-INF/classes 目錄下。這個檔案将被PropertyConfigurator所讀。每個web-application将用不同的預設配置檔案,因為每個檔案是和它的web-application 相關的。
1. export TOMCAT_OPTS="-DLog4j.debug -DLog4j.configuration=foobar.xml" export TOMCAT_OPTS="-DLog4j.debug -DLog4j.configuration=foobar.xml"
告訴Log4j輸出Log4j-internal的調試資訊,并且用foobar.xml作為預設的配置檔案。這個檔案應該放在你的web-application的WEB-INF/classes 目錄下。因為有.xml的擴充名,它将被DOMConfigurator所讀。每個web-application将用不同的預設配置檔案。因為每個檔案都和它所在的web-application 相關的。
2. set TOMCAT_OPTS=-DLog4j.configuration=foobar.lcf
-DLog4j.configuratorClass=com.foo.BarConfigurator
告訴Log4j用檔案foobar.lcf作為預設的配置檔案。這個檔案應該放在你的web-application的WEB-INF/classes 目錄下。因為定義了Log4j.configuratorClass 系統屬性,檔案将用自定義的com.foo.barconfigurator類來解析。每個web-application将用不同的預設配置檔案。因為每個檔案都和它所在的web-application 相關的。
3. set TOMCAT_OPTS=-DLog4j.configuration=file:/c:/foobar.lcf set TOMCAT_OPTS=-DLog4j.configuration=file:/c:/foobar.lcf
告訴Log4j用檔案foobar.lcf作為預設的配置檔案。這個配置檔案用URL file:/c:/foobar.lcf定義了全路徑名。這樣同樣的配置檔案将被所有的web-application所用。
不同的web-application将通過它們自己的類裝載器來裝載Log4j。這樣,每個Log4j的環境将獨立的運作,而沒有任何的互相同步。例如:在多個web-application中定義了完全相同的輸出源的FileAppenders将嘗試寫同樣的檔案。結果好象是缺乏安全性的。你必須確定每個不同的web-application的Log4j配置沒有用到同樣的系統資源。
用一個特别的servlet來做Log4j的初始化也是可以的。如下是一個例子:
public class Log4jInit extends HttpServlet {
public void init() {
String prefix = getServletContext().getRealPath("/");
String file = getInitParameter("Log4j-init-file");
if(file != null) {
PropertyConfigurator.configure(prefix+file);
}
public void doGet(HttpServletRequest req, HttpServletResponse res) {
在web.xml中定義随後的servlet為你的web-application。
<servlet>
<servlet-name>Log4j-init</servlet-name>
<servlet-class>xx.xx.Log4jInit</servlet-class>
<init-param>
<param-name>Log4j-init-file</param-name>
<param-value>WEB-INF/classes/Log4j.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
寫一個初始化的servlet是最有彈性的初始化Log4j的方法。代碼中沒有任何限制,你可以在servlet的init方法中定義它。
log4j可以使用3中配置器來初始化:BasicConfigurator,DOMConfigurator,PropertyConfigurator
其文法為:
BasicConfigurator.configure (): 自動快速地使用預設Log4j環境。
PropertyConfigurator.configure ( String configFilename) :讀取使用Java的特性檔案編寫的配置檔案。
DOMConfigurator.configure ( String filename ) :讀取XML形式的配置檔案。
這裡用的是PropertyConfigurator。使用PropertyConfigurator适用于所有的系統。如下的語句:
PropertyConfigurator.configure("log4j.properties");
就以log4j.properties為配置檔案初始化好了log4j環境。
注意一點:這個語句隻需要在系統啟動的時候執行一次。例如,在ActionServlet的init()方法中調用一次。
public class ActionServlet extends HttpServlet{
...
/**
* Initialize global variables
*/
public void init() throws ServletException {
// 初始化Action資源
try{
initLog4j();
}catch(IOException e){
throw new ServletException("Load ActionRes is Error");
protected void initLog4j(){
}//end class ActionServlet
使用Log4j,首先就是擷取日志記錄器,這個記錄器将負責控制日志資訊。其文法為:
public static Logger getLogger( String name),
通過指定的名字獲得記錄器,如果必要的話,則為這個名字建立一個新的記錄器。Name一般取本類的名字,比如:
static Logger logger = Logger.getLogger ( ServerWithLog4j.class.getName () ) ;
Log4j使得通過軟體元件命名logger很容易。我們可以通過Logger的靜态的初始化方法在每一個類裡定義一個logger,令logger的名字等于類名的全局名,而實作logger的命名。這是一個實效的簡單的定義一個logger的方法。因為日志輸出帶有産生日志的類的名字,這個命名政策使得我們更容易定位到一個日志資訊的來源。雖然普通,但卻是命名logger的常用政策之一。
Log4j沒有限制定義logger的可能。開發員可以自由的按照它們的意願定義logger的名稱。
然而,以類的所在位置來命名Logger好象是目前已知的最好方法。
log.debug("it is the debug info");
一個經常引用的依靠于logging的參數是可以計算的花費。這是一個合理的概念,一個适度的應用程式可能産生成千上萬個日志請求。許多努力花在測量和調試logging的優化上。Log4j要求快速和彈性:速度最重要,彈性是其次。
當日志被徹底的關閉,一個日志請求的花費等于一個方法的調用加上整數的比較時間。在233mhz的Pentium II 機器上這個花費通常在5-50納秒之間。
然而,方法調用包括參數建構的隐藏花費。
例如,對于logger cat,logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
引起了建構資訊參數的花費,例如,轉化整數i和entry[i]到一個string,并且連接配接中間字元串,不管資訊是否被輸出。這個參數的建構花費可能是很高,它主要決定于被調用的參數的大小。
避免參數建構的花費應如下,
if(logger.isDebugEnabled())
{
logger.debug("result is" + result );
如果logger的debug被關閉這将不會招緻參數建構的花費。另一方面,如果logger是debug的話,它将産生兩次判斷 logger是否能用的花費。一次是在debugenabled,一次是debug。這是無關緊要的,因為判斷日志的能用 隻占日志實際花費時間的約1%。
在Log4j裡,日志請求在Logger 類的執行個體裡。Logger 是一個類,而不是一個接口。這大量的減少了在方法調用上的彈性化的花費。
當然使用者采用預處理或編譯時間技術去編譯出所有的日志聲明。這将導緻完美的執行成效。然而因為二進制應用程式不包括任何的日志聲明的結果,日志不可能對那個二進制程式開啟。以我的觀點,以這種較大的代價來換取較小的性能優化是不值得的。
這是本質上的優化logger的層次。當日志狀态為開,Log4j依然需要比較請求的級别與logger的級别。然而, logger可能沒有被安排一個級别;它們将從它們的father繼承。這樣,在繼承之前,logger可能需要搜尋它的ancestor。
這裡有一個認真的努力使層次的搜尋盡可能的快。例如,子logger僅僅連接配接到它的存在的father logger。
在先前展示的BasicConfigurator 例子中,名為com.foo.bar 的logger是連接配接到跟根logger,是以繞過 了不存在的logger com和com.foo。這将顯著的改善執行的速度,特别是解析logger的層結構時。
典型的層次結構的解析的花費是logger徹底關閉時的三倍。
這是主要花費在日志輸出的格式化和發送它到它的輸出源上。這裡我們再一次的付出努力以使格式化執行的盡可能快。同appender一樣。實際上典型的花費大約是100-300毫秒。
詳情看org.apache.log4.performance.Logging。
雖然Log4j有許多特點,但是它的第一個設計目标還是速度。一些Log4j的元件已經被重寫過很多次以改善性能。不過,投稿者經常提出了新的優化。你應該滿意的知道,以SimpleLayout的配置執行測試已經展示了Log4j的輸出同System.out.println一樣快。