一、简?br /> 在程序中输出信息的目的有三:(x)一是监视程序运行情况;一是将E序的运行情况记录到日志文g中,以备来查看Q一是做试器。但信息输出的手D不仅限于System.out.println()或System.out.print()Q还有日志记录工具可以选择。与System.out.pringln()和System.out.print()相比Q日志记录工具可以控制输出别,q且可以在配|文件中对输出别进行设|,q样开发阶D늚信息在程序发布后可以通过讄输出U别来消除掉Q而无d代码q行修正?jin)。现在流行的日志记录工具很多Q?Log4J是其中的佼D?br /> Log4J是由著名开源组lApache推出的一ƾ日志记录工P供Java~码人员做日志输Z用,可以从网?a >http://logging.apache.org/log4j上免费获得,最新版?.2.11。获得logging-log4j-1.2.11.zip文g后,解压~,需要的是其中的log4j-1.2.11.jar文gQ将该文件放到特定的文g夹中备用Q我攑ֈ?jin)我机器的G:\YPJCCK\Log4J\lib文g夹中?br /> q里选择的IDE是Eclipse和JBuilder。Eclipse用的?.0.1加语a包,可以?a >www.eclipse.org|站上下载;JBuilder用的是JBuilder 2005?br />二、配|类?br /> 下面打开Eclipse或JBuilder?br /> 如果使用的是EclipseQ那么在Eclipse打开后,点击菜单"文g"->"新徏"->"目"Q打开"新徏目"对话框:(x)
请选中"Java目"Q点?下一?Q进?新徏Java目"对话框:(x)
在这个对话框中需要设|项目的名称以及(qing)目所在目录,我ؓ(f)自己的项目v名ؓ(f)Log4JTestQ目录ؓ(f)G:\YPJCCK\Log4J\Eclipse\ Log4JTest。设|好后点?下一?Q进入下一个窗口。在q个H口中选择名ؓ(f)"?的选项卡,然后点击"d外部JAR"按钮Q将保存于特定文件夹中的log4j-1.2.11.jar文g引用q来?/p>
讄好后Q点?完成"Q至此,已经具备?jin)在Eclipse下用Log4J的环境?br /> 如果使用的是JBuilderQ那么在JBuilder打开后,点击菜单"Tools"->"Configure" ->"Libraries"Q打开"Configure Libraries"对话框:(x)
点击"New"按钮Q打开"New Library Wizard"对话框:(x)
使用"Add"按钮保存于特定文g夹中的log4j-1.2.11.jar文g引用q来Qƈ讄NameQ即该类库的名字Q我Name讄?Log4J。设|好后点?OK"按钮Q回?Configure Libraries"对话框,再点?OK"按钮Q则JUnitcd已经被添加到JBuilder当中?br /> 下面l箋Q在JBuilder中创建项目。点击菜?File"->"New Project"Q打开"Project Wizard"对话框:(x)
在这个窗口中讄目名称?qing)存攄录,我的目名称仍?f)Log4JTestQ\径ؓ(f)G:/YPJCCK/log4J/JBuilder/Log4JTest。点?Next"q入下一个窗口:(x)
在这个窗口中选择"Required Libraries"选项卡,点击"Add"按钮Q将刚才讄的JUnit库引用进来。然后点?Next"按钮Q进入下一个窗口:(x)
在这个窗口中用鼠标点击Encoding下拉列表框,然后按一?G"键,选中相应选项Q此时该目的字W集p讄成GBK?jin)。如果做的是国内目Q这l对是个好习(fn)惯。最后点?Finish"Q项目创建完成?br />三、编写一个简单的CZ
在了(jin)解Log4J的用方法之前,先编写一个简单的CZQ以对Log4J有个感性认识?br />如果使用的是EclipseQ请点击"文g"->"新徏"->"c?Q打开"新徏Javac?对话框,讄包ؓ(f) piv.zheng.log4j.testQ名UCؓ(f)TestQƈ保"public static void main(String[] args)"选项选中Q如果用的是JBuilderQ请点击"File"->"New Class"Q打开"Class Wizard"对话框,讄Package为piv.zheng.log4j.testQClass name为TestQƈ保"Generate main method"选项选中。设|完成后Q点?OK"。代码如下:(x)
package piv.zheng.log4j.test;
import org.apache.log4j.Logger;
import org.apache.log4j.Level;
import org.apache.log4j.SimpleLayout;
import org.apache.log4j.ConsoleAppender;
public class Test {
public static void main(String[] args) {
SimpleLayout layout = new SimpleLayout();
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.setLevel(Level.FATAL);
log.debug("Here is DEBUG");
log.info("Here is INFO");
log.warn("Here is WARN");
log.error("Here is ERROR");
log.fatal("Here is FATAL");
}
}
xQ示例编写完成。请点击q行按钮旁边的倒三角,选择"q行?->"2 Java应用E序"QEclipseQ,或者在Testcȝ选项卡上点击鼠标右键Q在调出的快捯单中点击"Run using defaults"QJBuilderQ,q行E序Q观察从控制台输出的信息?br />四、Log4J入门
看过E序的运行效果后可能?x)奇怪,Z控制台只输出?FATAL - Here is FATAL"q样一条信息,而程序代码中的log.debug()、log.info(){方法也都设|了(jin)cM的内容,却没有被输出Q其实答案很单,但在公布之前Q先来了(jin)解一下Log4J的用?br /> 请先看前边的CZ代码Q会(x)发现Q示例中用到?jin)Logger、Level?ConsoleAppender、SimpleLayout{四个类。其中LoggercM用最多,甚至输出的信息也是在其对象log的fatalҎ(gu)中设|的Q那么LoggerI竟是做什么的呢?其实Logger是传说中的日志记录器(在Log4J中称为CategoryQ,创徏Ҏ(gu)有三Q?br /> 1Q根CategoryQ默认创建,获取Ҏ(gu)Q?/p>
Logger log = Logger.getRootLogger();
2Q用户创建的CategoryQ方法:(x)
Logger log = Logger.getLogger("test");
其中字符串test是ؓ(f)Category讑֮的名U。Category的名U允怋用Q何字W,但区分大写Q例如:(x)
Logger l1 = Logger.getLogger("x");
Logger l2 = Logger.getLogger("X");
l1和l2是两个CategoryQ而如果名U完全相同,例如Q?/p>
Logger l1 = Logger.getLogger("x");
Logger l2 = Logger.getLogger("x");
l1和l2是同一个Category。此外,W号"."在Category的名UC有特D作用,q一点将在后边介l?br /> 3Q与Ҏ(gu)2cMQ只是参数由字符串换成了(jin)cd象,其目的是通过cd象获取类的全名。这个方法比较常用,CZ中用的是q个Ҏ(gu)?br /> 那么Category是如何输Z息的呢?其实CZ中用到的debug、info、warn、error、fatal{五个方法都是用来输Z息的。什么,怎么q么多?原因很简单,Log4J支持分输出。Log4J的输出别有五个Q由低到高依ơ是DEBUGQ调试)(j)、INFOQ信息)(j)、WARNQ警告)(j)、ERRORQ错误)(j)和FATALQ致命)(j)Q分别与以上Ҏ(gu)对应。当输出U别讄为DEBUGӞ以上Ҏ(gu)都能够输Z息,当输出别设|ؓ(f)INFO Ӟ则只有debugҎ(gu)不能再输出信息Q依此类推,当输出别设|ؓ(f)FATALӞ只有fatalҎ(gu)可以输出信息?jin)。现在再回头看前边的问题Qؓ(f)何只有设|给fatalҎ(gu)的信息被输出׃隄解了(jin)Q示例中有这样一行代码:(x)
log.setLevel(Level.FATAL);
正是q行代码log对象的输出别设成了(jin)FATAL。在为log对象讄输出U别时用C(jin)Levelc,该类中定义了(jin)DEBUG、INFO、WARN?ERROR、FATAL{五个静(rn)态对象,与五个输出别相对应。此外,Levelq有两个Ҏ(gu)的静(rn)态对象ALL和OFFQ前者允许所有的Ҏ(gu)输出信息Q其U别其实比DEBUGq低Q后者则?x)禁止所有的Ҏ(gu)输出信息Q其U别比FATAL要高。除前边CZ中用到的五个Ҏ(gu)QLoggerq提供了(jin)q五个方法的重蝲Q以在输Z息的同时抛出异常Q以fatalҎ(gu)ZQ?/p>
log.fatal("Here is FATAL", new Exception("Exception"));
执行后输Z息:(x)
FATAL - Here is FATAL
java.lang.Exception: Exception
at piv.zheng.log4j.test.Test.main(Test.java:24)
其他Ҏ(gu)cM。此外,Loggerq提供了(jin)logҎ(gu)Q该Ҏ(gu)不针对Q何输出别,需要在调用时设|,例如Q?/p>
log.log(Level.FATAL, "Here is FATAL");
log.log(Level.FATAL, "Here is FATAL", new Exception("Exception"));
虽然一般情况下logҎ(gu)不如其它Ҏ(gu)方便Q但׃允许讄U别Q因此logҎ(gu)在很多时候反而比其它Ҏ(gu)更灵z,甚至可以在输出别ؓ(f)OFF时输Z息。不qlogҎ(gu)主要是给用户自定义的输出U别用的Q而且讄OFF输出U别的目的也Z(jin)不输ZQ何信息,因此请不要在logҎ(gu)中用OFF来输Z息?br /> 此外QCategory的输出别ƈ非必,若未讄Q子Category?x)默认用其父Category的输出别,若父Category也没讄Q就使用再上一UCategory的设|,直到根Category为止。根Category默认输出U别为DEBUGQ因此在CZ中,若将 "log.setLevel(Level.FATAL);"一行注释掉Q则所有方法都?x)输Z息?br /> 下面单介l一下Log4J?Category的承关pR其实在Log4J中Category之间是存在承关pȝQ根Category默认创徏Q是U别最高的CategoryQ用户创建的Category均承自它。而用户创建的Category之间也存在承关p,例如Q?/p>
Logger lx = Logger.getLogger("x");
Logger lxy = Logger.getLogger("xy");
Logger lx_y = Logger.getLogger("x.y");
Logger lx_z = Logger.getLogger("x.z");
Logger lx_y_z = Logger.getLogger("x.y.z");
其中的lx_y、lx_z是lx的子CategoryQ而lx_y_z是lx_y的子Category。但lxyq不是lx的子Category。也许有点ؕQ下面来一个一个看。首先看与lx_y、lx_z对应的Category的名U?x.y"?x.z"Q?."前边的是什么,"x"Q这说明与名UCؓ(f) "x"的Category对应lx是它们的父CategoryQ而与lx_y_z对应的Category的名U?x.y.z"Q最后一?."前边的是什么,"x.y"Q这说明lx_y是lx_y_z的父CategoryQ至于lxyQ由于与之对应的Category名称"xy"之间没有"."Q因此它是一个与lx同的CategoryQ其父Category是根Category器。此外还有一U情况,例如有一个名UCؓ(f)"a.b"?CategoryQ如果没有名UCؓ(f)"a"的CategoryQ那么它的父Category也是根Category。前边说q,"."在Category名称中有Ҏ(gu)作用Q其实它的作用就是ѝ至此,Z使用cd象来创徏Category也就不难理解?jin)?br /> 可是Q仅有Category是无法完成信息输出的Q还需要ؓ(f)CategorydAppenderQ即Category的输出源。前边的例子使用的是ConsoleAppenderQ即指定 Category信息输出到控制台。其实Log4J提供的Appender有很多,q里选择几常用的q行介绍?br /> 1Qorg.apache.log4j.WriterAppenderQ可以根据用户选择信息输出到Writer或OutputStream?br /> CZ代码Q?br /> SimpleLayout layout = new SimpleLayout ();
//向文件中输出信息QOutputStreamCZ
WriterAppender appender1 = null;
try {
appender1 = new WriterAppender(layout, new FileOutputStream("test.txt"));
}
catch(Exception ex) {}
//向控制台输出信息QW(xu)riterCZ
WriterAppender appender2 = null;
try {
appender2 = new WriterAppender(layout, new OutputStreamWriter(System.out));
}
catch(Exception ex) {}
//Category支持同时向多个目标输Z?br /> Logger log = Logger.getLogger(Test.class);
log.addAppender(appender1);
log.addAppender(appender2);
log.debug("output");
q个CZq一个示例修改而来Q没有设|输出别,而且向Category中添加了(jin)两个输出源,q行后会(x)在控制台中输?DEBUG - output"Qƈ在工E目录下生成test.txt文gQ该文g中也记录着"DEBUG - output"。若要将test.txt文g攑ֈ其它路径下,例如f:Q则?test.txt"改ؓ(f)"f:/test.txt"Q又如e:下的temp 文g夹,改?e:/temp/test.txt"。后边FileAppender、RollingFileAppender以及(qing) DailyRollingFileAppender讄目标文g时也都可以这h写?br /> 2Qorg.apache.log4j.ConsoleAppenderQ向控制台输Z息,l承?jin)WriterAppenderQ前边的CZ使用的就是它?br /> 3Qorg.apache.log4j.FileAppenderQ向文g输出信息Q也l承?jin)WriterAppender?br /> CZ代码Q?br /> SimpleLayout layout = new SimpleLayout();
//若文件不存在则创建文Ӟ若文件已存在则向文g中追加信?br /> FileAppender appender = null;
try {
appender = new FileAppender(layout, "test.txt");
} catch(Exception e) {}
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
q个CZ也由W一个示例修改而来Q运行后?x)在工程目录下生成test.txt文gQ该文g中记录着"DEBUG - output"。再ơ运行程序,查看文gQ则"DEBUG - output"有两行?br /> 另外QFileAppenderq有一个构造:(x)
FileAppender(Layout layout, String filename, boolean append)
与示例的cMQ只是多?jin)一个boolean型的参数append。append参数是个开养I用来讄当程序重启,而目标文件已存在Ӟ是向目标文gq加信息q是覆盖原来的信息,当gؓ(f)true时就q加Q这是FileAppender默认的,当gؓ(f)false时则覆盖。此外,FileAppenderq提供了(jin)setAppendҎ(gu)来设|a(chn)ppend开兟?br /> 4Qorg.apache.log4j.RollingFileAppenderQ承了(jin) FileAppenderQ也是向文g输出信息Q但文g大小可以限制。当文g大小过限制Ӟ该文件会(x)被{为备份文件或删除Q然后重新生成。文件的转换或删除与讄的备份文件最大数量有养I当数量大?时就转ؓ(f)备䆾文gQ否则(于{于0Q删除,默认的备份文件数量是1。{换备份文仉常简单,是修改文g名,在原文g名之后加?.1"Q例如文件test.txtQ{为备份文件后文g名ؓ(f)"test.txt.1"。但若同名的备䆾文g已存在,则会(x)先将该备份文件删除或更名Q这也与讄的备份文件最大数量有养I若达到最大数量就删除Q否则更名。若备䆾文g更名时也遇到同样情况Q则使用同样的处理方法,依此cLQ直到达到设|的备䆾文g最大数量。备份文件更名也很简单,是扩展名?Q例如test.txt.1文g更名后变为test.txt.2Q?test.txt.2文g更名后变为test.txt.3?br /> CZ代码Q?br /> SimpleLayout layout = new SimpleLayout();
//若文件不存在则创建文Ӟ若文件已存在则向文g中追加内?br /> RollingFileAppender appender = null;
try {
appender = new RollingFileAppender(layout, "test.txt");
} catch(Exception e) {}
//限制备䆾文g的数量,本例??br /> appender.setMaxBackupIndex(2);
//限制目标文g的大,单位字节Q本例ؓ(f)10字节
appender.setMaximumFileSize(10);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output0");
log.debug("output1");
log.debug("output2");
E序q行后,?x)在工程目录下生成test.txt、test.txt.1和test.txt.2三个文gQ其中test.txt内容为空Q而后两个文g则分别记录着"DEBUG - output2"?DEBUG - output1"Q这是怎么回事Q原来由于目标文件大被限制?0字节Q而三ơ用log.debugҎ(gu)输出的信息都过?0字节Q这样就D?jin)三ơ备份文件{换,所以test.txt内容为空。而备份文件最大数量被设ؓ(f)2Q因此第一ơ{换的备䆾文gp删掉?jin),而后两次的则保存下来。此外,׃ test.txt转换备䆾文g时是先{为test.txt.1Q再转ؓ(f)test.txt.2Q因此最后test.txt.2的内Ҏ(gu)"DEBUG - output1"Q而test.txt.1?DEBUG - output2"Q这点千万别弄?jin)?br /> 另外QRollingFileAppenderq提供了(jin)两个Ҏ(gu)Q?br /> Q?QsetMaxFileSizeQ功能与setMaximumFileSize一P但参数是字符Ԍ有两U情况:(x)一是仅由数字组成,默认单位为字节,例如"100"Q即表示限制文g大小?00字节Q一是由数字?qing)存储单位组成,例?1KB"?1MB"?1GB"Q其中单位不区分大小写,分别表示限制文g大小?K?M?G?br /> Q?QrollOverQ手动将目标文g转换为备份文Ӟ使用h较灵z,适用于复杂情c(din)?br /> CZ代码Q?br /> SimpleLayout layout = new SimpleLayout();
RollingFileAppender appender = null;
try {
appender = new RollingFileAppender(layout, "test.txt");
} catch(Exception e) {}
appender.setMaxBackupIndex(2);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output0");
appender.rollOver();
log.debug("output1");
appender.rollOver();
log.debug("output2");
appender.rollOver();
q里没限制目标文件大,但程序运行后Q效果与上例相同?br /> 5Qorg.apache.log4j.DailyRollingFileAppenderQ也l承?jin)FileAppenderQƈ且也是向文g输出信息Q但?x)根据设定的旉频率生成备䆾文g?br /> 旉频率格式介:(x)
'.'yyyy-MMQ按月生成,生成旉为每月最后一天午夜过后,例如test.txt?005q??1日午夜过后会(x)被更名ؓ(f)test.txt.2005-07Q然后重新生成?br /> '.'yyyy-wwQ按周生成,生成旉为每周六午夜q后Q例如test.txt?005q??3日午夜过后会(x)被更名ؓ(f)test.txt.2005-33Q?3表示当年W?3周?br /> '.'yyyy-MM-ddQ按天生成,生成旉为每天午夜过后,例如2005q??6日午夜过后,test.txt?x)被更名为test.txt.2005-08-16?br /> '.'yyyy-MM-dd-aQ也是按天生成,但每天会(x)生成两次Q中?2:00q后一ơ,午夜q后一ơ,例如test.txt?005q??6 ?2:00q后?x)被更名为test.txt.2005-8-16-上午Q午夜过后会(x)被更名ؓ(f)test.txt.2005-8-16-下午?br /> '.'yyyy-MM-dd-HHQ按时生成Q例如test.txt?005q??6?2:00q后?x)被更名为test.txt.2005-8-16-11?br /> '.'yyyy-MM-dd-HH-mmQ按分钟生成Q例如test.txt?005q??6?2:00q后?x)被更名为test.txt.2005-8-16-11-59?br /> CZ代码Q?br /> SimpleLayout layout = new SimpleLayout();
DailyRollingFileAppender appender = null;
try {
appender = new DailyRollingFileAppender(layout, "test.txt", "'.'yyyy-MM-dd-HH-mm");
} catch(Exception e) {}
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
~码完成后运行程序,{一分钟后再ơ运行,׃我是?005q??7?5:42分第一ơ运行程序的Q因此工E目录下最l有两个文gtest.txt和test.txt.2005-08-17-15-42?br /> 6Qorg.apache.log4j.AsyncAppenderQ用于管理不同类型的AppenderQ也能实现同时向多个源输Z息,但其执行是异步的?br /> CZ代码Q?br /> SimpleLayout layout = new SimpleLayout();
//向控制台输出
ConsoleAppender appender1 = null;
try {
appender1 = new ConsoleAppender(layout);
} catch(Exception e) {}
//向文件输?br /> FileAppender appender2 = null;
try {
appender2 = new FileAppender(layout, "test.txt");
} catch(Exception e) {}
//使用AsyncAppender实现同时向多个目标输Z?br /> AsyncAppender appender = new AsyncAppender();
appender.addAppender(appender1);
appender.addAppender(appender2);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
此外QAsyncAppender和Logger都提供了(jin)更多的方法来理AppenderQ例如getAppender?getAllAppenders、removeAppender和removeAllAppendersQ分别用来获取指定的Appender、获取全?Appender、移除指定的Appender以及(qing)U除全部Appender?br /> 7Qorg.apache.log4j.jdbc.JDBCAppenderQ将信息输出到数据库?br /> CZ代码Q?br /> JDBCAppender appender = new JDBCAppender();
appender.setDriver("com.mysql.jdbc.Driver");
appender.setURL("jdbc:mysql://localhost:3306/zheng");
appender.setUser("root");
appender.setPassword("11111111");
appender.setSql("insert into log4j (msg) values ('%m')");
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
q里使用的数据库是MySQL 5.0.4betaQ用户名rootQ密?1111111Q我在其中徏?jin)一个库zhengQ包含表log4jQ该表只有一个字DmsgQ类型ؓ(f)varchar(300)。此外,本例用到的JDBC驱动可以?a >http://dev.mysql.com/downloads/connector/j/3.1.html下蝲Q版?.1.8aQ下载mysql-connector-java-3.1.8a.zip文g后解压羃Q需要其中的mysql-connector- java-3.1.8-bin.jar文g。下面再来看代码。由于JDBCAppender内部默认使用PatternLayout格式化输Z息,因此q里没用到SimpleLayoutQ而appender.setSql所讄的SQL语句是PatternLayout所需的格式化字符Ԍ故此其中才有"%m"q样的字W,有关PatternLayout的具体内容后边介l。执行后Q表log4j增加一条记录,内容?output"?br /> 8Qorg.apache.log4j.nt.NTEventLogAppenderQ向Windows NTpȝ日志输出信息?br /> CZ代码Q?br /> SimpleLayout layout = new SimpleLayout();
NTEventLogAppender appender = new NTEventLogAppender("Java", layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
注意Q要完成此示例,q需向C:\WINNT\system32文g夹(我的操作pȝ装在?jin)C:\Q中复制一个名?NTEventLogAppender.dll的文件。如果跟我一L(fng)的是Log4J 1.2.11Q实在对不住QLog4J 1.2.11q未提供该文件。虽然logging-log4j-1.2.11.zip文g解压~后Q其下的src\java\org\apache\ log4j\nt文g夹中有一个make.bat文g执行后可以编译出该文Ӟ但还需要配|,很麻?ch)。还好,条条大道通罗马,1.2.11不行Q就?1.2.9Q可以从http://apache.justdn.org/logging/log4j/1.2.9下蝲Q下载后解压~logging-log4j-1.2.9.zip文gQ在其下的src\java\org\apache\log4j\nt文g夹中扑ֈ NTEventLogAppender.dllQ复制过d可以?jin)。程序执行后Q打开"事g查看?Q选择"应用E序日志"Q其中有一条来源ؓ(f)Java的记录,q条记录是刚才输出的信息了(jin)?br /> 9Qorg.apache.log4j.lf5.LF5AppenderQ执行时?x)弹Z个窗口,信息在该H口中以表格的Ş式显C?br /> CZ代码Q?br /> LF5Appender appender = new LF5Appender();
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
׃LF5Appender不需要Layout格式化输Z息,因此q里没有讄。此外LF5Appenderq提供了(jin)一个setMaxNumberOfRecordsҎ(gu)Q用来限制信息在表格中显C的行数?br /> 10Qorg.apache.log4j.net.SocketAppenderQ以套接字方式向服务器发送日志,然后由服务器信息输出?br /> CZ代码Q?br /> //指定要连接的服务器地址?qing)端口,q里使用的是本机9090端口
SocketAppender appender = new SocketAppender("localhost", 9090);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
SocketAppender不需要设|LayoutQ因为SocketAppender不负责输Z息。那么如何看C息输出的效果呢?q就需要SocketServer和SimpleSocketServer?jin)?br /> CZ代码1Q?br /> package piv.zheng.log4j.test;
import org.apache.log4j.net.SocketServer;
public class TestServer {
public static void main(String[] args) {
SocketServer.main(new String[]{"9090", "test.properties", "G:/YPJCCK/Log4J"});
}
}
q是SocketServer的示例。SocketServer只有一个静(rn)态方法mainQ该Ҏ(gu)意味着SocketServer不仅可以在代码中被调用,也可以用java命o(h)执行。mainҎ(gu)只有一个参敎ͼ是个字符串数l,但要求必L三个元素Q元素一用来指定端口Q本例ؓ(f)9090Q元素二用来指定输出信息旉要的配置文gQ该文g攑֜工程目录下,本例使用的test.properties内容如下Q?br /> log4j.rootLogger=, console
log4j.appender.console =org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.SimpleLayout
该配|指定SocketServer使用ConsoleAppender以SimpleLayout格式输出信息Q元素三用来指定一个\径,以存?lcf 文gQ我指定的是本机的G:/YPJCCK/Log4J文g夏V?lcf文g也是输出信息时用的配置文gQ格式与元素二所指定的配|文件一P?test.properties是默认配|文Ӟ卛_.lcf文g找不到时才用。那?lcf文g如何命名呢?其实.lcf文g的名Uƈ不是随意L(fng)Q当SocketAppender与SocketServer建立q接ӞSocketServer׃(x)获得SocketAppender所在计机的IP 地址与网lIDQƈ其格式化成"|络ID/IP地址"q样的字W串Q然后获取其中的|络ID作ؓ(f).lcf文g的主名,例如 "zhengyp/127.0.0.1"Q其中的"zhengyp"是L件名Q而后再根据这个文件名来调用相应的.lcf文g。这意味着对不同的计算机可以提供不同的配置文gQ信息输出时有不同的效果。此外,SocketServerq默认了(jin)一个名为generic.lcf的文Ӟ用于处理|络ID 获取不到或其他情况,本例是用的就是这个文Ӟ内容如下Q?br /> log4j.rootLogger=, console
log4j.appender.console =org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%m%n
该配|指定SocketServer使用ConsoleAppender以PatternLayout格式输出信息。运行程序时请先q行 SocketServerQ再q行SocketAppender。SocketAppenderq行l束后,可以从SocketServer的控制台看到输出的信息了(jin)?br /> CZ代码2Q?br /> package piv.zheng.log4j.test;
import org.apache.log4j.net.SimpleSocketServer;
public class TestServer {
public static void main(String[] args) {
SimpleSocketServer.main(new String[]{"9090", "test.properties"});
}
}
q是SimpleSocketServer的示例,与SocketServer相比Q只允许指定一个默认的配置文gQ而无法对不同计算Z用不同的配置文g?br /> 11Qorg.apache.log4j.net.SocketHubAppenderQ也是以套接字方式发送日志,但与SocketAppender相反QSocketHubAppender是服务器端,而不是客L(fng)?br /> CZ代码Q?br /> //指定服务器端口,q里使用的是本机9090端口
SocketHubAppender appender = new SocketHubAppender(9090);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
while (true) {
Thread.sleep(1000);
log.debug("output"); //输出信息
}
׃SocketHubAppender一旦运行就开始发送消息,而无论有无接收者,因此q里使用?jin)while语句q将条g设ؓ(f)true以保证程序持l运行。不qؓ(f)?jin)保证性能Q这里还使用?jin)Thread.sleep(1000)Q这L(fng)序每循环一ơ都休眠1U,如果机器性能不好Q还可以D的再大些。此外,׃SocketHubAppender也不负责输出信息Q因此同样不需要设|Layout。那么如何看C息输出的效果呢?q里我自己写?jin)个客户端程序,代码如下Q?br /> package piv.zheng.log4j.test;
import java.net.Socket;
import java.lang.Thread;
import org.apache.log4j.LogManager;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.net.SocketNode;
public class TestClient {
public static void main(String[] args) throws Exception {
//创徏客户端套接字对象
Socket s = new Socket("localhost", 9090);
//调用配置文g
PropertyConfigurator.configure("test.properties");
//从套接字中恢复LoggerQƈ输出信息
new Thread(new SocketNode(s, LogManager.getLoggerRepository())).start();
}
}
׃SocketHubAppender与SocketAppender一P发送的也是SocketNode对象Q因此编写该E序时参考了(jin) SocketServer的源码。此外,q里的配|文件直接用了(jin)上例的test.properties文g。运行程序时请先q行 SocketHubAppenderQ再q行客户端程序,然后从客L(fng)的控制台可以看到效果了(jin)?br /> 13Qorg.apache.log4j.net.TelnetAppenderQ与SocketHubAppender有些cMQ也是作为服务器发送信息,但TelnetAppender发送的不是SocketNode对象Q而是Category输出的结果?br /> CZ代码Q?br /> SimpleLayout layout = new SimpleLayout();
TelnetAppender appender = new TelnetAppender();
appender.setLayout(layout); //讄Layout
appender.setPort(9090); //讄端口?br /> appender.activateOptions(); //应用讄
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
while (true) {
java.lang.Thread.sleep(1000);
log.debug("output"); //输出信息
}
//appender.close();
注意最后一行被注释掉的代码Q若该行代码执行Q则TelnetAppender的资源会(x)被清理,从而导致TelnetAppender无法l箋q行。那么如何看C息输出的效果呢?q里提供两种Ҏ(gu)Q方法一Q用Telnet工具Q我使用的就是Windows自带的Telnet。运?TelnetAppenderE序后,点击[开始]菜单->[q行]Q在"q行"框中输入"telnet"Q回车,telnet客户端弹出,q是一个命令行E序Q输入命?open localhost 9090"Q回车,然后可以看到效果了(jin)。方法二Q自己写E序Q代码如下:(x)
package piv.zheng.log4j.test;
import java.net.*;
import java.io.*;
public class TestClient {
public static void main(String[] args) throws Exception {
//创徏客户端套接字对象
Socket s = new Socket("localhost", 9090);
//BufferedReader与Socketl定Q以输出Socket获得的信?br /> BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
//获得信息q输?br /> String line = in.readLine();
while (line != null) {
System.out.println(line);
line = in.readLine();
}
}
}
13Qorg.apache.log4j.net.SMTPAppenderQ向指定的电(sh)子邮件发送信息,但只能发送ERROR和FATALU别的信息,而且q没提供w䆾验证功能?br /> CZ代码Q?br /> SimpleLayout loyout = new SimpleLayout();
SMTPAppender appender = new SMTPAppender();
appender.setLayout(loyout); //讄Layout
appender.setFrom("zhengyp@126.com"); //讄发g?br /> appender.setSMTPHost("smtp.126.com"); //讄发送邮件服务器
appender.setTo("zhengyp@126.com"); //讄收g?br /> appender.setSubject("Log4J Test"); //讄邮g标题
appender.activateOptions(); //应用讄
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("Here is DEBUG");
log.info("Here is INFO");
log.warn("Here is WARN");
log.error("Here is ERROR");
log.fatal("Here is FATAL");
要运行此CZQ还需要JavaMail 和JAFQ前者是Sun推出的电(sh)子邮件类库,可以?a >http://java.sun.com/products/javamail/downloads/index.html下蝲Q最新版?.3.3Q下载j(lu)avamail-1_3_3-ea.zip压羃包后需要其中的mail.jar文gQ后者全U是JavaBeans Activation FrameworkQ提供了(jin)对输入Q意数据块的支持,q能相应地对其进行处理,可以?a >http://www.sun.com/download中找刎ͼ最新版?.1Q下载j(lu)af-1_1-ea.zip压羃包后需要其中的activation.jar文g。不q,E序q行后会(x)抛出两次异常Q分别是log.error和log.fatalҎ(gu)D的,p|的原因很单,我用的邮件服务器需要n份验证?br /> 14Qpiv.zheng.log4j.test.SMTPAppenderQ自定义的,依照Log4J提供的SMTPAppender修改而来Q增加了(jin)w䆾验证功能QƈL?jin)对U别的限制。由于代码太长,所以放C(jin)另一文章《自定义SMTPAppender的源码》中Q有兴趣的请自行L看?br /> CZ代码Q?br /> SimpleLayout layout = new SimpleLayout();
SMTPAppender appender = new SMTPAppender(layout);
appender.setFrom("zhengyp@126.com"); //发g?br /> appender.setSMTPHost("smtp.126.com"); //发送邮件服务器
appender.setTo("zhengyp@126.com"); //收g?br /> appender.setSubject("Log4J Test"); //邮g标题
appender.setAuth("true"); //w䆾验证标识
appender.setUsername("zhengyp"); //用户?br /> appender.setPassword("1111111"); //密码
appender.activateOptions(); //应用讄
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
同样需要JavaMail 和JAF。程序运行后?x)发送一邮Ӟ快去查看一下自q邮箱吧^_^
此外QLog4Jq提供了(jin)SyslogAppender、JMSAppenderQ均在org.apache.log4j.net包下Q以?qing)更多?AppenderQ或者用来向Unix操作pȝ的syslogd服务发送信息,或者通过JMS方式发送信息,或者以其他方式发送信息。由于条件有玎ͼ׃再介l了(jin)?br /> 不过Q在前边的示例中q用了(jin)SimpleLayout和PatternLayout来格式化输出的信息,q里也简单介l一下?br /> 1Qorg.apache.log4j.SimpleLayoutQ一直用的就是它Q输出的格式比较单,是"U别 - 信息"?br /> 2Qorg.apache.log4j.HTMLLayoutQ以HTML格式输出信息?br /> CZ代码Q?br /> HTMLLayout layout = new HTMLLayout();
layout.setTitle("Log4J Test"); //HTML|?br />
FileAppender appender = null;
try {
appender = new FileAppender(layout, "test.html");
} catch(Exception e) {}
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
E序q行后会(x)在工E目录下生成一个HTML,可以用浏览器来查看?br /> 3Qorg.apache.log4j.xml.XMLLayoutQ以XML格式输出信息?br /> CZ代码Q?br /> XMLLayout layout = new XMLLayout();
FileAppender appender = null;
try {
appender = new FileAppender(layout, "test.xml");
} catch(Exception e) {}
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
E序q行后会(x)在工E目录下生成一个test.xml文g?br /> 4Qorg.apache.log4j.TTCCLayoutQ输Z息的同时输出日志产生旉、相关线E及(qing)Category{信息?br /> CZ代码Q?br /> TTCCLayout layout = new TTCCLayout();
//是否打印与TTCCLayout兌的Category的名Uͼ默认为trueQ表C打?br /> layout.setCategoryPrefixing(true);
//是否打印当前U程Q默认ؓ(f)trueQ表C打?br /> layout.setThreadPrinting(true);
//是否打印输出和当前线E相关的NDC信息Q默认ؓ(f)trueQ表C打?br /> layout.setContextPrinting(true);
//讄日期旉格式
layout.setDateFormat("iso8601");
//讄时区
layout.setTimeZone("GMT+8:00");
//讄时区后需要调用此Ҏ(gu)应用讄
layout.activateOptions();
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
注意QTTCCLayout输出的时间格式及(qing)时区是可以设|的Q?br /> Q?QsetDateFormatQ设|日期时间格式,有五个常用|(x)"NULL"Q表CZ输出Q?RELATIVE"Q输Z息所用的旉Q以毫秒为单位,默认使用该|"ABSOLUTE"Q仅输出旉部分Q?DATE"Q按当前所在地区显C日期和旉Q?ISO8601"Q按ISO8601标准昄日期和时间。这些字W串不区分大写。此外,q可以用时间模式字W来格式化日期时_(d)详细内容请参考J2SE文档中的 java.text.SimpleDateFormatcR?br /> Q?QsetTimeZoneQ设|时区,详细内容请参考J2SE文档中的java.util.TimeZonecdjava.util.SimpleTimeZonecR但h意,当日期格式ؓ(f)"RELATIVE"Ӟ讄时区?x)造成冲突?br /> 5Qorg.apache.log4j.PatternLayoutQ用模式字符灉|指定信息输出的格式?br /> CZ代码Q?br /> String pattern = "Logger: %c %n"
+ "Date: %d{DATE} %n"
+ "Message: %m %n";
PatternLayout layout = new PatternLayout(pattern);
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
模式字符串简介:(x)
%cQCategory名称。还可以使用%c{n}的格式输出Category的部分名Uͼ其中n为正整数Q输出时?x)从Category名称的右侧v?n?."Q然后截取第n?."右侧的部分输出,例如Category的名UCؓ(f)"x.y.z"Q指定格式ؓ(f)"%c{2}"Q则输出"y.z"?br /> %CQ输Z息时Category所在类的名Uͼ也可以?C{n}的格式输出?br /> %dQ输Z息的旉Q也可以?d{FormatString}的格式输出,其中FormatString的D参考TTCCLayout的setDateFormatҎ(gu)Q但NULL和RELATIVE?d中无法用?br /> %FQ输Z息时Category所在类文g的名U?br /> %lQ输Z息时Category所在的位置Q?%C.%M(%F:%L)"可以产生同样的效果?br /> %LQ输Z息时Category在类文g中的行号?br /> %mQ信息本w?br /> %MQ输Z息时Category所在的Ҏ(gu)?br /> %nQ换行符Q可以理解成回R?br /> %pQ日志别?br /> %rQ输Z息所用的旉Q以毫秒为单位?br /> %tQ当前线E?br /> %xQ输出和当前U程相关的NDC信息?br /> %XQ输Z当前现成相关的MDC信息?br /> %%Q输??br />此外Q还可以?与模式字W之间加上修饰符来设|输出时的最宽度、最大宽度及(qing)文本寚w方式Q例如:(x)
%30d{DATE}Q按当前所在地区显C日期和旉Qƈ指定最宽度ؓ(f)30Q当输出信息于30个字W时?x)补以空格ƈ叛_齐?br /> %-30d{DATE}Q也是按当前所在地区显C日期和旉Q指定最宽度ؓ(f)30Qƈ在字W少?0时补以空|但由于用了(jin)"-"Q因此对齐方式ؓ(f)左对齐,与默认情况一栗?br /> %.40d{DATE}Q也是按当前所在地区显C日期和旉Q但指定最大宽度ؓ(f)40Q当输出信息多于40个字W时?x)将左边多出的字W截掉。此外,最大宽度只支持默认的左寚w方式Q而不支持叛_齐?br /> %30.40d{DATE}Q如果输Z息少?0个字W就补空格ƈ叛_齐,如果多于40个字W,将左边多出的字W截掉?br /> %-30.40d{DATE}Q如果输Z息少?0个字W就补空格ƈ左对齐,如果多于40个字W,将左边多出的字W截掉?br />五、Log4Jq阶
?jin)解以上内容后,已l初步掌握Log4J?jin),但要想灵zM用Log4JQ则q需要了(jin)解其配置功能。这里简单介l一下?br /> 1Qorg.apache.log4j.BasicConfiguratorQ默认用ConsoleAppender以PatternLayout Q用PatternLayout.TTCC_CONVERSION_PATTERNQ即"%r [%t] %p %c %x - %m%n"格式Q输Z息?br /> CZ代码Q?br /> BasicConfigurator.configure();
Logger log = Logger.getLogger(Test.class);
log.debug("output");
注意QBasicConfigurator?qing)其它Configurator其实都只Ҏ(gu)Categoryq行配置Q但׃用户创徏的Category?x)承根Category的特性(声明Q许多资料介lCategoryl承关系旉主要在讨出别,而事实上QCategory间承的不仅是输出别,所有特性都可以l承Q,因此输出时仍?x)显CBasicConfigurator配置的效果。此外,q可以用configureҎ(gu)指定AppenderQ以自定义输出。BasicConfigurator允许同时指定多个Appender?br /> CZ代码Q?br /> SimpleLayout layout1 = new SimpleLayout();
ConsoleAppender appender1 = new ConsoleAppender(layout1);
BasicConfigurator.configure(appender1);
String pattern = "Logger: %c %n"
+ "Date: %d{DATE} %n"
+ "Message: %m %n";
PatternLayout layout2 = new PatternLayout(pattern);
FileAppender appender2 = null;
try {
appender2 = new FileAppender(layout2, "test.log", false);
}
catch(Exception e){}
BasicConfigurator.configure(appender2);
Logger log = Logger.getLogger(Test.class);
log.debug("output");
q里用BasicConfigurator指定?jin)两个AppenderQ即ConsoleAppender和FileAppenderQ程序运行后信息?x)在以SimpleLayout输出到控制台的同时以PatternLayout输出到test.log文g。若要清除这些AppenderQ可以调?BasicConfigurator的resetConfigurationҎ(gu)?br /> 2Q?org.apache.log4j.PropertyConfiguratorQ调用文本配|文件输Z息,通常使用.properties文g。配|文件以"??的Ş式保存数据,注释?#"开头。PropertyConfigurator和配|文件在介绍SocketAppender?SocketHubAppender时曾提到q。用PropertyConfigurator可以避免编码?br /> CZ代码Q?br /> PropertyConfigurator.configure("test.properties");
Logger log = Logger.getLogger(Test.class);
log.debug("output");
要完成该CZQ还需要在工程目录下创Z个test.properties文gQ内容如下:(x)
##讄根CategoryQ其值由输出U别和指定的Appender两部分组?br /> #q里讄输出U别为DEBUG
log4j.rootLogger=DEBUG,appender
##输出信息到控制台
#创徏一个名为appender的AppenderQ类型ؓ(f)ConsoleAppender
log4j.appender.appender=org.apache.log4j.ConsoleAppender
#讄a(chn)ppender以SimpleLayout输出
log4j.appender.appender.layout=org.apache.log4j.SimpleLayout
此外QPropertyConfigurator也允许同时指定多个AppenderQ例如:(x)
#q里没有讄输出U别Q但指定?jin)两个Appender
log4j.rootLogger=,appender1,appender2
#输出信息到控制台
log4j.appender.appender1=org.apache.log4j.ConsoleAppender
log4j.appender.appender1.layout=org.apache.log4j.SimpleLayout
#输出信息到文?br /> log4j.appender.appender2=org.apache.log4j.FileAppender
log4j.appender.appender2.File=test.log
log4j.appender.appender2.Append=false
log4j.appender.appender2.layout=org.apache.log4j.PatternLayout
log4j.appender.appender2.layout.ConversionPattern=Logger: %c %nDate: %d{DATE} %nMessage: %m %n
关于更多配置Q网上示例很多,q里不再赘述。但要说明一件事Q就是配|文件中的键是怎么来的。参照后一个示例,查看 PropertyConfigurator源码Q会(x)发现"log4j.rootLogger"是定义好的,只能照写Q?log4j.appender" 字样也可以找刎ͼ与指定的Appender名称appender1、appender2联系hQlog4j.appender.appender1?log4j.appender.appender2也就不难理解?jin);再看下去Q还能找?prefix + ".layout""Q这样log4j.appender.appender1.layout也有?jin);可?log4j.appender.appender2.File 和log4j.appender.appender2.Append呢?q记得前边介lFileAppender时曾提到的setAppendҎ(gu)吗?其实FileAppenderq有个getAppendҎ(gu)Q这说明FileAppenderhAppend属性。那么F(tun)ile呢?当然也是 FileAppender的属性了(jin)。至于log4j.appender.appender2.layout.ConversionPattern也一P只不qFileAppender换成?jin)PatternLayout。其实别的Appender和Layout的属性也都是q样定义成键来进行设|的。此外,定义键时Q属性的首字母不区分大小写,例如"File"Q也可以写成"file"?br /> 3Q?org.apache.log4j.xml.DOMConfiguratorQ调用XML配置文g输出信息。其定义文档是log4j- 1.2.11.jar中org\apache\log4j\xml包下的log4j.dtd文g。与PropertyConfigurator相比Q?DOMConfiguratorg是趋ѝ?br /> CZ代码Q?br /> DOMConfigurator.configure("test.xml");
Logger log = Logger.getLogger(Test.class);
log.debug("output");
要完成该CZQ也需要在工程目录下创Z个test.xml文gQ内容如下:(x)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- 输出信息到控制台
创徏一个名为appender的AppenderQ类型ؓ(f)ConsoleAppender -->
<appender name="appender" class="org.apache.log4j.ConsoleAppender">
<!-- 讄a(chn)ppender以SimpleLayout输出 -->
<layout class="org.apache.log4j.SimpleLayout"/>
</appender>
<!-- 讄根CategoryQ其值由输出U别和指定的Appender两部分组?br /> q里讄输出U别为DEBUG -->
<root>
<priority value ="debug" />
<appender-ref ref="appender"/>
</root>
</log4j:configuration>
此外QDOMConfigurator也允许同时指定多个AppenderQ例如:(x)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- 输出信息到控制台 -->
<appender name="appender1" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.SimpleLayout"/>
</appender>
<!-- 输出信息到文?-->
<appender name="appender2" class="org.apache.log4j.FileAppender">
<param name="File" value="test.log"/>
<param name="Append" value="false"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="Logger: %c %nDate: %d{DATE} %nMessage: %m %n"/>
</layout>
</appender>
<!-- q里没有讄输出U别Q但指定?jin)两个Appender -->
<root>
<appender-ref ref="appender1"/>
<appender-ref ref="appender2"/>
</root>
</log4j:configuration>
׃以上两个CZ是在PropertyConfigurator的两个示例基上改的,而且也写?jin)注释,因此q里只简单介l一?lt;param> 标记?lt;param>标记有两个属性,name和valueQ前者的g是Appender或Layout的属性名Q作用与 log4j.appender.appender2.Fileq样的键一栗设|时Q首字母同样不区分大写Q例?File"也可以写?file"。此外还h意,使用q两DXML代码时应中文注释去掉,或者把<?xml version="1.0" encoding="UTF-8" ?>中的UTF-8Ҏ(gu)GBK或GB2312Q否则会(x)D错误。这里用的UTF-8是XML默认的字W集?br /> 4Q?org.apache.log4j.lf5.DefaultLF5ConfiguratorQ默认用LF5Appender来输Z息,需要调?log4j-1.2.11.jar中org\apache\log4j\lf5\config包下的defaultconfig.properties文g?br /> CZ代码Q?br /> try {
DefaultLF5Configurator.configure();
}
catch(Exception e){}
Logger log = Logger.getLogger(Test.class);
log.debug("output");
下面讨论另外一个话题:(x)Diagnostic Context。Diagnostic Context意ؓ(f)诊断环境Q针对于多用户ƈ发环境,在这U环境下Q通常需要对每个客户端提供独立的U程以处理其hQ此时若要在日志信息中对客户端加以区分,为每个线E分别创建Category是个办法。但q样做ƈ不高效,反而会(x)D大量资源被占用。Diagnostic Context所要解决的是q个问题。Diagnostic Context?x)?f)当前U程提供一定空_(d)然后信息保存到该空间供Category调用。与创徏一个Category相比Q这点信息所占的资源自然要少得多?br /> 1Qorg.apache.log4j.NDC。NDC是Nested Diagnostic Context的简写,意ؓ(f)嵌套诊断环境Q用时提供一个堆栈对象来保存信息。堆栈的特点是数据后q先出、先q后出,x理堆栈时Q后保存的数据会(x)被先清掉Q而先保存的数据则被后清掉?br /> CZ代码Q?br /> PatternLayout layout = new PatternLayout("%m %x%n");
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
String tmp = "zhengyp"; //模拟从客L(fng)获取的信?br /> log.debug("Start");
NDC.push(tmp); //d信息到堆栈中
log.debug("Before");
NDC.pop(); //信息从堆栈中移?br /> log.debug("After");
NDC.remove(); //当前线E移除,退出NDC环境
log.debug("End");
q里使用?jin)PatternLayout来格式化信息Q其模式字符%x是用来输出NDC信息的。程序运行后?x)输出如下内容?x)
Start
Before zhengyp
After
End
可以看到Q第二行输出时由于已向堆栈中d?jin)信息,因?zhengyp"也会(x)同时输出Q而第三行输出时由于信息已被移除,因此没再输?zhengyp"。不q这个示例仅单演CZ(jin)NDC的用法,而没有显C出NDC的堆栈特性,所以下面再提供一个示例,代码如下Q?br /> TTCCLayout layout = new TTCCLayout();
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("Start");
NDC.push("zhengyp"); //d信息到堆栈中
log.debug("Test1");
NDC.push("192.168.0.1"); //向堆栈中q加信息
log.debug("Test2");
NDC.pop(); //从堆栈中U除信息Q但U除的只是最后的信息
log.debug("Test3");
NDC.pop(); //再次从堆栈中U除信息
log.debug("Test4");
log.debug("End");
q里格式化输Z息用的是TTCCLayoutQ还记得其setContextPrintingҎ(gu)吗?E序q行后,从输出的信息可以看到效果了(jin)。此外,NDCq提供了(jin)其他Ҏ(gu)Q?br /> Q?QgetQ获取堆栈中的全部信息。以上例ZQ当输出Test2Ӟ使用该方法会(x)获得"zhengyp 192.168.0.1"?br /> Q?QpeekQ获取堆栈中最后的信息。仍以上例ؓ(f)例,当输出Test1时会(x)获得"zhengyp"QTest2时ؓ(f)"192.168.0.1"Q而当输出Test3时由?192.168.0.1"已被U除Q?zhengyp"又成?jin)最后的信息Q因此获得的仍是"zhengyp"?br /> Q?QclearQ清I堆栈中的全部信息?br /> Q?QsetMaxDepthQ设|堆栈的最大深度,卛_前的信息可以保留多少Q对之后q加的信息没有媄(jing)响。当需要一ơ清掉多条信息时Q用setMaxDepth?x)比多次调用pop方便?br /> 2Qorg.apache.log4j.MDC。MDC是Mapped Diagnostic Context的简写,意ؓ(f)映射诊断环境Q提供了(jin)一个Map对象来保存信息。Map对象使用Key、Value的Ş式保存倹{?br /> CZ代码Q?br /> PatternLayout layout = new PatternLayout("%m %X{name} %X{ip}%n");
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("Start");
//d信息到Map?br /> MDC.put("name", "zhengyp1");
MDC.put("ip", "192.168.1.1");
log.debug("Test1");
//d信息到Map中,若Key重复Q则覆盖之前的?br /> MDC.put("name", "zhengyp2");
MDC.put("ip", "192.168.1.2");
log.debug("Test2");
//信息从Map中移除,此时信息不再输出
MDC.remove("name");
MDC.remove("ip");
log.debug("End");
q个CZ演示?jin)MDC的基本用法,格式化信息用的也是PatternLayoutQ模式字Wؓ(f)"%X"Q其格式必须?%X{Key}"。其中Key是?Map对象d信息时putҎ(gu)所用的KeyQ这里ؓ(f)name和ip。由于可以?%X{Key}"输出信息Q因此MDC使用h?x)比NDC更灵zR此外,MDCq提供了(jin)getҎ(gu)来获取指定Key的信息?br />六、小l?br /> 用了(jin)q半个月Q终于大概掌握了(jin)Log4J。由于本文是边学边写的,目的是将Log4J的用法记录下来,而非提供一份中文参考,因此内容q不l致Q但量提供?jin)示例。不q到最后才发现Q示例存在问题,其实Logger做ؓ(f)cȝstatic成员比较恰当Q而我Z(jin)图方便,竟直接写C(jin)mainҎ(gu)中,q一点还h意?br /> 此外Q这里再推荐一下《The Complete log4j Manual》,是对Log4J较详l的介绍Q在|上可以扑ֈQ只不过是英文的?/p>
public abstract class SellVehicleTemplate {
public void sellVehicle() {
getCustomerInfo();
testDriveVehicle();
negotiateSale();
arrangeFinancing();
closeSale();
}
public abstract void getCustomerInfo();
public abstract void testDriveVehicle();
public abstract void negotiateSale();
public abstract void arrangeFinancing();
public abstract void closeSale();
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
public class GetCustomerInfo implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Get customer info");
ctx.put("customerName","George Burdell");
return false;
}
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
public class TestDriveVehicle implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Test drive the vehicle");
return false;
}
}
public class NegotiateSale implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Negotiate sale");
return false;
}
}
public class ArrangeFinancing implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Arrange financing");
return false;
}
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
public class CloseSale implements Command {
public boolean execute(Context ctx) throws Exception {
System.out.println("Congratulations "
+ctx.get("customerName")
+", you bought a new car!");
return false;
}
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.impl.ChainBase;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.impl.ContextBase;
public class SellVehicleChain extends ChainBase {
public SellVehicleChain() {
super();
addCommand(new GetCustomerInfo());
addCommand(new TestDriveVehicle());
addCommand(new NegotiateSale());
addCommand(new ArrangeFinancing());
addCommand(new CloseSale());
}
public static void main(String[] args) throws Exception {
Command process = new SellVehicleChain();
Context ctx = new ContextBase();
process.execute(ctx);
}
}
package com.jadecove.chain.sample;
import org.apache.commons.chain.impl.ContextBase;
public class SellVehicleContext extends ContextBase {
private String customerName;
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String name) {
this.customerName = name;
}
}
public static void main(String[] args) throws Exception {
Command process = new SellVehicleChain();
Context ctx = new SellVehicleContext();
process.execute(ctx);
}
public boolean execute(Context ctx) throws Exception {
SellVehicleContext myCtx = (SellVehicleContext) ctx;
System.out.println("Congratulations "
+ myCtx.getCustomerName()
+ ", you bought a new car!");
return false;
}
<catalog>
<chain name="sell-vehicle">
<command id="GetCustomerInfo"
className="com.jadecove.chain.sample.GetCustomerInfo"/>
<command id="TestDriveVehicle"
className="com.jadecove.chain.sample.TestDriveVehicle"/>
<command id="NegotiateSale"
className="com.jadecove.chain.sample.NegotiateSale"/>
<command id="ArrangeFinancing"
className="com.jadecove.chain.sample.ArrangeFinancing"/>
<command id="CloseSale"
className="com.jadecove.chain.sample.CloseSale"/>
</chain>
</catalog>
package com.jadecove.chain.sample;
import org.apache.commons.chain.Catalog;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.config.ConfigParser;
import org.apache.commons.chain.impl.CatalogFactoryBase;
public class CatalogLoader {
private static final String CONFIG_FILE =
"/com/jadecove/chain/sample/chain-config.xml";
private ConfigParser parser;
private Catalog catalog;
public CatalogLoader() {
parser = new ConfigParser();
}
public Catalog getCatalog() throws Exception {
if (catalog == null) {
parser.parse(this.getClass().getResource(CONFIG_FILE));
}
catalog = CatalogFactoryBase.getInstance().getCatalog();
return catalog;
}
public static void main(String[] args) throws Exception {
CatalogLoader loader = new CatalogLoader();
Catalog sampleCatalog = loader.getCatalog();
Command command = sampleCatalog.getCommand("sell-vehicle");
Context ctx = new SellVehicleContext();
command.execute(ctx);
}
}
<catalog name="auto-sales">
<chain name="sell-vehicle">
<command id="GetCustomerInfo"
className="com.jadecove.chain.sample.GetCustomerInfo"/>
<command id="TestDriveVehicle"
className="com.jadecove.chain.sample.TestDriveVehicle"/>
<command id="NegotiateSale"
className="com.jadecove.chain.sample.NegotiateSale"/>
<command
className="org.apache.commons.chain.generic.LookupCommand"
catalogName="auto-sales"
name="arrange-financing"
optional="true"/>
<command id="CloseSale"
className="com.jadecove.chain.sample.CloseSale"/>
</chain>
<chain name="arrange-financing">
<command id="ArrangeFinancing"
className="com.jadecove.chain.sample.ArrangeFinancing"/>
</chain>
</catalog>
package com.jadecove.chain.sample;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.Filter;
public class SellVehicleExceptionHandler implements Filter {
public boolean execute(Context context) throws Exception {
System.out.println("Filter.execute() called.");
return false;
}
public boolean postprocess(Context context,
Exception exception) {
if (exception == null) return false;
System.out.println("Exception "
+ exception.getMessage()
+ " occurred.");
return true;
}
}
<chain name="sell-vehicle">
<command id="ExceptionHandler"
className =
"com.jadecove.chain.sample.SellVehicleExceptionHandler"/>
<command id="GetCustomerInfo"
className="com.jadecove.chain.sample.GetCustomerInfo"/>