日志log4j的實(shí)現(xiàn)模式有人說(shuō)是AOP,不過(guò)我不清楚,這里重點(diǎn)講述一下LOG4J.一些使用.同時(shí)當(dāng)前版本 的LOG4J與以前的版本差別也是很大的,所以一定要清楚使用的LOG4J版本,這里所述以最新的為準(zhǔn)。
log4j有三種主要的
組件:
記錄器,
存放器,
布局 1、 記錄器(記錄器可不關(guān)心log數(shù)據(jù)存放的事喲)
log4j允許程序員定義多個(gè)記錄器,每個(gè)記錄器有自己的名字,記錄器之間通過(guò)名字來(lái)表明隸屬關(guān)系(或家族關(guān)系)。列如,記錄器a.b,與記錄器a.b.c之間是父子關(guān)系,而記錄器a與a.b.c之間是祖先與后代的關(guān)系,父子關(guān)系是祖先與后代關(guān)系的特例。通過(guò)這種關(guān)系,可以描述不同記錄器之間的邏輯關(guān)系。
有一個(gè)記錄器叫根記錄器,它永遠(yuǎn)存在,且不能通過(guò)名字檢索或引用,可以通過(guò)Logger.getRootLogger()方法取得它,而一般記錄器通過(guò)Logger.getLogger(String name)方法。下面是Logger類的基本方法。
package org.apache.log4j;
public class Logger {
// Creation & 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);
}
記錄器還有一個(gè)重要的屬性,就是級(jí)別。(這好理解,就象一個(gè)家庭中,成員間存在輩份關(guān)系,但不同的成員的身高可能不一樣,且身高與輩份無(wú)關(guān))程序員可以給不同的記錄器賦以不同的級(jí)別,如果某個(gè)成員沒(méi)有被明確值,就自動(dòng)繼承最近的一個(gè)有級(jí)別長(zhǎng)輩的級(jí)別值。根記錄器總有級(jí)別值。例如:
記錄器名 |
賦予的級(jí)別值 |
繼承的級(jí)別值 |
root |
Proot |
Proot |
X |
Px |
Px |
X.Y |
none |
Px |
X.Y.Z |
none |
Px |
程序員可以自由定義級(jí)別。級(jí)別值之間存在偏序關(guān)系,如上面幾種級(jí)別就有關(guān)系DEBUG
每一條要輸出的log信息,也有一個(gè)級(jí)別值。
前面的Logger類中,就預(yù)定義了 DEBUG, INFO, WARN, ERROR ,F(xiàn)ATAL幾種級(jí)別,由于與方法綁定,讓人易產(chǎn)生誤解,其實(shí)這幾個(gè)方法只不過(guò)表明了要記錄的log信息的級(jí)別。當(dāng)調(diào)用log()方法時(shí),log信息的級(jí)別就需要在通過(guò)參數(shù)明確指定。
如果一條log信息的級(jí)別,大于等于記錄器的級(jí)別值,那么記錄器就會(huì)記錄它。如果你覺得難以理解,可參考下例。
// 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");
有幾個(gè)有趣的情況,一是當(dāng)一個(gè)記錄器實(shí)例化后,再一次用相同的名字調(diào)用getLogger()會(huì)返回對(duì)它的引用,這非常有利于用同一個(gè)記錄器在不同代碼或類中記錄log信息,另一個(gè)是與自然界中祖先先于后代出現(xiàn)不同,一個(gè)記錄器的祖先可以比后代記錄出現(xiàn)的晚,但會(huì)自動(dòng)根據(jù)名字之間的關(guān)系建立這種家族關(guān)系。
2、存放器
在log4j中,log信息通過(guò)存放器輸出到目的地。支持的存放器有console, files, GUI components, remote socket servers, JMS, NT Event Loggers, remote UNIX Syslog daemons。通過(guò)file存放器,log信息可以被輸出到不同的文件中(即不同的目的地)。log信息可被異步存放。
一個(gè)記錄器可以有多個(gè)存放器,可以通過(guò)方法addAppender來(lái)增加存放器。一條blog信息如果可被這個(gè)記錄器處理,則記錄器會(huì)把這條信息送往每個(gè)它所擁有的存放器。
每個(gè)記錄器有一個(gè)繼承開關(guān),其開關(guān)決定記錄器是/否繼承其父記錄器的存放器,注意,如果繼承則只繼承其父記錄器,而不考慮更遠(yuǎn)的祖先的情況。參考下表:
記錄器 |
增加的存放器 |
繼承的存放器 |
輸出的目的地 |
備注 |
root |
A1 |
not applicable |
A1 |
The root logger is anonymous but can be accessed with the Logger.getRootLogger() method. There is no default appender attached to root. |
x |
A-x1, A-x2 |
TRUE |
A1, A-x1, A-x2 |
Appenders of "x" and root. |
x.y |
none |
TRUE |
A1, A-x1, A-x2 |
Appenders of "x" and root. |
x.y.z |
A-xyz1 |
TRUE |
A1, A-x1, A-x2, A-xyz1 |
Appenders in "x.y.z", "x" and root. |
security |
A-sec |
FALSE |
A-sec |
No appender accumulation since the additivity flag is set to false. |
security.access |
none |
TRUE |
A-sec |
Only appenders of "security" because the additivity flag in "security" is set to false. |
3、布局
布局負(fù)責(zé)格式化輸出的log信息。log4j的PatternLayout可以讓程序以類似C語(yǔ)言printf的格式化模板來(lái)定義格式。
4、log4j可據(jù)程序員制定的標(biāo)準(zhǔn)自動(dòng)提供一些log信息,這對(duì)那類需要頻繁log的對(duì)象的情況很幫助。對(duì)象的自動(dòng)log,具有繼承性
前面主要記了一些原理,今天是實(shí)務(wù)。
1、研究發(fā)現(xiàn),一個(gè)系統(tǒng)中4%的代碼是用來(lái)作logging的。
2、Log4J的配置文件(Configuration File)就是用來(lái)設(shè)置記錄器的級(jí)別、存放器和布局的,它可接key=value格式的設(shè)置或xml格式的設(shè)置信息。通過(guò)配置,可以創(chuàng)建出Log4J的運(yùn)行環(huán)境。
Log4J運(yùn)行時(shí),不對(duì)環(huán)境做任何假定,尤其是沒(méi)有默認(rèn)的存放器。
3、有幾種方式可以配置Log4J
1)在程序中調(diào)用BasicConfigurator.configure()方法;
2)配置放在文件里,通過(guò)命令行參數(shù)傳遞文件名字,通過(guò)PropertyConfigurator.configure(args[x])解析并配置;
3)配置放在文件里,通過(guò)環(huán)境變量傳遞文件名等信息,利用log4j默認(rèn)的初始化過(guò)程解析并配置;
4)配置放在文件里,通過(guò)應(yīng)用服務(wù)器配置傳遞文件名等信息,利用一個(gè)特殊的servlet來(lái)完成配置。
看下面的例子:
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.");
}
}
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!");
}
}
BasicConfigurator.configure給根記錄器增加一個(gè)ConsoleAppender,輸出格式通過(guò)PatternLayout設(shè)為"%-4r [%t] %-5p %c %x - %m%n",還有根記錄器的默認(rèn)級(jí)別是Level.DEBUG.
記錄器之間的關(guān)系如下圖:
輸出結(jié)果如下:
0 [main] INFO MyApp - Entering application.
36 [main] DEBUG com.foo.Bar - Did it again!
51 [main] INFO MyApp - Exiting application.
下面的代碼結(jié)合配置信息,會(huì)得到與上述程序一樣的結(jié)果。
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.");
}
}
配置文件的內(nèi)容如下:
# 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
利用配置文件,可以很方便地修改配置。如下例
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
# Print the date in ISO 8601 format
log4j.appender.stdout.layout.ConversionPattern=%d [%t] %-5p %c - %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
# Print only messages of level WARN or above in the package com.foo.
log4j.logger.com.foo=WARN
對(duì)于tomcat4,利用環(huán)境變量傳遞參數(shù)的,可參見以下幾個(gè)例子
unix設(shè)置
export CATALINA_OPTS ="-Dlog4j.configuration=foobar.txt"<===用PropertyConfigurator解析
export CATALINA_OPTS ="-Dlog4j.debug -Dlog4j.configuration=foobar.xml"<===用DOMConfigurator解析
以下是windows設(shè)置
set CATALINA_OPTS =-Dlog4j.configuration=foobar.lcf -Dlog4j.configuratorClass=com.foo.BarConfigurator<===用com.foo.BarConfigurator解析
set CATALINA_OPTS =-Dlog4j.configuration=file:/c:/foobar.lcf
配置文件位置如果沒(méi)有明確指明,則要放在WEB-INF/classes目錄下。
4、用servlet配置log4j
以下都是參考冰之火的文章,抄來(lái)放在這兒,并做了一些必要的修改。需要說(shuō)明的是,下面的代碼需要自己寫并發(fā)布,下的jar中沒(méi)有這個(gè)類。待我以后寫一個(gè),也放上來(lái)。我也寫了兩個(gè),在筆記(三)中。
在Application目錄下的web.xml文件加入以后代碼
<servlet>
<servlet-name>log4jlog4j-init</servlet-name>
<servlet-class>com.apache.jakarta.log4j.Log4jInit</servlet-class>
<init-param>
<param-name>log4j</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
這段代碼的意思是說(shuō),在Tomcat啟動(dòng)時(shí)加載com.apache.jakarta.log4j.Log4jInit這個(gè)名叫Log4jInit.class這個(gè)類文件。
其中Log4jInit.class的源代碼如下
package com.apache.jakarta.log4j;
import org.apache.log4j.PropertyConfigurator;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Log4jInit extends HttpServlet {
public void init() {
String prefix = getServletContext().getRealPath("/");
String file = getInitParameter("log4j");
// if the log4j-init-file is not set, then no point in trying
System.out.println("................log4j start");
if(file != null) {
PropertyConfigurator.configure(prefix+file);
}
}
public void doGet(HttpServletRequest req, HttpServletResponse res) {
}
}
在加載的過(guò)程中,程序會(huì)讀取/WEB-INF/log4j.properties這個(gè)文件。
配置文件講解如下:
# Set root logger level to DEBUG and its only appender to A1
#log4j中有五級(jí)logger
#FATAL 0
#ERROR 3
#WARN 4
#INFO 6
#DEBUG 7
#配置根Logger,其語(yǔ)法為:
#log4j.rootLogger = [ level ] , appenderName, appenderName, …
log4j.rootLogger=INFO, A1 ,R
#這一句設(shè)置以為著所有的log都輸出
#如果為log4j.rootLogger=WARN, 則意味著只有WARN,ERROR,FATAL
#被輸出,DEBUG,INFO將被屏蔽掉.
# A1 is set to be a ConsoleAppender.
#log4j中Appender有幾層如控制臺(tái)、文件、GUI組件、甚至是套接口服務(wù)器、NT的事件記錄器、UNIX Syslog守護(hù)進(jìn)程等
#ConsoleAppender輸出到控制臺(tái)
log4j.appender.A1=org.apache.log4j.ConsoleAppender
# A1 使用的輸出布局,其中l(wèi)og4j提供4種布局. org.apache.log4j.HTMLLayout(以HTML表格形式布局)
#org.apache.log4j.PatternLayout(可以靈活地指定布局模式),
#org.apache.log4j.SimpleLayout(包含日志信息的級(jí)別和信息字符串),
#org.apache.log4j.TTCCLayout(包含日志產(chǎn)生的時(shí)間、線程、類別等等信息)
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
#靈活定義輸出格式 具體查看log4j javadoc org.apache.log4j.PatternLayout
#d 時(shí)間 ....
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n
#R 輸出到文件 RollingFileAppender的擴(kuò)展,可以提供一種日志的備份功能。
log4j.appender.R=org.apache.log4j.RollingFileAppender
#日志文件的名稱
log4j.appender.R.File=log4j.log
#日志文件的大小
log4j.appender.R.MaxFileSize=100KB
# 保存一個(gè)備份文件
log4j.appender.R.MaxBackupIndex=1
log4j.appender.R.layout=org.apache.log4j.TTCCLayout
#log4j.appender.R.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n
配置根Logger,其語(yǔ)法為:
log4j.rootLogger = [ level ] , appenderName, appenderName, ...
level 是日志記錄的優(yōu)先級(jí)
appenderName就是指定日志信息輸出到哪個(gè)地方。您可以同時(shí)指定多個(gè)輸出目的地。
配置日志信息輸出目的地Appender,其語(yǔ)法為
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(控制臺(tái)),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天產(chǎn)生一個(gè)日志文件),
org.apache.log4j.RollingFileAppender(文件大小到達(dá)指定尺寸的時(shí)候產(chǎn)生一個(gè)新的文件),
org.apache.log4j.WriterAppender(將日志信息以流格式發(fā)送到任意指定的地方)
配置日志信息的格式(布局),其語(yǔ)法為:
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(包含日志信息的級(jí)別和信息字符串),
org.apache.log4j.TTCCLayout(包含日志產(chǎn)生的時(shí)間、線程、類別等等信息)
簡(jiǎn)單的Log4j使用只需要導(dǎo)入下面的包就可以了
// import log4j packages
import org.apache.log4j.Logger;
然后你需要在類的開頭寫上下面一句
private final static Logger log =
Logger.getLogger(ClassName.class);//注意這里寫的是當(dāng)前類的類名。
這樣你就有了一個(gè)日志對(duì)象叫做 log,這個(gè)log有以下集中用法
log.info();//一般信息
log.debug();//調(diào)試信息
log.error();//錯(cuò)誤信息
log.warn();//警告信息
log.fatal();//致命錯(cuò)誤信息
上面列出的就是所謂log4j的輸出級(jí)別,log4j建議只使用4個(gè)級(jí)別,它們從上到下分別為
ERROR、WARN、INFO、DEBUG,假設(shè)你定義的級(jí)別是info,那么error和warn的日志可以顯示而比他低的debug信息就不顯示了。
知道了上面的內(nèi)容還不夠,要想很好的使用log4j你還需要配置log4j的環(huán)境。因?yàn)槊總€(gè)log都可以通過(guò)配置它的屬性文件來(lái)達(dá)到控制日志輸出的格式的目的,下面我給出一個(gè)在weblogic下使用log4j的配置過(guò)程,首先給出這個(gè)配置文件的完整信息。
#log4j.rootLogger=INFO,A1,R //這一句指定了日志輸出的級(jí)別為info,A1和R分別代表日志輸出到什么地方。
log4j.category.hybl_wshabcm=debug,A1,R //這一句指定了日志具體輸出哪個(gè)包的信息,以及輸出位置
log4j.appender.A1=org.apache.log4j.ConsoleAppender //這里指定了日志輸出的第一個(gè)位置A1是控制臺(tái)ConsoleAppender
/*
*其中,Log4j提供的appender有以下幾種:
*org.apache.log4j.ConsoleAppender(控制臺(tái)),
*org.apache.log4j.FileAppender(文件),
*org.apache.log4j.DailyRollingFileAppender(每天產(chǎn)生一個(gè)日志文件),
*org.apache.log4j.RollingFileAppender(文件大小到達(dá)指定尺寸的時(shí)候產(chǎn)生一個(gè)新的文件),
*org.apache.log4j.WriterAppender(將日志信息以流格式發(fā)送到任意指定的地方)
*
*/
log4j.appender.A1.layout=org.apache.log4j.PatternLayout //指定A1的布局模式
/*
*其中,Log4j提供的layout有以下幾種:
×org.apache.log4j.HTMLLayout(以HTML表格形式布局),
*org.apache.log4j.PatternLayout(可以靈活地指定布局模式),
*org.apache.log4j.SimpleLayout(包含日志信息的級(jí)別和信息字符串),
*org.apache.log4j.TTCCLayout(包含日志產(chǎn)生的時(shí)間、線程、類別等等信息)
*/
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%c]-[%p] %m%n //指定日志的輸出格式
log4j.appender.R=org.apache.log4j.RollingFileAppender //指定以文件的方式輸出日志
log4j.appender.R.File=c:/sys.html //文件位置
log4j.appender.R.MaxFileSize=500KB //文件最大尺寸
log4j.appender.R.MaxBackupIndex=1 //備份數(shù)
log4j.appender.R.layout=org.apache.log4j.HTMLLayout //文件的格式為Html格式
#log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c] [%p] - %m%n
有了上面的這個(gè)文件我們還要在weblogic啟動(dòng)的時(shí)候通過(guò)一個(gè)類加載這個(gè)文件,可以使用下面的方式
1、加載配置文件的Servlet
package hybl_wshabcm.servelet;
import java.io.File;
import java.io.LineNumberReader;
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Vector;
import java.sql.Driver;
import java.sql.DriverManager;
// import servlet packages
import javax.servlet.http.HttpServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
// import log4j packages
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class SetupServlet extends HttpServlet{
public void init(ServletConfig config) throws ServletException{
super.init(config);
// first thing to do, is to set up the Driver that we might be using
// in case of JDBCAppender
try{
Driver d =
(Driver)(Class.forName(
"org.gjt.mm.mysql.Driver").newInstance());
DriverManager.registerDriver(d);
//加載JDBC驅(qū)動(dòng)程序,當(dāng)準(zhǔn)備將日志記錄到數(shù)據(jù)庫(kù)的時(shí)候可以使用
}catch(Exception e){ System.err.println(e); }
// next load up the properties
//啟動(dòng)時(shí)從web.xml中獲得配置文件的信息
String props = config.getInitParameter("props");
if(props == null || props.length() == 0 ||
!(new File(props)).isFile()){
System.err.println(
"ERROR: Cannot read the configuration file. " +
"Please check the path of the config init param in web.xml");
throw new ServletException();
}
}
public void destroy(){
super.destroy();
}
}
2、web.xml文件的部署
上面的這個(gè)Servlet在服務(wù)器啟動(dòng)的時(shí)候?qū)⒁恍┏跏蓟畔⒓虞d,那么如何在weblogic啟動(dòng)的時(shí)候?qū)⑦@個(gè)類也
加載呢?看一個(gè)例子
setup
hybl_wshabcm.servelet.SetupServlet//啟動(dòng)時(shí)裝載的類
props
/WEB-INF/log4j.properties//部署文件的位置
2//這句很重要
好了,現(xiàn)在你可以象開頭那樣使用log4j來(lái)記錄日志了