- 浏览: 96909 次
- 性别:
- 来自: 南京市
最新评论
-
fengfan2008:
很多产品图片是一样的, 其实不存在你说的问题;
www.8brl.cn网站上线了,请大家多多指教 -
yekui:
搜索出结果后,点击上架时间排序,出现了跳跃,请LZ查看。整体还 ...
www.8brl.cn网站上线了,请大家多多指教 -
解未知数:
恩,不错..
javascript实现HTML在线编辑器 -
hnzhoujunmei:
你想说明什么问题,有源码实现吗?
java发送手机短信 -
yasaso:
不错 收藏了, 感谢楼主分享
apache&tomcat集群
一、简介
在程序中输出信息的目的有三:一是监视程序运行情况;一是将程序的运行情况记录到日志文件中,以备将来查看;一是做为调试器。但信息输出的手段不仅限于System.out.println()或System.out.print(),还有日志记录工具可以选择。与System.out.pringln()和System.out.print()相比,日志记录工具可以控制输出级别,并且可以在配置文件中对输出级别进行设置,这样开发阶段的信息在程序发布后就可以通过设置输出级别来消除掉,而无须对代码进行修正了。现在流行的日志记录工具很多,Log4J就是其中的佼佼者。
Log4J是由著名开源组织Apache推出的一款日志记录工具,供Java编码人员做日志输出之用,可以从网站http://logging.apache.org/log4j上免费获得,最新版本1.2.11。获得logging-log4j-1.2.11.zip文件后,解压缩,需要的是其中的log4j-1.2.11.jar文件,将该文件放到特定的文件夹中备用,我放到了我机器的G:\YPJCCK\Log4J\lib文件夹中。
这里选择的IDE是Eclipse和JBuilder。Eclipse用的是3.0.1加语言包,可以到www.eclipse.org网站上下载;JBuilder用的是JBuilder 2005。
二、配置类库
下面打开Eclipse或JBuilder。
如果使用的是Eclipse,那么在Eclipse打开后,点击菜单"文件"->"新建"->"项目",打开"新建项目"对话框:
请选中"Java项目",点击"下一步",进入"新建Java项目"对话框:
在这个对话框中需要设置项目的名称以及项目所在目录,我为自己的项目起名为Log4JTest,目录为G:\YPJCCK\Log4J\Eclipse\Log4JTest。设置好后点击"下一步",进入下一个窗口。在这个窗口中选择名为"库"的选项卡,然后点击"添加外部JAR"按钮,将保存于特定文件夹中的log4j-1.2.11.jar文件引用进来。
设置好后,点击"完成",至此,已经具备了在Eclipse下使用Log4J的环境。
如果使用的是JBuilder,那么在JBuilder打开后,点击菜单"Tools"->"Configure" ->"Libraries",打开"Configure Libraries"对话框:
点击"New"按钮,打开"New Library Wizard"对话框:
使用"Add"按钮将保存于特定文件夹中的log4j-1.2.11.jar文件引用进来,并设置Name,即该类库的名字,我将Name设置为Log4J。设置好后点击"OK"按钮,回到"Configure Libraries"对话框,再点击"OK"按钮,则JUnit类库已经被添加到JBuilder当中。
下面继续,在JBuilder中创建项目。点击菜单"File"->"New Project",打开"Project Wizard"对话框:
在这个窗口中设置项目名称及存放目录,我的项目名称仍为Log4JTest,路径为G:/YPJCCK/log4J/JBuilder/Log4JTest。点击"Next"进入下一个窗口:
在这个窗口中选择"Required Libraries"选项卡,点击"Add"按钮,将刚才设置的JUnit库引用进来。然后点击"Next"按钮,进入下一个窗口:
在这个窗口中用鼠标点击Encoding下拉列表框,然后按一下"G"键,选中相应选项,此时该项目的字符集就被设置成GBK了。如果做的是国内项目,这绝对是个好习惯。最后点击"Finish",项目创建完成。
三、编写一个简单的示例
在了解Log4J的使用方法之前,先编写一个简单的示例,以对Log4J有个感性认识。
如果使用的是Eclipse,请点击"文件"->"新建"->"类",打开"新建Java类"对话框,设置包为piv.zheng.log4j.test,名称为Test,并确保"public static void main(String[] args)"选项选中;如果使用的是JBuilder,请点击"File"->"New Class",打开"Class Wizard"对话框,设置Package为piv.zheng.log4j.test,Class name为Test,并确保"Generate main method"选项选中。设置完成后,点击"OK"。代码如下:
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");
}
}
至此,示例编写完成。请点击运行按钮旁边的倒三角,选择"运行为"->"2 Java应用程序"(Eclipse),或者在Test类的选项卡上点击鼠标右键,在调出的快捷菜单中点击"Run using defaults"(JBuilder),运行程序,观察从控制台输出的信息。
四、Log4J入门
看过程序的运行效果后可能会奇怪,为何控制台只输出了"FATAL - Here is FATAL"这样一条信息,而程序代码中的log.debug()、log.info()等方法也都设置了类似的内容,却没有被输出?其实答案很简单,但在公布之前,先来了解一下Log4J的使用。
请先看前边的示例代码,会发现,示例中用到了Logger、Level、ConsoleAppender、SimpleLayout等四个类。其中Logger类使用最多,甚至输出的信息也是在其对象log的fatal方法中设置的,那么Logger究竟是做什么的呢?其实Logger就是传说中的日志记录器(在Log4J中称为Category),创建方法有三:
1.根Category,默认创建,获取方法:
Logger log = Logger.getRootLogger();
2.用户创建的Category,方法:
Logger log = Logger.getLogger("test");
其中字符串test是为Category设定的名称。Category的名称允许使用任何字符,但区分大小写,例如:
Logger l1 = Logger.getLogger("x");
Logger l2 = Logger.getLogger("X");
l1和l2就是两个Category;而如果名称完全相同,例如:
Logger l1 = Logger.getLogger("x");
Logger l2 = Logger.getLogger("x");
l1和l2就是同一个Category。此外,符号"."在Category的名称中有特殊作用,这一点将在后边介绍。
3.与方法2类似,只是参数由字符串换成了类对象,其目的是通过类对象获取类的全名。这个方法比较常用,示例中使用的就是这个方法。
那么Category是如何输出信息的呢?其实示例中用到的debug、info、warn、error、fatal等五个方法都是用来输出信息的。什么,怎么这么多?原因很简单,Log4J支持分级输出。Log4J的输出级别有五个,由低到高依次是DEBUG(调试)、INFO(信息)、WARN(警告)、ERROR(错误)和FATAL(致命),分别与以上方法对应。当输出级别设置为DEBUG时,以上方法都能够输出信息,当输出级别设置为INFO时,则只有debug方法将不能再输出信息,依此类推,当输出级别设置为FATAL时,就只有fatal方法可以输出信息了。现在再回头看前边的问题,为何只有设置给fatal方法的信息被输出就不难理解了,示例中有这样一行代码:
log.setLevel(Level.FATAL);
正是这行代码将log对象的输出级别设成了FATAL。在为log对象设置输出级别时用到了Level类,该类中定义了DEBUG、INFO、WARN、ERROR、FATAL等五个静态对象,与五个输出级别相对应。此外,Level还有两个特殊的静态对象ALL和OFF,前者允许所有的方法输出信息,其级别其实比DEBUG还低;后者则会禁止所有的方法输出信息,其级别比FATAL要高。除前边示例中用到的五个方法,Logger还提供了这五个方法的重载,以在输出信息的同时抛出异常,以fatal方法为例:
log.fatal("Here is FATAL", new Exception("Exception"));
执行后输出信息:
FATAL - Here is FATAL
java.lang.Exception: Exception
at piv.zheng.log4j.test.Test.main(Test.java:24)
其他方法类似。此外,Logger还提供了log方法,该方法不针对任何输出级别,需要在调用时设置,例如:
log.log(Level.FATAL, "Here is FATAL");
log.log(Level.FATAL, "Here is FATAL", new Exception("Exception"));
虽然一般情况下log方法不如其它方法方便,但由于允许设置级别,因此log方法在很多时候反而比其它方法更灵活,甚至可以在输出级别为OFF时输出信息。不过log方法主要是给用户自定义的输出级别用的,而且设立OFF输出级别的目的也为了不输出任何信息,因此请不要在log方法中使用OFF来输出信息。
此外,Category的输出级别并非必须,若未设置,子Category会默认使用其父Category的输出级别,若父Category也没设置,就使用再上一级Category的设置,直到根Category为止。根Category默认输出级别为DEBUG,因此在示例中,若将"log.setLevel(Level.FATAL);"一行注释掉,则所有方法都会输出信息。
下面简单介绍一下Log4J中Category的继承关系。其实在Log4J中Category之间是存在继承关系的,根Category默认创建,是级别最高的Category,用户创建的Category均继承自它。而用户创建的Category之间也存在继承关系,例如:
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的子Category,而lx_y_z是lx_y的子Category。但lxy并不是lx的子Category。也许有点乱,下面来一个一个看。首先看与lx_y、lx_z对应的Category的名称"x.y"和"x.z","."前边的是什么,"x",这说明与名称为"x"的Category对应lx就是它们的父Category;而与lx_y_z对应的Category的名称"x.y.z",最后一个"."前边的是什么,"x.y",这说明lx_y是lx_y_z的父Category;至于lxy,由于与之对应的Category名称"xy"之间没有".",因此它是一个与lx同级的Category,其父Category就是根Category器。此外还有一种情况,例如有一个名称为"a.b"的Category,如果没有名称为"a"的Category,那么它的父Category也是根Category。前边说过,"."在Category名称中有特殊作用,其实它的作用就是继承。至此,为何使用类对象来创建Category也就不难理解了。
可是,仅有Category是无法完成信息输出的,还需要为Category添加Appender,即Category的输出源。前边的例子使用的是ConsoleAppender,即指定Category将信息输出到控制台。其实Log4J提供的Appender有很多,这里选择几常用的进行介绍。
1.org.apache.log4j.WriterAppender,可以根据用户选择将信息输出到Writer或OutputStream。
示例代码:
SimpleLayout layout = new SimpleLayout ();
//向文件中输出信息,OutputStream示例
WriterAppender appender1 = null;
try {
appender1 = new WriterAppender(layout, new FileOutputStream("test.txt"));
}
catch(Exception ex) {}
//向控制台输出信息,Writer示例
WriterAppender appender2 = null;
try {
appender2 = new WriterAppender(layout, new OutputStreamWriter(System.out));
}
catch(Exception ex) {}
//Category支持同时向多个目标输出信息
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender1);
log.addAppender(appender2);
log.debug("output");
这个示例由第一个示例修改而来,没有设置输出级别,而且向Category中添加了两个输出源,运行后会在控制台中输出"DEBUG - output",并在工程目录下生成test.txt文件,该文件中也记录着"DEBUG - output"。若要将test.txt文件放到其它路径下,例如f:,则将"test.txt"改为"f:/test.txt",又如e:下的temp文件夹,就改为"e:/temp/test.txt"。后边FileAppender、RollingFileAppender以及DailyRollingFileAppender设置目标文件时也都可以这样来写。
2.org.apache.log4j.ConsoleAppender,向控制台输出信息,继承了WriterAppender,前边的示例使用的就是它。
3.org.apache.log4j.FileAppender,向文件输出信息,也继承了WriterAppender。
示例代码:
SimpleLayout layout = new SimpleLayout();
//若文件不存在则创建文件,若文件已存在则向文件中追加信息
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");
这个示例也由第一个示例修改而来,运行后会在工程目录下生成test.txt文件,该文件中记录着"DEBUG - output"。再次运行程序,查看文件,则"DEBUG - output"有两行。
另外,FileAppender还有一个构造:
FileAppender(Layout layout, String filename, boolean append)
与示例的类似,只是多了一个boolean型的参数append。append参数是个开关,用来设置当程序重启,而目标文件已存在时,是向目标文件追加信息还是覆盖原来的信息,当值为true时就追加,这是FileAppender默认的,当值为false时则覆盖。此外,FileAppender还提供了setAppend方法来设置append开关。
4.org.apache.log4j.RollingFileAppender,继承了FileAppender,也是向文件输出信息,但文件大小可以限制。当文件大小超过限制时,该文件会被转为备份文件或删除,然后重新生成。文件的转换或删除与设置的备份文件最大数量有关,当数量大于0时就转为备份文件,否则(小于等于0)删除,默认的备份文件数量是1。转换备份文件非常简单,就是修改文件名,在原文件名之后加上".1",例如文件test.txt,转为备份文件后文件名为"test.txt.1"。但若同名的备份文件已存在,则会先将该备份文件删除或更名,这也与设置的备份文件最大数量有关,若达到最大数量就删除,否则更名。若备份文件更名时也遇到同样情况,则使用同样的处理方法,依此类推,直到达到设置的备份文件最大数量。备份文件更名也很简单,就是将扩展名加1,例如test.txt.1文件更名后变为test.txt.2,test.txt.2文件更名后变为test.txt.3。
示例代码:
SimpleLayout layout = new SimpleLayout();
//若文件不存在则创建文件,若文件已存在则向文件中追加内容
RollingFileAppender appender = null;
try {
appender = new RollingFileAppender(layout, "test.txt");
} catch(Exception e) {}
//限制备份文件的数量,本例为2个
appender.setMaxBackupIndex(2);
//限制目标文件的大小,单位字节,本例为10字节
appender.setMaximumFileSize(10);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output0");
log.debug("output1");
log.debug("output2");
程序运行后,会在工程目录下生成test.txt、test.txt.1和test.txt.2三个文件,其中test.txt内容为空,而后两个文件则分别记录着"DEBUG - output2"和"DEBUG - output1",这是怎么回事?原来由于目标文件大小被限制为10字节,而三次使用log.debug方法输出的信息都超过了10字节,这样就导致了三次备份文件转换,所以test.txt内容为空。而备份文件最大数量被设为2,因此第一次转换的备份文件就被删掉了,而后两次的则保存下来。此外,由于test.txt转换备份文件时是先转为test.txt.1,再转为test.txt.2,因此最后test.txt.2的内容是"DEBUG - output1",而test.txt.1是"DEBUG - output2",这点千万别弄混了。
另外,RollingFileAppender还提供了两个方法:
(1)setMaxFileSize,功能与setMaximumFileSize一样,但参数是字符串,有两种情况:一是仅由数字组成,默认单位为字节,例如"100",即表示限制文件大小为100字节;一是由数字及存储单位组成,例如"1KB"、"1MB"、"1GB",其中单位不区分大小写,分别表示限制文件大小为1K、1M、1G。
(2)rollOver,手动将目标文件转换为备份文件,使用起来较灵活,适用于复杂情况。
示例代码:
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();
这里没限制目标文件大小,但程序运行后,效果与上例相同。
5.org.apache.log4j.DailyRollingFileAppender,也继承了FileAppender,并且也是向文件输出信息,但会根据设定的时间频率生成备份文件。
时间频率格式简介:
'.'yyyy-MM,按月生成,生成时间为每月最后一天午夜过后,例如test.txt在2005年7月31日午夜过后会被更名为test.txt.2005-07,然后重新生成。
'.'yyyy-ww,按周生成,生成时间为每周六午夜过后,例如test.txt在2005年8月13日午夜过后会被更名为test.txt.2005-33,33表示当年第33周。
'.'yyyy-MM-dd,按天生成,生成时间为每天午夜过后,例如2005年8月16日午夜过后,test.txt会被更名为test.txt.2005-08-16。
'.'yyyy-MM-dd-a,也是按天生成,但每天会生成两次,中午12:00过后一次,午夜过后一次,例如test.txt在2005年8月16日12:00过后会被更名为test.txt.2005-8-16-上午,午夜过后会被更名为test.txt.2005-8-16-下午。
'.'yyyy-MM-dd-HH,按小时生成,例如test.txt在2005年8月16日12:00过后会被更名为test.txt.2005-8-16-11。
'.'yyyy-MM-dd-HH-mm,按分钟生成,例如test.txt在2005年8月16日12:00过后会被更名为test.txt.2005-8-16-11-59。
示例代码:
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");
编码完成后运行程序,等一分钟后再次运行,由于我是在2005年8月17日15:42分第一次运行程序的,因此工程目录下最终有两个文件test.txt和test.txt.2005-08-17-15-42。
6.org.apache.log4j.AsyncAppender,用于管理不同类型的Appender,也能实现同时向多个源输出信息,但其执行是异步的。
示例代码:
SimpleLayout layout = new SimpleLayout();
//向控制台输出
ConsoleAppender appender1 = null;
try {
appender1 = new ConsoleAppender(layout);
} catch(Exception e) {}
//向文件输出
FileAppender appender2 = null;
try {
appender2 = new FileAppender(layout, "test.txt");
} catch(Exception e) {}
//使用AsyncAppender实现同时向多个目标输出信息
AsyncAppender appender = new AsyncAppender();
appender.addAppender(appender1);
appender.addAppender(appender2);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
此外,AsyncAppender和Logger都提供了更多的方法来管理Appender,例如getAppender、getAllAppenders、removeAppender和removeAllAppenders,分别用来获取指定的Appender、获取全部Appender、移除指定的Appender以及移除全部Appender。
7.org.apache.log4j.jdbc.JDBCAppender,将信息输出到数据库。
示例代码:
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");
这里使用的数据库是MySQL 5.0.4beta,用户名root,密码11111111,我在其中建了一个库zheng,包含表log4j,该表只有一个字段msg,类型为varchar(300)。此外,本例用到的JDBC驱动可以从http://dev.mysql.com/downloads/connector/j/3.1.html下载,版本3.1.8a,下载mysql-connector-java-3.1.8a.zip文件后解压缩,需要其中的mysql-connector-java-3.1.8-bin.jar文件。下面再来看代码。由于JDBCAppender内部默认使用PatternLayout格式化输出信息,因此这里没用到SimpleLayout,而appender.setSql所设置的SQL语句就是PatternLayout所需的格式化字符串,故此其中才有"%m"这样的字符,有关PatternLayout的具体内容后边介绍。执行后,表log4j增加一条记录,内容为"output"。
8.org.apache.log4j.nt.NTEventLogAppender,向Windows NT系统日志输出信息。
示例代码:
SimpleLayout layout = new SimpleLayout();
NTEventLogAppender appender = new NTEventLogAppender("Java", layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
注意,要完成此示例,还需向C:\WINNT\system32文件夹(我的操作系统装在了C:\)中复制一个名为NTEventLogAppender.dll的文件。如果跟我一样用的是Log4J 1.2.11,实在对不住,Log4J 1.2.11并未提供该文件。虽然logging-log4j-1.2.11.zip文件解压缩后,其下的src\java\org\apache\log4j\nt文件夹中有一个make.bat文件执行后可以编译出该文件,但还需要配置,很麻烦。还好,条条大道通罗马,1.2.11不行,就换1.2.9,可以从http://apache.justdn.org/logging/log4j/1.2.9下载,下载后解压缩logging-log4j-1.2.9.zip文件,在其下的src\java\org\apache\log4j\nt文件夹中找到NTEventLogAppender.dll,复制过去就可以了。程序执行后,打开"事件查看器",选择"应用程序日志",其中有一条来源为Java的记录,这条记录就是刚才输出的信息了。
9.org.apache.log4j.lf5.LF5Appender,执行时会弹出一个窗口,信息在该窗口中以表格的形式显示。
示例代码:
LF5Appender appender = new LF5Appender();
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
由于LF5Appender不需要Layout格式化输出信息,因此这里没有设置。此外LF5Appender还提供了一个setMaxNumberOfRecords方法,用来限制信息在表格中显示的行数。
10.org.apache.log4j.net.SocketAppender,以套接字方式向服务器发送日志,然后由服务器将信息输出。
示例代码:
//指定要连接的服务器地址及端口,这里使用的是本机9090端口
SocketAppender appender = new SocketAppender("localhost", 9090);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
SocketAppender不需要设置Layout,因为SocketAppender不负责输出信息。那么如何看到信息输出的效果呢?这就需要SocketServer和SimpleSocketServer了。
示例代码1:
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"});
}
}
这是SocketServer的示例。SocketServer只有一个静态方法main,该方法意味着SocketServer不仅可以在代码中被调用,也可以用java命令执行。main方法只有一个参数,是个字符串数组,但要求必须有三个元素:元素一用来指定端口,本例为9090;元素二用来指定输出信息时需要的配置文件,该文件放在工程目录下,本例使用的test.properties内容如下:
log4j.rootLogger=, console
log4j.appender.console =org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.SimpleLayout
该配置指定SocketServer使用ConsoleAppender以SimpleLayout格式输出信息;元素三用来指定一个路径,以存放.lcf文件,我指定的是本机的G:/YPJCCK/Log4J文件夹。.lcf文件也是输出信息时使用的配置文件,格式与元素二所指定的配置文件一样,但test.properties是默认配置文件,即当.lcf文件找不到时才使用。那么.lcf文件如何命名呢?其实.lcf文件的名称并不是随意起的,当SocketAppender与SocketServer建立连接时,SocketServer就会获得SocketAppender所在计算机的IP地址与网络ID,并将其格式化成"网络ID/IP地址"这样的字符串,然后获取其中的网络ID作为.lcf文件的主名,例如"zhengyp/127.0.0.1",其中的"zhengyp"就是主文件名,而后再根据这个文件名来调用相应的.lcf文件。这意味着对不同的计算机可以提供不同的配置文件,使信息输出时有不同的效果。此外,SocketServer还默认了一个名为generic.lcf的文件,用于处理网络ID获取不到或其他情况,本例是用的就是这个文件,内容如下:
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格式输出信息。运行程序时请先运行SocketServer,再运行SocketAppender。SocketAppender运行结束后,就可以从SocketServer的控制台看到输出的信息了。
示例代码2:
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"});
}
}
这是SimpleSocketServer的示例,与SocketServer相比,只允许指定一个默认的配置文件,而无法对不同计算机使用不同的配置文件。
11.org.apache.log4j.net.SocketHubAppender,也是以套接字方式发送日志,但与SocketAppender相反,SocketHubAppender是服务器端,而不是客户端。
示例代码:
//指定服务器端口,这里使用的是本机9090端口
SocketHubAppender appender = new SocketHubAppender(9090);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
while (true) {
Thread.sleep(1000);
log.debug("output"); //输出信息
}
由于SocketHubAppender一旦运行就开始发送消息,而无论有无接收者,因此这里使用了while语句并将条件设为true以保证程序持续运行。不过为了保证性能,这里还使用了Thread.sleep(1000),这样程序每循环一次都休眠1秒,如果机器性能不好,还可以将值设的再大些。此外,由于SocketHubAppender也不负责输出信息,因此同样不需要设置Layout。那么如何看到信息输出的效果呢?这里我自己写了个客户端程序,代码如下:
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);
//调用配置文件
PropertyConfigurator.configure("test.properties");
//从套接字中恢复Logger,并输出信息
new Thread(new SocketNode(s, LogManager.getLoggerRepository())).start();
}
}
由于SocketHubAppender与SocketAppender一样,发送的也是SocketNode对象,因此编写该程序时参考了SocketServer的源码。此外,这里的配置文件直接使用了上例的test.properties文件。运行程序时请先运行SocketHubAppender,再运行客户端程序,然后从客户端的控制台就可以看到效果了。
13.org.apache.log4j.net.TelnetAppender,与SocketHubAppender有些类似,也是作为服务器发送信息,但TelnetAppender发送的不是SocketNode对象,而是Category输出的结果。
示例代码:
SimpleLayout layout = new SimpleLayout();
TelnetAppender appender = new TelnetAppender();
appender.setLayout(layout); //设置Layout
appender.setPort(9090); //设置端口号
appender.activateOptions(); //应用设置
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
while (true) {
java.lang.Thread.sleep(1000);
log.debug("output"); //输出信息
}
//appender.close();
注意最后一行被注释掉的代码,若该行代码执行,则TelnetAppender的资源会被清理,从而导致TelnetAppender无法继续运行。那么如何看到信息输出的效果呢?这里提供两种方法:方法一,使用Telnet工具,我使用的就是Windows自带的Telnet。运行TelnetAppender程序后,点击[开始]菜单->[运行],在"运行"框中输入"telnet",回车,telnet客户端弹出,这是一个命令行程序,输入命令"open localhost 9090",回车,然后就可以看到效果了。方法二,自己写程序,代码如下:
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与Socket绑定,以输出Socket获得的信息
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
//获得信息并输出
String line = in.readLine();
while (line != null) {
System.out.println(line);
line = in.readLine();
}
}
}
13.org.apache.log4j.net.SMTPAppender,向指定的电子邮件发送信息,但只能发送ERROR和FATAL级别的信息,而且还没提供身份验证功能。
示例代码:
SimpleLayout loyout = new SimpleLayout();
SMTPAppender appender = new SMTPAppender();
appender.setLayout(loyout); //设置Layout
appender.setFrom("zhengyp@126.com"); //设置发件人
appender.setSMTPHost("smtp.126.com"); //设置发送邮件服务器
appender.setTo("zhengyp@126.com"); //设置收件人
appender.setSubject("Log4J Test"); //设置邮件标题
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");
要运行此示例,还需要JavaMail 和JAF,前者是Sun推出的电子邮件类库,可以从http://java.sun.com/products/javamail/downloads/index.html下载,最新版本1.3.3,下载javamail-1_3_3-ea.zip压缩包后需要其中的mail.jar文件;后者全称是JavaBeans Activation Framework,提供了对输入任意数据块的支持,并能相应地对其进行处理,可以从http://www.sun.com/download中找到,最新版本1.1,下载jaf-1_1-ea.zip压缩包后需要其中的activation.jar文件。不过,程序运行后会抛出两次异常,分别是log.error和log.fatal方法导致的,失败的原因很简单,我用的邮件服务器需要身份验证。
14.piv.zheng.log4j.test.SMTPAppender,自定义的,依照Log4J提供的SMTPAppender修改而来,增加了身份验证功能,并去掉了对级别的限制。由于代码太长,所以放到了另一篇文章《SMTPAppender源码》中,有兴趣的请自行去查看。
示例代码:
SimpleLayout layout = new SimpleLayout();
SMTPAppender appender = new SMTPAppender(layout);
appender.setFrom("zhengyp@126.com"); //发件人
appender.setSMTPHost("smtp.126.com"); //发送邮件服务器
appender.setTo("zhengyp@126.com"); //收件人
appender.setSubject("Log4J Test"); //邮件标题
appender.setAuth("true"); //身份验证标识
appender.setUsername("zhengyp"); //用户名
appender.setPassword("1111111"); //密码
appender.activateOptions(); //应用设置
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
同样需要JavaMail 和JAF。程序运行后会发送一封邮件,快去查看一下自己的邮箱吧^_^
此外,Log4J还提供了SyslogAppender、JMSAppender(均在org.apache.log4j.net包下)以及更多的Appender,或者用来向Unix操作系统的syslogd服务发送信息,或者通过JMS方式发送信息,或者以其他方式发送信息。由于条件有现,就不再介绍了。
不过,在前边的示例中还使用了SimpleLayout和PatternLayout来格式化输出的信息,这里也简单介绍一下。
1.org.apache.log4j.SimpleLayout,一直用的就是它,输出的格式比较简单,就是"级别 - 信息"。
2.org.apache.log4j.HTMLLayout,以HTML格式输出信息。
示例代码:
HTMLLayout layout = new HTMLLayout();
layout.setTitle("Log4J Test"); //HTML页标题
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");
程序运行后会在工程目录下生成一个HTML页,可以用浏览器来查看。
3.org.apache.log4j.xml.XMLLayout,以XML格式输出信息。
示例代码:
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");
程序运行后会在工程目录下生成一个test.xml文件。
4.org.apache.log4j.TTCCLayout,输出信息的同时输出日志产生时间、相关线程及Category等信息。
示例代码:
TTCCLayout layout = new TTCCLayout();
//是否打印与TTCCLayout关联的Category的名称,默认为true,表示打印
layout.setCategoryPrefixing(true);
//是否打印当前线程,默认为true,表示打印
layout.setThreadPrinting(true);
//是否打印输出和当前线程相关的NDC信息,默认为true,表示打印
layout.setContextPrinting(true);
//设置日期时间格式
layout.setDateFormat("iso8601");
//设置时区
layout.setTimeZone("GMT+8:00");
//设置时区后需要调用此方法应用设置
layout.activateOptions();
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
注意,TTCCLayout输出的时间格式及时区是可以设置的:
(1)setDateFormat,设置日期时间格式,有五个常用值:"NULL",表示不输出;"RELATIVE",输出信息所用的时间,以毫秒为单位,默认使用该值;"ABSOLUTE",仅输出时间部分;"DATE",按当前所在地区显示日期和时间;"ISO8601",按ISO8601标准显示日期和时间。这些字符串不区分大小写。此外,还可以使用时间模式字符来格式化日期时间,详细内容请参考J2SE文档中的java.text.SimpleDateFormat类。
(2)setTimeZone,设置时区,详细内容请参考J2SE文档中的java.util.TimeZone类和java.util.SimpleTimeZone类。但请注意,当日期格式为"RELATIVE"时,设置时区会造成冲突。
5.org.apache.log4j.PatternLayout,用模式字符灵活指定信息输出的格式。
示例代码:
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");
模式字符串简介:
%c:Category名称。还可以使用%c{n}的格式输出Category的部分名称,其中n为正整数,输出时会从Category名称的右侧起查n个".",然后截取第n个"."右侧的部分输出,例如Category的名称为"x.y.z",指定格式为"%c{2}",则输出"y.z"。
%C:输出信息时Category所在类的名称,也可以使用%C{n}的格式输出。
%d:输出信息的时间,也可以用%d{FormatString}的格式输出,其中FormatString的值请参考TTCCLayout的setDateFormat方法,但NULL和RELATIVE在%d中无法使用。
%F:输出信息时Category所在类文件的名称。
%l:输出信息时Category所在的位置,使用"%C.%M(%F:%L)"可以产生同样的效果。
%L:输出信息时Category在类文件中的行号。
%m:信息本身。
%M:输出信息时Category所在的方法。
%n:换行符,可以理解成回车。
%p:日志级别。
%r:输出信息所用的时间,以毫秒为单位。
%t:当前线程。
%x:输出和当前线程相关的NDC信息。
%X:输出与当前现成相关的MDC信息。
%%:输出%。
此外,还可以在%与模式字符之间加上修饰符来设置输出时的最小宽度、最大宽度及文本对齐方式,例如:
%30d{DATE}:按当前所在地区显示日期和时间,并指定最小宽度为30,当输出信息少于30个字符时会补以空格并右对齐。
%-30d{DATE}:也是按当前所在地区显示日期和时间,指定最小宽度为30,并在字符少于30时补以空格,但由于使用了"-",因此对齐方式为左对齐,与默认情况一样。
%.40d{DATE}:也是按当前所在地区显示日期和时间,但指定最大宽度为40,当输出信息多于40个字符时会将左边多出的字符截掉。此外,最大宽度只支持默认的左对齐方式,而不支持右对齐。
%30.40d{DATE}:如果输出信息少于30个字符就补空格并右对齐,如果多于40个字符,就将左边多出的字符截掉。
%-30.40d{DATE}:如果输出信息少于30个字符就补空格并左对齐,如果多于40个字符,就将左边多出的字符截掉。
五、Log4J进阶
了解以上内容后,就已经初步掌握Log4J了,但要想灵活使用Log4J,则还需要了解其配置功能。这里简单介绍一下。
1.org.apache.log4j.BasicConfigurator,默认使用ConsoleAppender以PatternLayout(使用PatternLayout.TTCC_CONVERSION_PATTERN,即"%r [%t] %p %c %x - %m%n"格式)输出信息。
示例代码:
BasicConfigurator.configure();
Logger log = Logger.getLogger(Test.class);
log.debug("output");
注意,BasicConfigurator及其它Configurator其实都只对根Category进行配置,但由于用户创建的Category会继承根Category的特性(声明,许多资料介绍Category继承关系时都主要在讨论输出级别,而事实上,Category间继承的不仅是输出级别,所有特性都可以继承),因此输出时仍会显示BasicConfigurator配置的效果。此外,还可以使用configure方法指定Appender,以自定义输出。BasicConfigurator允许同时指定多个Appender。
示例代码:
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");
这里用BasicConfigurator指定了两个Appender,即ConsoleAppender和FileAppender,程序运行后信息会在以SimpleLayout输出到控制台的同时以PatternLayout输出到test.log文件。若要清除这些Appender,可以调用BasicConfigurator的resetConfiguration方法。
2.org.apache.log4j.PropertyConfigurator,调用文本配置文件输出信息,通常使用.properties文件。配置文件以"键=值"的形式保存数据,注释以"#"开头。PropertyConfigurator和配置文件在介绍SocketAppender和SocketHubAppender时曾提到过。使用PropertyConfigurator可以避免硬编码。
示例代码:
PropertyConfigurator.configure("test.properties");
Logger log = Logger.getLogger(Test.class);
log.debug("output");
要完成该示例,还需要在工程目录下创建一个test.properties文件,内容如下:
##设置根Category,其值由输出级别和指定的Appender两部分组成
#这里设置输出级别为DEBUG
log4j.rootLogger=DEBUG,appender
##输出信息到控制台
#创建一个名为appender的Appender,类型为ConsoleAppender
log4j.appender.appender=org.apache.log4j.ConsoleAppender
#设置appender以SimpleLayout输出
log4j.appender.appender.layout=org.apache.log4j.SimpleLayout
此外,PropertyConfigurator也允许同时指定多个Appender,例如:
#这里没有设置输出级别,但指定了两个Appender
log4j.rootLogger=,appender1,appender2
#输出信息到控制台
log4j.appender.appender1=org.apache.log4j.ConsoleAppender
log4j.appender.appender1.layout=org.apache.log4j.SimpleLayout
#输出信息到文件
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
关于更多配置,网上示例很多,这里不再赘述。但要说明一件事,就是配置文件中的键是怎么来的。参照后一个示例,查看PropertyConfigurator源码,会发现"log4j.rootLogger"是定义好的,只能照写;而"log4j.appender"字样也可以找到,与指定的Appender名称appender1、appender2联系起来,log4j.appender.appender1和log4j.appender.appender2也就不难理解了;再看下去,还能找到"prefix + ".layout"",这样log4j.appender.appender1.layout也有了;可是log4j.appender.appender2.File 和log4j.appender.appender2.Append呢?还记得前边介绍FileAppender时曾提到的setAppend方法吗?其实FileAppender还有个getAppend方法,这说明FileAppender具有Append属性。那么File呢?当然也是FileAppender的属性了。至于log4j.appender.appender2.layout.ConversionPattern也一样,只不过FileAppender换成了PatternLayout。其实别的Appender和Layout的属性也都是这样定义成键来进行设置的。此外,定义键时,属性的首字母不区分大小写,例如"File",也可以写成"file"。
3.org.apache.log4j.xml.DOMConfigurator,调用XML配置文件输出信息。其定义文档是log4j-1.2.11.jar中org\apache\log4j\xml包下的log4j.dtd文件。与PropertyConfigurator相比,DOMConfigurator似乎是趋势。
示例代码:
DOMConfigurator.configure("test.xml");
Logger log = Logger.getLogger(Test.class);
log.debug("output");
要完成该示例,也需要在工程目录下创建一个test.xml文件,内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- 输出信息到控制台
创建一个名为appender的Appender,类型为ConsoleAppender -->
<appender name="appender" class="org.apache.log4j.ConsoleAppender">
<!-- 设置appender以SimpleLayout输出 -->
<layout class="org.apache.log4j.SimpleLayout"/>
</appender>
<!-- 设置根Category,其值由输出级别和指定的Appender两部分组成
这里设置输出级别为DEBUG -->
<root>
<priority value ="debug" />
<appender-ref ref="appender"/>
</root>
</log4j:configuration>
此外,DOMConfigurator也允许同时指定多个Appender,例如:
<?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>
<!-- 这里没有设置输出级别,但指定了两个Appender -->
<root>
<appender-ref ref="appender1"/>
<appender-ref ref="appender2"/>
</root>
</log4j:configuration>
由于以上两个示例是在PropertyConfigurator的两个示例基础上改的,而且也写了注释,因此这里只简单介绍一下<param>标记。<param>标记有两个属性,name和value,前者的值也是Appender或Layout的属性名,作用与log4j.appender.appender2.File这样的键一样。设置时,首字母同样不区分大小写,例如"File"也可以写成"file"。此外还请注意,使用这两段XML代码时应将中文注释去掉,或者把<?xml version="1.0" encoding="UTF-8" ?>中的UTF-8改成GBK或GB2312,否则会导致错误。这里使用的UTF-8是XML默认的字符集。
4.org.apache.log4j.lf5.DefaultLF5Configurator,默认使用LF5Appender来输出信息,需要调用log4j-1.2.11.jar中org\apache\log4j\lf5\config包下的defaultconfig.properties文件。
示例代码:
try {
DefaultLF5Configurator.configure();
}
catch(Exception e){}
Logger log = Logger.getLogger(Test.class);
log.debug("output");
下面讨论另外一个话题:Diagnostic Context。Diagnostic Context意为诊断环境,针对于多用户并发环境,在这种环境下,通常需要对每个客户端提供独立的线程以处理其请求,此时若要在日志信息中对客户端加以区分,为每个线程分别创建Category是个办法。但这样做并不高效,反而会导致大量资源被占用。Diagnostic Context所要解决的就是这个问题。Diagnostic Context会为当前线程提供一定空间,然后将信息保存到该空间供Category调用。与创建一个Category相比,这点信息所占的资源自然要少得多。
1.org.apache.log4j.NDC。NDC是Nested Diagnostic Context的简写,意为嵌套诊断环境,使用时提供一个堆栈对象来保存信息。堆栈的特点是数据后进先出、先进后出,即清理堆栈时,后保存的数据会被先清掉,而先保存的数据则被后清掉。
示例代码:
PatternLayout layout = new PatternLayout("%m %x%n");
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
String tmp = "zhengyp"; //模拟从客户端获取的信息
log.debug("Start");
NDC.push(tmp); //添加信息到堆栈中
log.debug("Before");
NDC.pop(); //将信息从堆栈中移除
log.debug("After");
NDC.remove(); //将当前线程移除,退出NDC环境
log.debug("End");
这里使用了PatternLayout来格式化信息,其模式字符%x就是用来输出NDC信息的。程序运行后会输出如下内容:
Start
Before zhengyp
After
End
可以看到,第二行输出时由于已向堆栈中添加了信息,因此"zhengyp"也会同时输出;而第三行输出时由于信息已被移除,因此就没再输出"zhengyp"。不过这个示例仅简单演示了NDC的用法,而没有显示出NDC的堆栈特性,所以下面再提供一个示例,代码如下:
TTCCLayout layout = new TTCCLayout();
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("Start");
NDC.push("zhengyp"); //添加信息到堆栈中
log.debug("Test1");
NDC.push("192.168.0.1"); //向堆栈中追加信息
log.debug("Test2");
NDC.pop(); //从堆栈中移除信息,但移除的只是最后的信息
log.debug("Test3");
NDC.pop(); //再次从堆栈中移除信息
log.debug("Test4");
log.debug("End");
这里格式化输出信息使用的是TTCCLayout,还记得其setContextPrinting方法吗?程序运行后,从输出的信息就可以看到效果了。此外,NDC还提供了其他方法:
(1)get,获取堆栈中的全部信息。以上例为例,当输出Test2时,使用该方法会获得"zhengyp 192.168.0.1"。
(2)peek,获取堆栈中最后的信息。仍以上例为例,当输出Test1时会获得"zhengyp",Test2时为"192.168.0.1",而当输出Test3时由于"192.168.0.1"已被移除,"zhengyp"又成了最后的信息,因此获得的仍是"zhengyp"。
(3)clear,清空堆栈中的全部信息。
(4)setMaxDepth,设置堆栈的最大深度,即当前的信息可以保留多少,对之后追加的信息没有影响。当需要一次清掉多条信息时,使用setMaxDepth会比多次调用pop方便。
2.org.apache.log4j.MDC。MDC是Mapped Diagnostic Context的简写,意为映射诊断环境,提供了一个Map对象来保存信息。Map对象使用Key、Value的形式保存值。
示例代码:
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");
//添加信息到Map中
MDC.put("name", "zhengyp1");
MDC.put("ip", "192.168.1.1");
log.debug("Test1");
//添加信息到Map中,若Key重复,则覆盖之前的值
MDC.put("name", "zhengyp2");
MDC.put("ip", "192.168.1.2");
log.debug("Test2");
//将信息从Map中移除,此时信息不再输出
MDC.remove("name");
MDC.remove("ip");
log.debug("End");
这个示例演示了MDC的基本用法,格式化信息用的也是PatternLayout,模式字符为"%X",其格式必须为"%X{Key}"。其中Key就是向Map对象添加信息时put方法所用的Key,这里为name和ip。由于可以使用"%X{Key}"输出信息,因此MDC使用起来会比NDC更灵活。此外,MDC还提供了get方法来获取指定Key的信息。
六、小结
用了近半个月,终于大概掌握了Log4J。由于本文是边学边写的,目的是将Log4J的用法记录下来,而非提供一份中文参考,因此内容并不细致,但尽量提供了示例。不过到最后才发现,示例存在问题,其实Logger做为类的static成员比较恰当,而我为了图方便,竟直接写到了main方法中,这一点还请注意。
此外,这里再推荐一下《The Complete log4j Manual》,是对Log4J较详细的介绍,在网上可以找到,只不过是英文的。
在程序中输出信息的目的有三:一是监视程序运行情况;一是将程序的运行情况记录到日志文件中,以备将来查看;一是做为调试器。但信息输出的手段不仅限于System.out.println()或System.out.print(),还有日志记录工具可以选择。与System.out.pringln()和System.out.print()相比,日志记录工具可以控制输出级别,并且可以在配置文件中对输出级别进行设置,这样开发阶段的信息在程序发布后就可以通过设置输出级别来消除掉,而无须对代码进行修正了。现在流行的日志记录工具很多,Log4J就是其中的佼佼者。
Log4J是由著名开源组织Apache推出的一款日志记录工具,供Java编码人员做日志输出之用,可以从网站http://logging.apache.org/log4j上免费获得,最新版本1.2.11。获得logging-log4j-1.2.11.zip文件后,解压缩,需要的是其中的log4j-1.2.11.jar文件,将该文件放到特定的文件夹中备用,我放到了我机器的G:\YPJCCK\Log4J\lib文件夹中。
这里选择的IDE是Eclipse和JBuilder。Eclipse用的是3.0.1加语言包,可以到www.eclipse.org网站上下载;JBuilder用的是JBuilder 2005。
二、配置类库
下面打开Eclipse或JBuilder。
如果使用的是Eclipse,那么在Eclipse打开后,点击菜单"文件"->"新建"->"项目",打开"新建项目"对话框:
请选中"Java项目",点击"下一步",进入"新建Java项目"对话框:
在这个对话框中需要设置项目的名称以及项目所在目录,我为自己的项目起名为Log4JTest,目录为G:\YPJCCK\Log4J\Eclipse\Log4JTest。设置好后点击"下一步",进入下一个窗口。在这个窗口中选择名为"库"的选项卡,然后点击"添加外部JAR"按钮,将保存于特定文件夹中的log4j-1.2.11.jar文件引用进来。
设置好后,点击"完成",至此,已经具备了在Eclipse下使用Log4J的环境。
如果使用的是JBuilder,那么在JBuilder打开后,点击菜单"Tools"->"Configure" ->"Libraries",打开"Configure Libraries"对话框:
点击"New"按钮,打开"New Library Wizard"对话框:
使用"Add"按钮将保存于特定文件夹中的log4j-1.2.11.jar文件引用进来,并设置Name,即该类库的名字,我将Name设置为Log4J。设置好后点击"OK"按钮,回到"Configure Libraries"对话框,再点击"OK"按钮,则JUnit类库已经被添加到JBuilder当中。
下面继续,在JBuilder中创建项目。点击菜单"File"->"New Project",打开"Project Wizard"对话框:
在这个窗口中设置项目名称及存放目录,我的项目名称仍为Log4JTest,路径为G:/YPJCCK/log4J/JBuilder/Log4JTest。点击"Next"进入下一个窗口:
在这个窗口中选择"Required Libraries"选项卡,点击"Add"按钮,将刚才设置的JUnit库引用进来。然后点击"Next"按钮,进入下一个窗口:
在这个窗口中用鼠标点击Encoding下拉列表框,然后按一下"G"键,选中相应选项,此时该项目的字符集就被设置成GBK了。如果做的是国内项目,这绝对是个好习惯。最后点击"Finish",项目创建完成。
三、编写一个简单的示例
在了解Log4J的使用方法之前,先编写一个简单的示例,以对Log4J有个感性认识。
如果使用的是Eclipse,请点击"文件"->"新建"->"类",打开"新建Java类"对话框,设置包为piv.zheng.log4j.test,名称为Test,并确保"public static void main(String[] args)"选项选中;如果使用的是JBuilder,请点击"File"->"New Class",打开"Class Wizard"对话框,设置Package为piv.zheng.log4j.test,Class name为Test,并确保"Generate main method"选项选中。设置完成后,点击"OK"。代码如下:
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");
}
}
至此,示例编写完成。请点击运行按钮旁边的倒三角,选择"运行为"->"2 Java应用程序"(Eclipse),或者在Test类的选项卡上点击鼠标右键,在调出的快捷菜单中点击"Run using defaults"(JBuilder),运行程序,观察从控制台输出的信息。
四、Log4J入门
看过程序的运行效果后可能会奇怪,为何控制台只输出了"FATAL - Here is FATAL"这样一条信息,而程序代码中的log.debug()、log.info()等方法也都设置了类似的内容,却没有被输出?其实答案很简单,但在公布之前,先来了解一下Log4J的使用。
请先看前边的示例代码,会发现,示例中用到了Logger、Level、ConsoleAppender、SimpleLayout等四个类。其中Logger类使用最多,甚至输出的信息也是在其对象log的fatal方法中设置的,那么Logger究竟是做什么的呢?其实Logger就是传说中的日志记录器(在Log4J中称为Category),创建方法有三:
1.根Category,默认创建,获取方法:
Logger log = Logger.getRootLogger();
2.用户创建的Category,方法:
Logger log = Logger.getLogger("test");
其中字符串test是为Category设定的名称。Category的名称允许使用任何字符,但区分大小写,例如:
Logger l1 = Logger.getLogger("x");
Logger l2 = Logger.getLogger("X");
l1和l2就是两个Category;而如果名称完全相同,例如:
Logger l1 = Logger.getLogger("x");
Logger l2 = Logger.getLogger("x");
l1和l2就是同一个Category。此外,符号"."在Category的名称中有特殊作用,这一点将在后边介绍。
3.与方法2类似,只是参数由字符串换成了类对象,其目的是通过类对象获取类的全名。这个方法比较常用,示例中使用的就是这个方法。
那么Category是如何输出信息的呢?其实示例中用到的debug、info、warn、error、fatal等五个方法都是用来输出信息的。什么,怎么这么多?原因很简单,Log4J支持分级输出。Log4J的输出级别有五个,由低到高依次是DEBUG(调试)、INFO(信息)、WARN(警告)、ERROR(错误)和FATAL(致命),分别与以上方法对应。当输出级别设置为DEBUG时,以上方法都能够输出信息,当输出级别设置为INFO时,则只有debug方法将不能再输出信息,依此类推,当输出级别设置为FATAL时,就只有fatal方法可以输出信息了。现在再回头看前边的问题,为何只有设置给fatal方法的信息被输出就不难理解了,示例中有这样一行代码:
log.setLevel(Level.FATAL);
正是这行代码将log对象的输出级别设成了FATAL。在为log对象设置输出级别时用到了Level类,该类中定义了DEBUG、INFO、WARN、ERROR、FATAL等五个静态对象,与五个输出级别相对应。此外,Level还有两个特殊的静态对象ALL和OFF,前者允许所有的方法输出信息,其级别其实比DEBUG还低;后者则会禁止所有的方法输出信息,其级别比FATAL要高。除前边示例中用到的五个方法,Logger还提供了这五个方法的重载,以在输出信息的同时抛出异常,以fatal方法为例:
log.fatal("Here is FATAL", new Exception("Exception"));
执行后输出信息:
FATAL - Here is FATAL
java.lang.Exception: Exception
at piv.zheng.log4j.test.Test.main(Test.java:24)
其他方法类似。此外,Logger还提供了log方法,该方法不针对任何输出级别,需要在调用时设置,例如:
log.log(Level.FATAL, "Here is FATAL");
log.log(Level.FATAL, "Here is FATAL", new Exception("Exception"));
虽然一般情况下log方法不如其它方法方便,但由于允许设置级别,因此log方法在很多时候反而比其它方法更灵活,甚至可以在输出级别为OFF时输出信息。不过log方法主要是给用户自定义的输出级别用的,而且设立OFF输出级别的目的也为了不输出任何信息,因此请不要在log方法中使用OFF来输出信息。
此外,Category的输出级别并非必须,若未设置,子Category会默认使用其父Category的输出级别,若父Category也没设置,就使用再上一级Category的设置,直到根Category为止。根Category默认输出级别为DEBUG,因此在示例中,若将"log.setLevel(Level.FATAL);"一行注释掉,则所有方法都会输出信息。
下面简单介绍一下Log4J中Category的继承关系。其实在Log4J中Category之间是存在继承关系的,根Category默认创建,是级别最高的Category,用户创建的Category均继承自它。而用户创建的Category之间也存在继承关系,例如:
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的子Category,而lx_y_z是lx_y的子Category。但lxy并不是lx的子Category。也许有点乱,下面来一个一个看。首先看与lx_y、lx_z对应的Category的名称"x.y"和"x.z","."前边的是什么,"x",这说明与名称为"x"的Category对应lx就是它们的父Category;而与lx_y_z对应的Category的名称"x.y.z",最后一个"."前边的是什么,"x.y",这说明lx_y是lx_y_z的父Category;至于lxy,由于与之对应的Category名称"xy"之间没有".",因此它是一个与lx同级的Category,其父Category就是根Category器。此外还有一种情况,例如有一个名称为"a.b"的Category,如果没有名称为"a"的Category,那么它的父Category也是根Category。前边说过,"."在Category名称中有特殊作用,其实它的作用就是继承。至此,为何使用类对象来创建Category也就不难理解了。
可是,仅有Category是无法完成信息输出的,还需要为Category添加Appender,即Category的输出源。前边的例子使用的是ConsoleAppender,即指定Category将信息输出到控制台。其实Log4J提供的Appender有很多,这里选择几常用的进行介绍。
1.org.apache.log4j.WriterAppender,可以根据用户选择将信息输出到Writer或OutputStream。
示例代码:
SimpleLayout layout = new SimpleLayout ();
//向文件中输出信息,OutputStream示例
WriterAppender appender1 = null;
try {
appender1 = new WriterAppender(layout, new FileOutputStream("test.txt"));
}
catch(Exception ex) {}
//向控制台输出信息,Writer示例
WriterAppender appender2 = null;
try {
appender2 = new WriterAppender(layout, new OutputStreamWriter(System.out));
}
catch(Exception ex) {}
//Category支持同时向多个目标输出信息
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender1);
log.addAppender(appender2);
log.debug("output");
这个示例由第一个示例修改而来,没有设置输出级别,而且向Category中添加了两个输出源,运行后会在控制台中输出"DEBUG - output",并在工程目录下生成test.txt文件,该文件中也记录着"DEBUG - output"。若要将test.txt文件放到其它路径下,例如f:,则将"test.txt"改为"f:/test.txt",又如e:下的temp文件夹,就改为"e:/temp/test.txt"。后边FileAppender、RollingFileAppender以及DailyRollingFileAppender设置目标文件时也都可以这样来写。
2.org.apache.log4j.ConsoleAppender,向控制台输出信息,继承了WriterAppender,前边的示例使用的就是它。
3.org.apache.log4j.FileAppender,向文件输出信息,也继承了WriterAppender。
示例代码:
SimpleLayout layout = new SimpleLayout();
//若文件不存在则创建文件,若文件已存在则向文件中追加信息
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");
这个示例也由第一个示例修改而来,运行后会在工程目录下生成test.txt文件,该文件中记录着"DEBUG - output"。再次运行程序,查看文件,则"DEBUG - output"有两行。
另外,FileAppender还有一个构造:
FileAppender(Layout layout, String filename, boolean append)
与示例的类似,只是多了一个boolean型的参数append。append参数是个开关,用来设置当程序重启,而目标文件已存在时,是向目标文件追加信息还是覆盖原来的信息,当值为true时就追加,这是FileAppender默认的,当值为false时则覆盖。此外,FileAppender还提供了setAppend方法来设置append开关。
4.org.apache.log4j.RollingFileAppender,继承了FileAppender,也是向文件输出信息,但文件大小可以限制。当文件大小超过限制时,该文件会被转为备份文件或删除,然后重新生成。文件的转换或删除与设置的备份文件最大数量有关,当数量大于0时就转为备份文件,否则(小于等于0)删除,默认的备份文件数量是1。转换备份文件非常简单,就是修改文件名,在原文件名之后加上".1",例如文件test.txt,转为备份文件后文件名为"test.txt.1"。但若同名的备份文件已存在,则会先将该备份文件删除或更名,这也与设置的备份文件最大数量有关,若达到最大数量就删除,否则更名。若备份文件更名时也遇到同样情况,则使用同样的处理方法,依此类推,直到达到设置的备份文件最大数量。备份文件更名也很简单,就是将扩展名加1,例如test.txt.1文件更名后变为test.txt.2,test.txt.2文件更名后变为test.txt.3。
示例代码:
SimpleLayout layout = new SimpleLayout();
//若文件不存在则创建文件,若文件已存在则向文件中追加内容
RollingFileAppender appender = null;
try {
appender = new RollingFileAppender(layout, "test.txt");
} catch(Exception e) {}
//限制备份文件的数量,本例为2个
appender.setMaxBackupIndex(2);
//限制目标文件的大小,单位字节,本例为10字节
appender.setMaximumFileSize(10);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output0");
log.debug("output1");
log.debug("output2");
程序运行后,会在工程目录下生成test.txt、test.txt.1和test.txt.2三个文件,其中test.txt内容为空,而后两个文件则分别记录着"DEBUG - output2"和"DEBUG - output1",这是怎么回事?原来由于目标文件大小被限制为10字节,而三次使用log.debug方法输出的信息都超过了10字节,这样就导致了三次备份文件转换,所以test.txt内容为空。而备份文件最大数量被设为2,因此第一次转换的备份文件就被删掉了,而后两次的则保存下来。此外,由于test.txt转换备份文件时是先转为test.txt.1,再转为test.txt.2,因此最后test.txt.2的内容是"DEBUG - output1",而test.txt.1是"DEBUG - output2",这点千万别弄混了。
另外,RollingFileAppender还提供了两个方法:
(1)setMaxFileSize,功能与setMaximumFileSize一样,但参数是字符串,有两种情况:一是仅由数字组成,默认单位为字节,例如"100",即表示限制文件大小为100字节;一是由数字及存储单位组成,例如"1KB"、"1MB"、"1GB",其中单位不区分大小写,分别表示限制文件大小为1K、1M、1G。
(2)rollOver,手动将目标文件转换为备份文件,使用起来较灵活,适用于复杂情况。
示例代码:
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();
这里没限制目标文件大小,但程序运行后,效果与上例相同。
5.org.apache.log4j.DailyRollingFileAppender,也继承了FileAppender,并且也是向文件输出信息,但会根据设定的时间频率生成备份文件。
时间频率格式简介:
'.'yyyy-MM,按月生成,生成时间为每月最后一天午夜过后,例如test.txt在2005年7月31日午夜过后会被更名为test.txt.2005-07,然后重新生成。
'.'yyyy-ww,按周生成,生成时间为每周六午夜过后,例如test.txt在2005年8月13日午夜过后会被更名为test.txt.2005-33,33表示当年第33周。
'.'yyyy-MM-dd,按天生成,生成时间为每天午夜过后,例如2005年8月16日午夜过后,test.txt会被更名为test.txt.2005-08-16。
'.'yyyy-MM-dd-a,也是按天生成,但每天会生成两次,中午12:00过后一次,午夜过后一次,例如test.txt在2005年8月16日12:00过后会被更名为test.txt.2005-8-16-上午,午夜过后会被更名为test.txt.2005-8-16-下午。
'.'yyyy-MM-dd-HH,按小时生成,例如test.txt在2005年8月16日12:00过后会被更名为test.txt.2005-8-16-11。
'.'yyyy-MM-dd-HH-mm,按分钟生成,例如test.txt在2005年8月16日12:00过后会被更名为test.txt.2005-8-16-11-59。
示例代码:
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");
编码完成后运行程序,等一分钟后再次运行,由于我是在2005年8月17日15:42分第一次运行程序的,因此工程目录下最终有两个文件test.txt和test.txt.2005-08-17-15-42。
6.org.apache.log4j.AsyncAppender,用于管理不同类型的Appender,也能实现同时向多个源输出信息,但其执行是异步的。
示例代码:
SimpleLayout layout = new SimpleLayout();
//向控制台输出
ConsoleAppender appender1 = null;
try {
appender1 = new ConsoleAppender(layout);
} catch(Exception e) {}
//向文件输出
FileAppender appender2 = null;
try {
appender2 = new FileAppender(layout, "test.txt");
} catch(Exception e) {}
//使用AsyncAppender实现同时向多个目标输出信息
AsyncAppender appender = new AsyncAppender();
appender.addAppender(appender1);
appender.addAppender(appender2);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
此外,AsyncAppender和Logger都提供了更多的方法来管理Appender,例如getAppender、getAllAppenders、removeAppender和removeAllAppenders,分别用来获取指定的Appender、获取全部Appender、移除指定的Appender以及移除全部Appender。
7.org.apache.log4j.jdbc.JDBCAppender,将信息输出到数据库。
示例代码:
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");
这里使用的数据库是MySQL 5.0.4beta,用户名root,密码11111111,我在其中建了一个库zheng,包含表log4j,该表只有一个字段msg,类型为varchar(300)。此外,本例用到的JDBC驱动可以从http://dev.mysql.com/downloads/connector/j/3.1.html下载,版本3.1.8a,下载mysql-connector-java-3.1.8a.zip文件后解压缩,需要其中的mysql-connector-java-3.1.8-bin.jar文件。下面再来看代码。由于JDBCAppender内部默认使用PatternLayout格式化输出信息,因此这里没用到SimpleLayout,而appender.setSql所设置的SQL语句就是PatternLayout所需的格式化字符串,故此其中才有"%m"这样的字符,有关PatternLayout的具体内容后边介绍。执行后,表log4j增加一条记录,内容为"output"。
8.org.apache.log4j.nt.NTEventLogAppender,向Windows NT系统日志输出信息。
示例代码:
SimpleLayout layout = new SimpleLayout();
NTEventLogAppender appender = new NTEventLogAppender("Java", layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
注意,要完成此示例,还需向C:\WINNT\system32文件夹(我的操作系统装在了C:\)中复制一个名为NTEventLogAppender.dll的文件。如果跟我一样用的是Log4J 1.2.11,实在对不住,Log4J 1.2.11并未提供该文件。虽然logging-log4j-1.2.11.zip文件解压缩后,其下的src\java\org\apache\log4j\nt文件夹中有一个make.bat文件执行后可以编译出该文件,但还需要配置,很麻烦。还好,条条大道通罗马,1.2.11不行,就换1.2.9,可以从http://apache.justdn.org/logging/log4j/1.2.9下载,下载后解压缩logging-log4j-1.2.9.zip文件,在其下的src\java\org\apache\log4j\nt文件夹中找到NTEventLogAppender.dll,复制过去就可以了。程序执行后,打开"事件查看器",选择"应用程序日志",其中有一条来源为Java的记录,这条记录就是刚才输出的信息了。
9.org.apache.log4j.lf5.LF5Appender,执行时会弹出一个窗口,信息在该窗口中以表格的形式显示。
示例代码:
LF5Appender appender = new LF5Appender();
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
由于LF5Appender不需要Layout格式化输出信息,因此这里没有设置。此外LF5Appender还提供了一个setMaxNumberOfRecords方法,用来限制信息在表格中显示的行数。
10.org.apache.log4j.net.SocketAppender,以套接字方式向服务器发送日志,然后由服务器将信息输出。
示例代码:
//指定要连接的服务器地址及端口,这里使用的是本机9090端口
SocketAppender appender = new SocketAppender("localhost", 9090);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
SocketAppender不需要设置Layout,因为SocketAppender不负责输出信息。那么如何看到信息输出的效果呢?这就需要SocketServer和SimpleSocketServer了。
示例代码1:
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"});
}
}
这是SocketServer的示例。SocketServer只有一个静态方法main,该方法意味着SocketServer不仅可以在代码中被调用,也可以用java命令执行。main方法只有一个参数,是个字符串数组,但要求必须有三个元素:元素一用来指定端口,本例为9090;元素二用来指定输出信息时需要的配置文件,该文件放在工程目录下,本例使用的test.properties内容如下:
log4j.rootLogger=, console
log4j.appender.console =org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.SimpleLayout
该配置指定SocketServer使用ConsoleAppender以SimpleLayout格式输出信息;元素三用来指定一个路径,以存放.lcf文件,我指定的是本机的G:/YPJCCK/Log4J文件夹。.lcf文件也是输出信息时使用的配置文件,格式与元素二所指定的配置文件一样,但test.properties是默认配置文件,即当.lcf文件找不到时才使用。那么.lcf文件如何命名呢?其实.lcf文件的名称并不是随意起的,当SocketAppender与SocketServer建立连接时,SocketServer就会获得SocketAppender所在计算机的IP地址与网络ID,并将其格式化成"网络ID/IP地址"这样的字符串,然后获取其中的网络ID作为.lcf文件的主名,例如"zhengyp/127.0.0.1",其中的"zhengyp"就是主文件名,而后再根据这个文件名来调用相应的.lcf文件。这意味着对不同的计算机可以提供不同的配置文件,使信息输出时有不同的效果。此外,SocketServer还默认了一个名为generic.lcf的文件,用于处理网络ID获取不到或其他情况,本例是用的就是这个文件,内容如下:
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格式输出信息。运行程序时请先运行SocketServer,再运行SocketAppender。SocketAppender运行结束后,就可以从SocketServer的控制台看到输出的信息了。
示例代码2:
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"});
}
}
这是SimpleSocketServer的示例,与SocketServer相比,只允许指定一个默认的配置文件,而无法对不同计算机使用不同的配置文件。
11.org.apache.log4j.net.SocketHubAppender,也是以套接字方式发送日志,但与SocketAppender相反,SocketHubAppender是服务器端,而不是客户端。
示例代码:
//指定服务器端口,这里使用的是本机9090端口
SocketHubAppender appender = new SocketHubAppender(9090);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
while (true) {
Thread.sleep(1000);
log.debug("output"); //输出信息
}
由于SocketHubAppender一旦运行就开始发送消息,而无论有无接收者,因此这里使用了while语句并将条件设为true以保证程序持续运行。不过为了保证性能,这里还使用了Thread.sleep(1000),这样程序每循环一次都休眠1秒,如果机器性能不好,还可以将值设的再大些。此外,由于SocketHubAppender也不负责输出信息,因此同样不需要设置Layout。那么如何看到信息输出的效果呢?这里我自己写了个客户端程序,代码如下:
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);
//调用配置文件
PropertyConfigurator.configure("test.properties");
//从套接字中恢复Logger,并输出信息
new Thread(new SocketNode(s, LogManager.getLoggerRepository())).start();
}
}
由于SocketHubAppender与SocketAppender一样,发送的也是SocketNode对象,因此编写该程序时参考了SocketServer的源码。此外,这里的配置文件直接使用了上例的test.properties文件。运行程序时请先运行SocketHubAppender,再运行客户端程序,然后从客户端的控制台就可以看到效果了。
13.org.apache.log4j.net.TelnetAppender,与SocketHubAppender有些类似,也是作为服务器发送信息,但TelnetAppender发送的不是SocketNode对象,而是Category输出的结果。
示例代码:
SimpleLayout layout = new SimpleLayout();
TelnetAppender appender = new TelnetAppender();
appender.setLayout(layout); //设置Layout
appender.setPort(9090); //设置端口号
appender.activateOptions(); //应用设置
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
while (true) {
java.lang.Thread.sleep(1000);
log.debug("output"); //输出信息
}
//appender.close();
注意最后一行被注释掉的代码,若该行代码执行,则TelnetAppender的资源会被清理,从而导致TelnetAppender无法继续运行。那么如何看到信息输出的效果呢?这里提供两种方法:方法一,使用Telnet工具,我使用的就是Windows自带的Telnet。运行TelnetAppender程序后,点击[开始]菜单->[运行],在"运行"框中输入"telnet",回车,telnet客户端弹出,这是一个命令行程序,输入命令"open localhost 9090",回车,然后就可以看到效果了。方法二,自己写程序,代码如下:
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与Socket绑定,以输出Socket获得的信息
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
//获得信息并输出
String line = in.readLine();
while (line != null) {
System.out.println(line);
line = in.readLine();
}
}
}
13.org.apache.log4j.net.SMTPAppender,向指定的电子邮件发送信息,但只能发送ERROR和FATAL级别的信息,而且还没提供身份验证功能。
示例代码:
SimpleLayout loyout = new SimpleLayout();
SMTPAppender appender = new SMTPAppender();
appender.setLayout(loyout); //设置Layout
appender.setFrom("zhengyp@126.com"); //设置发件人
appender.setSMTPHost("smtp.126.com"); //设置发送邮件服务器
appender.setTo("zhengyp@126.com"); //设置收件人
appender.setSubject("Log4J Test"); //设置邮件标题
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");
要运行此示例,还需要JavaMail 和JAF,前者是Sun推出的电子邮件类库,可以从http://java.sun.com/products/javamail/downloads/index.html下载,最新版本1.3.3,下载javamail-1_3_3-ea.zip压缩包后需要其中的mail.jar文件;后者全称是JavaBeans Activation Framework,提供了对输入任意数据块的支持,并能相应地对其进行处理,可以从http://www.sun.com/download中找到,最新版本1.1,下载jaf-1_1-ea.zip压缩包后需要其中的activation.jar文件。不过,程序运行后会抛出两次异常,分别是log.error和log.fatal方法导致的,失败的原因很简单,我用的邮件服务器需要身份验证。
14.piv.zheng.log4j.test.SMTPAppender,自定义的,依照Log4J提供的SMTPAppender修改而来,增加了身份验证功能,并去掉了对级别的限制。由于代码太长,所以放到了另一篇文章《SMTPAppender源码》中,有兴趣的请自行去查看。
示例代码:
SimpleLayout layout = new SimpleLayout();
SMTPAppender appender = new SMTPAppender(layout);
appender.setFrom("zhengyp@126.com"); //发件人
appender.setSMTPHost("smtp.126.com"); //发送邮件服务器
appender.setTo("zhengyp@126.com"); //收件人
appender.setSubject("Log4J Test"); //邮件标题
appender.setAuth("true"); //身份验证标识
appender.setUsername("zhengyp"); //用户名
appender.setPassword("1111111"); //密码
appender.activateOptions(); //应用设置
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
同样需要JavaMail 和JAF。程序运行后会发送一封邮件,快去查看一下自己的邮箱吧^_^
此外,Log4J还提供了SyslogAppender、JMSAppender(均在org.apache.log4j.net包下)以及更多的Appender,或者用来向Unix操作系统的syslogd服务发送信息,或者通过JMS方式发送信息,或者以其他方式发送信息。由于条件有现,就不再介绍了。
不过,在前边的示例中还使用了SimpleLayout和PatternLayout来格式化输出的信息,这里也简单介绍一下。
1.org.apache.log4j.SimpleLayout,一直用的就是它,输出的格式比较简单,就是"级别 - 信息"。
2.org.apache.log4j.HTMLLayout,以HTML格式输出信息。
示例代码:
HTMLLayout layout = new HTMLLayout();
layout.setTitle("Log4J Test"); //HTML页标题
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");
程序运行后会在工程目录下生成一个HTML页,可以用浏览器来查看。
3.org.apache.log4j.xml.XMLLayout,以XML格式输出信息。
示例代码:
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");
程序运行后会在工程目录下生成一个test.xml文件。
4.org.apache.log4j.TTCCLayout,输出信息的同时输出日志产生时间、相关线程及Category等信息。
示例代码:
TTCCLayout layout = new TTCCLayout();
//是否打印与TTCCLayout关联的Category的名称,默认为true,表示打印
layout.setCategoryPrefixing(true);
//是否打印当前线程,默认为true,表示打印
layout.setThreadPrinting(true);
//是否打印输出和当前线程相关的NDC信息,默认为true,表示打印
layout.setContextPrinting(true);
//设置日期时间格式
layout.setDateFormat("iso8601");
//设置时区
layout.setTimeZone("GMT+8:00");
//设置时区后需要调用此方法应用设置
layout.activateOptions();
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("output");
注意,TTCCLayout输出的时间格式及时区是可以设置的:
(1)setDateFormat,设置日期时间格式,有五个常用值:"NULL",表示不输出;"RELATIVE",输出信息所用的时间,以毫秒为单位,默认使用该值;"ABSOLUTE",仅输出时间部分;"DATE",按当前所在地区显示日期和时间;"ISO8601",按ISO8601标准显示日期和时间。这些字符串不区分大小写。此外,还可以使用时间模式字符来格式化日期时间,详细内容请参考J2SE文档中的java.text.SimpleDateFormat类。
(2)setTimeZone,设置时区,详细内容请参考J2SE文档中的java.util.TimeZone类和java.util.SimpleTimeZone类。但请注意,当日期格式为"RELATIVE"时,设置时区会造成冲突。
5.org.apache.log4j.PatternLayout,用模式字符灵活指定信息输出的格式。
示例代码:
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");
模式字符串简介:
%c:Category名称。还可以使用%c{n}的格式输出Category的部分名称,其中n为正整数,输出时会从Category名称的右侧起查n个".",然后截取第n个"."右侧的部分输出,例如Category的名称为"x.y.z",指定格式为"%c{2}",则输出"y.z"。
%C:输出信息时Category所在类的名称,也可以使用%C{n}的格式输出。
%d:输出信息的时间,也可以用%d{FormatString}的格式输出,其中FormatString的值请参考TTCCLayout的setDateFormat方法,但NULL和RELATIVE在%d中无法使用。
%F:输出信息时Category所在类文件的名称。
%l:输出信息时Category所在的位置,使用"%C.%M(%F:%L)"可以产生同样的效果。
%L:输出信息时Category在类文件中的行号。
%m:信息本身。
%M:输出信息时Category所在的方法。
%n:换行符,可以理解成回车。
%p:日志级别。
%r:输出信息所用的时间,以毫秒为单位。
%t:当前线程。
%x:输出和当前线程相关的NDC信息。
%X:输出与当前现成相关的MDC信息。
%%:输出%。
此外,还可以在%与模式字符之间加上修饰符来设置输出时的最小宽度、最大宽度及文本对齐方式,例如:
%30d{DATE}:按当前所在地区显示日期和时间,并指定最小宽度为30,当输出信息少于30个字符时会补以空格并右对齐。
%-30d{DATE}:也是按当前所在地区显示日期和时间,指定最小宽度为30,并在字符少于30时补以空格,但由于使用了"-",因此对齐方式为左对齐,与默认情况一样。
%.40d{DATE}:也是按当前所在地区显示日期和时间,但指定最大宽度为40,当输出信息多于40个字符时会将左边多出的字符截掉。此外,最大宽度只支持默认的左对齐方式,而不支持右对齐。
%30.40d{DATE}:如果输出信息少于30个字符就补空格并右对齐,如果多于40个字符,就将左边多出的字符截掉。
%-30.40d{DATE}:如果输出信息少于30个字符就补空格并左对齐,如果多于40个字符,就将左边多出的字符截掉。
五、Log4J进阶
了解以上内容后,就已经初步掌握Log4J了,但要想灵活使用Log4J,则还需要了解其配置功能。这里简单介绍一下。
1.org.apache.log4j.BasicConfigurator,默认使用ConsoleAppender以PatternLayout(使用PatternLayout.TTCC_CONVERSION_PATTERN,即"%r [%t] %p %c %x - %m%n"格式)输出信息。
示例代码:
BasicConfigurator.configure();
Logger log = Logger.getLogger(Test.class);
log.debug("output");
注意,BasicConfigurator及其它Configurator其实都只对根Category进行配置,但由于用户创建的Category会继承根Category的特性(声明,许多资料介绍Category继承关系时都主要在讨论输出级别,而事实上,Category间继承的不仅是输出级别,所有特性都可以继承),因此输出时仍会显示BasicConfigurator配置的效果。此外,还可以使用configure方法指定Appender,以自定义输出。BasicConfigurator允许同时指定多个Appender。
示例代码:
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");
这里用BasicConfigurator指定了两个Appender,即ConsoleAppender和FileAppender,程序运行后信息会在以SimpleLayout输出到控制台的同时以PatternLayout输出到test.log文件。若要清除这些Appender,可以调用BasicConfigurator的resetConfiguration方法。
2.org.apache.log4j.PropertyConfigurator,调用文本配置文件输出信息,通常使用.properties文件。配置文件以"键=值"的形式保存数据,注释以"#"开头。PropertyConfigurator和配置文件在介绍SocketAppender和SocketHubAppender时曾提到过。使用PropertyConfigurator可以避免硬编码。
示例代码:
PropertyConfigurator.configure("test.properties");
Logger log = Logger.getLogger(Test.class);
log.debug("output");
要完成该示例,还需要在工程目录下创建一个test.properties文件,内容如下:
##设置根Category,其值由输出级别和指定的Appender两部分组成
#这里设置输出级别为DEBUG
log4j.rootLogger=DEBUG,appender
##输出信息到控制台
#创建一个名为appender的Appender,类型为ConsoleAppender
log4j.appender.appender=org.apache.log4j.ConsoleAppender
#设置appender以SimpleLayout输出
log4j.appender.appender.layout=org.apache.log4j.SimpleLayout
此外,PropertyConfigurator也允许同时指定多个Appender,例如:
#这里没有设置输出级别,但指定了两个Appender
log4j.rootLogger=,appender1,appender2
#输出信息到控制台
log4j.appender.appender1=org.apache.log4j.ConsoleAppender
log4j.appender.appender1.layout=org.apache.log4j.SimpleLayout
#输出信息到文件
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
关于更多配置,网上示例很多,这里不再赘述。但要说明一件事,就是配置文件中的键是怎么来的。参照后一个示例,查看PropertyConfigurator源码,会发现"log4j.rootLogger"是定义好的,只能照写;而"log4j.appender"字样也可以找到,与指定的Appender名称appender1、appender2联系起来,log4j.appender.appender1和log4j.appender.appender2也就不难理解了;再看下去,还能找到"prefix + ".layout"",这样log4j.appender.appender1.layout也有了;可是log4j.appender.appender2.File 和log4j.appender.appender2.Append呢?还记得前边介绍FileAppender时曾提到的setAppend方法吗?其实FileAppender还有个getAppend方法,这说明FileAppender具有Append属性。那么File呢?当然也是FileAppender的属性了。至于log4j.appender.appender2.layout.ConversionPattern也一样,只不过FileAppender换成了PatternLayout。其实别的Appender和Layout的属性也都是这样定义成键来进行设置的。此外,定义键时,属性的首字母不区分大小写,例如"File",也可以写成"file"。
3.org.apache.log4j.xml.DOMConfigurator,调用XML配置文件输出信息。其定义文档是log4j-1.2.11.jar中org\apache\log4j\xml包下的log4j.dtd文件。与PropertyConfigurator相比,DOMConfigurator似乎是趋势。
示例代码:
DOMConfigurator.configure("test.xml");
Logger log = Logger.getLogger(Test.class);
log.debug("output");
要完成该示例,也需要在工程目录下创建一个test.xml文件,内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- 输出信息到控制台
创建一个名为appender的Appender,类型为ConsoleAppender -->
<appender name="appender" class="org.apache.log4j.ConsoleAppender">
<!-- 设置appender以SimpleLayout输出 -->
<layout class="org.apache.log4j.SimpleLayout"/>
</appender>
<!-- 设置根Category,其值由输出级别和指定的Appender两部分组成
这里设置输出级别为DEBUG -->
<root>
<priority value ="debug" />
<appender-ref ref="appender"/>
</root>
</log4j:configuration>
此外,DOMConfigurator也允许同时指定多个Appender,例如:
<?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>
<!-- 这里没有设置输出级别,但指定了两个Appender -->
<root>
<appender-ref ref="appender1"/>
<appender-ref ref="appender2"/>
</root>
</log4j:configuration>
由于以上两个示例是在PropertyConfigurator的两个示例基础上改的,而且也写了注释,因此这里只简单介绍一下<param>标记。<param>标记有两个属性,name和value,前者的值也是Appender或Layout的属性名,作用与log4j.appender.appender2.File这样的键一样。设置时,首字母同样不区分大小写,例如"File"也可以写成"file"。此外还请注意,使用这两段XML代码时应将中文注释去掉,或者把<?xml version="1.0" encoding="UTF-8" ?>中的UTF-8改成GBK或GB2312,否则会导致错误。这里使用的UTF-8是XML默认的字符集。
4.org.apache.log4j.lf5.DefaultLF5Configurator,默认使用LF5Appender来输出信息,需要调用log4j-1.2.11.jar中org\apache\log4j\lf5\config包下的defaultconfig.properties文件。
示例代码:
try {
DefaultLF5Configurator.configure();
}
catch(Exception e){}
Logger log = Logger.getLogger(Test.class);
log.debug("output");
下面讨论另外一个话题:Diagnostic Context。Diagnostic Context意为诊断环境,针对于多用户并发环境,在这种环境下,通常需要对每个客户端提供独立的线程以处理其请求,此时若要在日志信息中对客户端加以区分,为每个线程分别创建Category是个办法。但这样做并不高效,反而会导致大量资源被占用。Diagnostic Context所要解决的就是这个问题。Diagnostic Context会为当前线程提供一定空间,然后将信息保存到该空间供Category调用。与创建一个Category相比,这点信息所占的资源自然要少得多。
1.org.apache.log4j.NDC。NDC是Nested Diagnostic Context的简写,意为嵌套诊断环境,使用时提供一个堆栈对象来保存信息。堆栈的特点是数据后进先出、先进后出,即清理堆栈时,后保存的数据会被先清掉,而先保存的数据则被后清掉。
示例代码:
PatternLayout layout = new PatternLayout("%m %x%n");
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
String tmp = "zhengyp"; //模拟从客户端获取的信息
log.debug("Start");
NDC.push(tmp); //添加信息到堆栈中
log.debug("Before");
NDC.pop(); //将信息从堆栈中移除
log.debug("After");
NDC.remove(); //将当前线程移除,退出NDC环境
log.debug("End");
这里使用了PatternLayout来格式化信息,其模式字符%x就是用来输出NDC信息的。程序运行后会输出如下内容:
Start
Before zhengyp
After
End
可以看到,第二行输出时由于已向堆栈中添加了信息,因此"zhengyp"也会同时输出;而第三行输出时由于信息已被移除,因此就没再输出"zhengyp"。不过这个示例仅简单演示了NDC的用法,而没有显示出NDC的堆栈特性,所以下面再提供一个示例,代码如下:
TTCCLayout layout = new TTCCLayout();
ConsoleAppender appender = new ConsoleAppender(layout);
Logger log = Logger.getLogger(Test.class);
log.addAppender(appender);
log.debug("Start");
NDC.push("zhengyp"); //添加信息到堆栈中
log.debug("Test1");
NDC.push("192.168.0.1"); //向堆栈中追加信息
log.debug("Test2");
NDC.pop(); //从堆栈中移除信息,但移除的只是最后的信息
log.debug("Test3");
NDC.pop(); //再次从堆栈中移除信息
log.debug("Test4");
log.debug("End");
这里格式化输出信息使用的是TTCCLayout,还记得其setContextPrinting方法吗?程序运行后,从输出的信息就可以看到效果了。此外,NDC还提供了其他方法:
(1)get,获取堆栈中的全部信息。以上例为例,当输出Test2时,使用该方法会获得"zhengyp 192.168.0.1"。
(2)peek,获取堆栈中最后的信息。仍以上例为例,当输出Test1时会获得"zhengyp",Test2时为"192.168.0.1",而当输出Test3时由于"192.168.0.1"已被移除,"zhengyp"又成了最后的信息,因此获得的仍是"zhengyp"。
(3)clear,清空堆栈中的全部信息。
(4)setMaxDepth,设置堆栈的最大深度,即当前的信息可以保留多少,对之后追加的信息没有影响。当需要一次清掉多条信息时,使用setMaxDepth会比多次调用pop方便。
2.org.apache.log4j.MDC。MDC是Mapped Diagnostic Context的简写,意为映射诊断环境,提供了一个Map对象来保存信息。Map对象使用Key、Value的形式保存值。
示例代码:
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");
//添加信息到Map中
MDC.put("name", "zhengyp1");
MDC.put("ip", "192.168.1.1");
log.debug("Test1");
//添加信息到Map中,若Key重复,则覆盖之前的值
MDC.put("name", "zhengyp2");
MDC.put("ip", "192.168.1.2");
log.debug("Test2");
//将信息从Map中移除,此时信息不再输出
MDC.remove("name");
MDC.remove("ip");
log.debug("End");
这个示例演示了MDC的基本用法,格式化信息用的也是PatternLayout,模式字符为"%X",其格式必须为"%X{Key}"。其中Key就是向Map对象添加信息时put方法所用的Key,这里为name和ip。由于可以使用"%X{Key}"输出信息,因此MDC使用起来会比NDC更灵活。此外,MDC还提供了get方法来获取指定Key的信息。
六、小结
用了近半个月,终于大概掌握了Log4J。由于本文是边学边写的,目的是将Log4J的用法记录下来,而非提供一份中文参考,因此内容并不细致,但尽量提供了示例。不过到最后才发现,示例存在问题,其实Logger做为类的static成员比较恰当,而我为了图方便,竟直接写到了main方法中,这一点还请注意。
此外,这里再推荐一下《The Complete log4j Manual》,是对Log4J较详细的介绍,在网上可以找到,只不过是英文的。
发表评论
-
网站性能测试一
2012-05-14 13:24 1139在说明性能测试之前我 ... -
www.8brl.cn网站上线了,请大家多多指教
2012-02-02 13:12 1491http://www.8brl.cn经过一段时间的研发终于上线 ... -
SSH问题及解决之三 SpringSide3缺陷
2010-10-13 15:03 2439http://code.google.com/p/spring ... -
SSH问题及解决之二 级联配置
2010-10-11 14:06 3510系统开发过程中采用Hibernate+JPA: 在一个Grou ... -
java发送手机短信
2010-10-11 10:05 2121java领域实现向手机发短信有三种途径: 1. smsl ... -
MyEclipse+Tomcat编程技巧
2010-09-24 10:26 1070为了调试的方便我把公司maven结构的项目重组成MyEclip ... -
SSH问题及解决之一 Hibernate中实体属性存在关键字及p6spy的使用
2010-09-24 10:17 14691. 在使用Strust2+Spring3+Hibernate ... -
CXF的第一个例子helloWorld
2009-11-03 16:08 2804公司要采用WebService作为数据提供平台,经过研究决定使 ... -
Openfire架设自己的即时聊天服务器
2009-10-22 11:10 2804一、安装环境 操作系统:Windows XP Prefess ... -
提高Java程序的GC效率
2009-09-18 10:49 11081.java的GC程序是一个由JVM实现的对用户透明的程序, ... -
JAVA网络程序的安全
2009-09-18 10:40 9621.数据安全传输: 编码+加密+数字签名. java中提供 ... -
apache&tomcat集群
2009-09-01 13:28 1300搭建集群和实现负载平衡 部分参考: http://ispr ... -
字符串处理
2009-06-16 16:19 830许多开发人员在做项目 ... -
java.util.list操作
2009-06-15 14:15 1227如何让集合的长度能自动改变,能够增加/删除元素呢?这就是用 ... -
Log4J使用完全手册
2009-05-11 16:24 908Log4 J是Apache的一个开放源代码项目(http:// ... -
运行多个TestCase中的testsuite
2009-05-07 16:20 1350import junit.framework.TestSu ... -
只运行部分testcase
2009-05-07 16:18 741package com.ff.ldap; import ... -
struts + spring + hibernate整合事务配置的问题
2009-02-06 15:03 1248一个关于struts + spring + hibernate ... -
谈谈SSH开发中权限的控制
2008-08-03 15:27 2816通过编写一个继承RequestProcessor的MyRequ ... -
如何用Java对图片jpg, gif进行压缩
2008-08-03 15:19 1467如何用Java对图片jpg, gif进行压缩
相关推荐
Log4j是一款广泛使用的开源日志框架,由Apache软件基金会开发。本文将深入探讨Log4j的基本概念、配置与使用方法。 1. **什么是Log4j** Log4j是一个基于Java的日志记录工具,它提供了灵活的控制来记录日志信息,...
#### 四、在代码中使用Log4j 要在代码中使用Log4j,首先需要获取一个Logger实例。 - **语法**:`public static Logger getLogger(String name)` - **示例**: ```java import org.apache.log4j.Logger; ...
### Log4j 使用实例详解 #### 一、Log4j简介 Log4j 是一个开源的日志记录框架,由 Apache 软件基金会提供。它允许开发者根据需求配置日志等级,并灵活选择日志信息的输出方式。Log4j 的核心组件包括 Logger、...
**日志框架Log4j详解** Log4j是Apache组织提供的一款开源的日志记录工具,广泛应用于Java开发中。...通过阅读提供的"log4j使用方法"文档,你将能更深入地了解Log4j的配置和使用技巧,为你的开发工作带来便利。
### Log4j 使用详解 #### 一、Log4j简介 Log4j 是 Apache 的一个开源项目,通过使用 Log4j,开发者能够控制日志信息的输出等级及去向,从而更加灵活地处理日志信息。它具有强大的功能,简单的配置,并且能够支持...
#### 四、log4j 配置详解 1. **配置文件**: - `log4j.properties` 或 `log4j.xml` 文件用于定义 log4j 的配置规则。 - 配置文件通常放置在项目的根目录或者类路径下。 - 如果未指定配置文件的位置,则 log4j 会...
标题“log4j.rar log4j使用方法”暗示了我们要讨论的是一个关于log4j日志框架的压缩包文件,其中包含了如何使用log4j的指导资料。log4j是Java编程语言中广泛使用的开源日志记录工具,它允许开发者灵活地控制日志信息...
### log4j使用配置方法及项目中的应用 #### 概述与背景 在软件开发过程中,日志记录是一项至关重要的任务,它不仅帮助开发者监控代码执行状态,还为后续的故障排查、性能优化和安全审计提供了关键信息。传统的做法...
### log4j 使用简介 #### 一、Log4j 概述 Log4j 是一个由 Apache 提供的开源项目,专门用于实现日志记录功能。它为开发者提供了灵活的日志记录方式,允许通过多种不同的格式来记录每一条日志信息,并且能够将这些...
以下是Log4j的核心组成部分、使用方法以及日志分级的详细解释。 1. **LOG4J组成**: - **Logger**:是Log4j的核心,负责决定哪些日志信息应该被记录和哪些应该被忽略。它根据预定义的级别来过滤日志,由`org....
### 四、使用Log4j 在Java代码中,我们可以使用以下方式调用Log4j记录日志: ```java import org.apache.log4j.Logger; public class MyClass { private static final Logger logger = Logger.getLogger(MyClass...
Apache Log4j 是一个广泛使用的开源日志框架,它为Java应用程序提供了强大的日志记录功能。Log4j 的主要优点在于它的灵活性和可配置性,允许开发者根据需要调整日志的输出目的地、格式以及级别。 **配置Log4j** ...
这个"log4j使用实例.zip"压缩包包含了关于如何在Java项目中配置和使用Log4j的示例代码,非常适合Java后端初学者学习。 首先,Log4j的核心组件包括Logger、Appender和Layout。Logger是日志信息的生产者,负责生成...
"Log4j使用手册和API"则明确指出了文档的核心内容,Log4j是一个广泛使用的Java日志库,它的API提供了丰富的日志记录功能,包括不同级别的日志输出(如DEBUG, INFO, WARN, ERROR等)、自定义日志格式、日志分发等。...
#### 四、使用Log4j ##### 4.1 概述 Log4j是另一个非常流行的Java日志框架,由Apache基金会维护。它比JCL更加强大,支持更多的特性,比如多级日志记录、动态日志文件配置等。Log4j的核心优势在于其灵活性和强大的...
Log4j 建议只使用四个级别,优先级从高到低分别是 ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。 在配置文件中,我们可以定义多个日志信息输出目的地,例如...
【log4j 使用介绍】 log4j 是一个广泛使用的 Java 日志框架,它提供了一种高效、灵活的方式来记录应用程序中的事件。这篇介绍旨在帮助初学者理解 log4j 的核心概念和使用方法。 **1. 简介** 日志记录在软件开发中...
Log4j是一个广泛使用的Java日志框架,由ApacheJakarta项目开发。它为开发者提供了灵活且功能丰富的日志记录机制,使应用程序中的日志管理变得简单。本教程将深入介绍如何使用Log4j进行日志记录,包括配置、基本用法...