Log4j

Log4j的基本应用

Introduction
        Log4j   是一个开源代码的项目(Open   source   project),它使开发人员能最大限度的灵活控制程序调试信息的输出,这一点它是通过额外的配置文件实现的。而且log4j   开发包很容易上手,同时可能会使一些开发人员上瘾。
        绝大多数的大型开发项目都有它们自己专门用于记录程序日志和对程序跟踪调试的API。许多经验证明,调试信息的管理在软件开发过程中扮演了十分重要的角色。日志管理通常提供了以下的几点好处:首先,它能在程序运行过程中精确的提供运行时的上下文(context)方便我开发人员找到   BUG,一旦在程序中加入了Log   输出的代码,程序运行过程中就能自动的生成并输出logging信息。其次,log信息可以输出到不同的地方(控制台,文件,日志服务器等等)以备时候研究。最后,除了在开发过程中发挥它的作用外,一个功能足够强大的日志记录开发包能当作一个审计工具(audit   tool)使用。
        为了能够适应以上的规律,早在1996年   EU   SEMPER(Secure   Electronic   Marketplace   for   Europe)   项目组决定开发自己的日志管理开发包(tracing   API),经过了无数次的改动和功能加强,最终诞生了log4j   ——   一个十分广受欢迎的java   logging   开发工具包。这个开发工具包的发行遵循   IBM   Public   License,certified   by   the   open   source   initiative。
        同时,Logging   确实也存在一些缺陷,比如,它影响了程序运行的速度,增加了代码的开销,增加了许多无谓的输出。为了减少这些负面因数的影响,log4j   被设计得尽量的高效和灵活。因为,很少有哪个应用程序(系统)把记录日志当作主要的功能,log4j的开发人员尽量使log4j容易理解和使用。
        这篇文章首先描述log4j的大致框架和重要的组成部分。作者通过一些简单的例子来说明如何使用log4j。

Categories,appenders,and   layouts

Log4j最主要的三大基本构件:

1.Categories(对log信息进行分类筛选,通俗的讲就是什么信息应该被输出,什么log信息应该被忽略)
2.Appenders(定义了log信息输出的设备,log信息应该被输出到什么地方,控制台、文件、网络设备等)
3.Layouts(对log信息进行格式化)
软件开发人员能通过这三大构件实现根据日志的类型和优先级进行记录,并且能在程序运行时去控制log   信息输出的格式(formatted)和往什么地方输出(控制台、log文件),让我们依次去了解他们。

Category   hierarchy
        与System.out.println   方式输出调试信息相比,一个专门的日志控制工具(logging   API)的最组要优点在于它能够在适当的时候关闭一些调试信息输出以不影响其他人的调试。这种能力的实现决定于程序开发人员能够根据一定的标准对这些logging   信息进行分类。
        基于以上原则,org.apache.log4j.Category   类实现了整个log4j包的核心,调试信息被根据一般开发人员的习惯进行了分类并命名。和java开发包的分类规则类似,(a   category   is   said   to   be   a   parent   of   another   category   if   its   name   followed   by   a   dot,   is   a   profix   of   the   child   category   name.   )   比如,命名为com.foo   的category   是被命名为com.foo.Bar   的category的parent.就象我们习惯的java是java.util   的parent,并且是java.util.vector   的ancestor一样。
        最上层的Category被称为根(root   category),根category有两个特点:
(1)它总是存在(It   always   exists)
(2)它的名字不能被直接得到
        在Category   类里面,我们通过getRoot()方法得到root   category。而静态方法getInstance()实例化所有其他的Category.   getInstance()通过调用时传递的叁数得到该被实例化的Category的实例名。   Category类的其它方法在下面列出:
package   org.apache.log4j;
public   Category   class   {
//   Creation   &   retrieval   methods:
public   static   Category   getRoot();
public   static   Category   getInstance(String   name);
//   printing   methods:
public   void   debug(String   message);
public   void   info(String   message);
public   void   warn(String   message);
public   void   error(String   message);
//   generic   printing   method:
public   void   log(Priority   p,   String   message);
}
        我们可以通过org.apache.log4j.Priority类中的set   方法定义一个Category的优先级。(设置的Category的优先级别和传统的Unix   Syslog   定义的级别一样)。但log4j仅鼓励使用以下的四个优先级别:ERROR,WARN,INFO   和DEBUG,   这四个级别的优先级为ERROR> WARN> INFO> DEBUG。但org.apache.log4j.Propority中的set方法提供了更高的灵活性,即用户可以通过Priority类的子类去定义自己的优先级。如果一个   category没有被定义自己的优先级别,它将继承最接近的祖先所定义的优先级别。这样就保证了所有的Category最终都继承一个优先级别(因为根category总是有一个默认的优先级定义)。
        我们获得了一个Category   的实例以后,就可以通过调用以下的方法输出调试信息:
1.error()   输出ERROR级别的调试信息
2.warn()   输出WARN级别的调试信息
3.info()   输出INFO   级别的调试信息
4.debug()   输出   DEBUG   级别的调试信息
5.log()   普通的LOG   信息
        根据定义,以上的5个方法将自己判断输出信息的优先级别。打个比方,假如c   是一个Category   类的实例,那么语句c.info(“……”)   只有在优先级达到INFO级的输出请求(A   logging   request)时候才被执行。
        日志输出请求(A   logging   request)   是指当调试级别高于或等于该级别的时候才被允许执行。否则,被认为这个日志输出请求为不允许,一个没有被定义优先级别的category   将自动根据层次关系从它的parent   或   ancestor   继承优先级别。
        通过下面的程序段,你将发现这个规则:
//   get   a   category   instance   named   "com.foo "
Category   cat   =   Category.getInstance( "com.foo ");
//   Now   set   its   priority.
cat.setPriority(Priority.INFO);
Category   barcat   =   Category.getInstance( "com.foo.Bar ");
//   This   request   is   enabled,   because   WARN   > =   INFO.
cat.warn( "Low   fuel   level. ");
//   This   request   is   disabled,   because   DEBUG   <   INFO.
cat.debug( "Starting   search   for   nearest   gas   station. ");
//   The   category   instance   barcat,   named   "com.foo.Bar ",
//   will   inherit   its   priority   from   the   category   named
//   "com.foo "   Thus,   the   following   request   is   enabled
//   because   INFO   > =   INFO.
barcat.info( "Located   nearest   gas   station. ");
//   This   request   is   disabled,   because   DEBUG   <   INFO.
barcat.debug( "Exiting   gas   station   search ");

        如果通过一样的叁数多次调用getInstance()   方法将返回第一次调用时生成的category对象的引用。因此,通过这种方式我们可以配置一个   Category   然后不需要其他额外的处理就能在其他地方得到这个对象。Category   能通过任何顺序创建和配置。有一点值得注意的就是一个parent   category回自动找到并连接属于他的child   的cagetory,即使,他是在他的child   category之后被定义。Log4j通常是在程序或类被初始化的时候被设定的。最好的方式是通过额外的配置文件去定义log4j的配置信息,接下来我们将简要的讨论这方面的内容。
        Log4j允许通过程序构件的名称去定义Category的名字。这样我们就可以为每一个java类文件名(包含该类的包名)定义一个Category,这是一种有用并且直观的category   实例名的定义方式。并且这样我们就能方便的从大量log信息中判断出它们各自的来源。当然了,那不是硬性规定的,实际上Log4j没有对设置category的实例名做什么限制,程序员可以根据自己的喜好随意定义category的实例名。

Appenders   and   layouts

        通过category实现有灵活控制log   信息的输出仅是log4j包吸引人的方面之一,Log4j的Appenders   类还实现了将log   信息输出到许多不同的输出设备中。目前,log4j   的Appenders   可以实现将log信息输出到以下几种输出设备中:
1.Console
2.Files  
3.GUI   components
4.Remote   socket   servers
5.NT   Event   Loggers
6.Remote   UNIX   Syslog   daemons
        一个Category可以同时被对个appenders   引用,也就是说,一个Category的log信息可以同时输出到多个输出设备中。打个比方,假如你使一个root   category   的log   信息输出到console   ,那么很自然的,这个root   category   所有被允许输出的log信息将被输出到console,接着,你又定义了一个child   category   ,并且设定这个child   category   的log   信息输出到File   appenders,我们假定这个child   category   的名字为C   ,   那么   C   以及C   的child   category   的所有被允许的Log信息将被同时输出到   console   appender   和   File   appender。同时值得注意的是,我们可以覆盖这个默认的设置从而避免由于category的继承关系而导致一些戎余appender   被自动配置到parent   category   中。
        通常的,用户不但需要自己指定log信息的输出设备,而且,他们还需要配置log信息的输出格式   ,这个功能是通过和appender类相关的layout类实现的。虽然appender类只是实现了如何将一个格式化过的log信息输出到相关的输出设备,Layout类能够根据用户的需要去格式化log信息的输出。   PatternLayout   是log4j发行包中的一部分,它提供了和C   语言中的printf   方法一样的灵活性去让程序员自己格式化log信息。
        例如,格式化语句   %r   [%t]   %-5p   %c   -   %m%n   将产生以下的输出格式:
176   [main]   INFO   org.foo.Bar   –   Located   nearest   gas   station
格式说明如下:
第一个区域   “176”   等于程序从开始运行到打印出这个信息所运行的毫秒数
第二个区域   “[main]”   是输出这个log   信息的线程
第三个区域   “INFO”   是这个log语句所属的优先级
第四个区域   “org.foo.Bar”   是输出这个log信息的Category   的实例名
第五个区域   “Located   nearest   gas   station”   这条log信息的内容
Configuration

        在程序中添加这些Log信息的输出语句所须的工作量是不可小视的,调查资料表明,在程序开发过程中,用于输出调试信息所需的代码量估计要占到整个应用程序总代码量的%4左右。因此,即便是最一般大小的应用程序也需要至少包含有几千行的log语句。这样,不需要我们人工地去维护这些log输出的语句就变的十分重要了。
        虽然,Log4j开发包能够在代码中去灵活控制log输出,但是通过配置文件去实现Log信息的控制比在代码中实现灵活得多。目前,Log4j的配置文件支持XML格式和JAVA   properties   (key=value)   格式。
        下面我们举个例子:
import   com.foo.Bar;
//   Import   log4j   classes.
import   org.apache.log4j.Category;
import   org.apache.log4j.BasicConfigurator;
public   class   MyApp   {
//   Define   a   static   category   variable   so   that   it   references   the
//   Category   instance   named   "MyApp ".
static   Category   cat   =   Category.getInstance(MyApp.class.getName());
public   static   void   main(String[]   args)   {
//   Set   up   a   simple   configuration   that   logs   on   the   console.
BasicConfigurator.configure();
cat.info( "Entering   application. ");
Bar   bar   =   new   Bar();
bar.doIt();
cat.info( "Exiting   application. ");
}
}

        正象大家所看到的,MyApp类首先引入log4j包中的相关类,然后定义一个命名为MyApp的静态的Category实例,大家注意到这个Category的名字恰好和MyApp的类名一样。
MyApp类还使用到了被定义在com.foo包中的Bar类:
package   com.foo;
import   org.apache.log4j.Category;
public   class   Bar   {
static   Category   cat   =   Category.getInstance(Bar.class.getName());
public   void   doIt()   {
cat.debug( "Did   it   again! ");
}
}

        类MyApp是通过调用了BasicConfigurator.configure()   方法获得了log4j的默认设置的。这个方法将root   Category   设定了一个ConsoleAppender让log信息输出到console。并且log信息的输出被默认的格式化为   %-4r   [%t]   %-5p   %c   %x   -   %m%n。还有一点值得注意的是root   category   的优先级别被默认的定义为Priority.DEBUG级。
        MyApp程序的log输出为:
0   [main]   INFO   MyApp   -   Entering   application.
36   [main]   DEBUG   com.foo.Bar   -   Did   it   again!
51   [main]   INFO   MyApp   -   Exiting   application.


        MyApp类通过调用BasicConfigurator.configure()   方法得到log4j的默认配置,其它的类只需要引入   org.log4j.Category   类并得到一个Category   就可以输出log了。
        上面的例子总是输出同样的log信息(除非改变源代码并重新编译,这和直接用System.out.println()函数输出调试信息是一样的)。但幸运的是,log4j允许我们对MyApp程序稍加修改就可以在程序运行时对log信息进行控制。下面是修改后的版本:
import   com.foo.Bar;
import   org.apache.log4j.Category;
import   org.apache.log4j.PropertyConfigurator;
public   class   MyApp   {
static   Category   cat   =   Category.getInstance(MyApp.class.getName());
public   static   void   main(String[]   args)   {
//   BasicConfigurator   replaced   with   PropertyConfigurator.
PropertyConfigurator.configure(args[0]);
cat.info( "Entering   application. ");
Bar   bar   =   new   Bar();
bar.doIt();
cat.info( "Exiting   application. ");
}
}
        这个例子中MyApp通过传给PropertyConfigurator()   方法的叁数去指示程序去读取log配置文件。
        下面的是一个配置文件的简单例子,这个配置文件的结果将和log4j的默认配置相同:
#   Set   root   category   priority   to   DEBUG   and   its   only   appender   to   A1.
log4j.rootCategory=DEBUG,   A1
#   A1   is   set   to   be   a   ConsoleAppender   which   outputs   to   System.out.
log4j.appender.A1=org.apache.log4j.ConsoleAppender
#   A1   uses   PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r   [%t]   %-5p   %c   %x   -   %m%n

        假设我们不在需要包com.foo   下的类的log输出,可以把log配置文件改成下面的形式:
log4j.rootCategory=DEBUG,   A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
#   Print   the   date   in   ISO   8601   format
log4j.appender.A1.layout.ConversionPattern=%d   [%t]   %-5p   %c   -   %m%n
#   Print   only   messages   of   priority   WARN   or   above   in   the   package   com.foo.
log4j.category.com.foo=WARN
        使用新的配置文件将得到如下的log输出:
2000-09-07   14:07:41,508   [main]   INFO   MyApp   -   Entering   application.
2000-09-07   14:07:41,529   [main]   INFO   MyApp   -   Exiting   application.

        因为category   com.foo.Bar   没有定义优先级别,它就只能从包com.foo中继承优先级别,而在配置文件中,我们给com.foo   category   定义的优先级别为WARN。所以,com.foo.Bar类中的doIt()   方法的log输出就被禁止了。
        下面,我们在举个例子,下面的配置文件使log信息同时输出到控制台和log文件:
log4j.rootCategory=debug,   stdout,   R
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#   Pattern   to   output   the   caller 's   file   name   and   line   number.
log4j.appender.stdout.layout.ConversionPattern=%5p   [%t]   (%F:%L)   -   %m%n
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=example.log
log4j.appender.R.MaxFileSize=100KB
#   Keep   one   backup   file
log4j.appender.R.MaxBackupIndex=1
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p   %t   %c   -   %m%n

调用这个增强版的配置文件将在控制台上看到如下的输出信息:
INFO   [main]   (MyApp2.java:12)   -   Entering   application.
DEBUG   [main]   (Bar.java:8)   -   Doing   it   again!
INFO   [main]   (MyApp2.java:15)   -   Exiting   application.

        同时,上面的配置文件中我们在root   category   增加了第二个输出对象   FileAppender   。log信息将同时被输出到example.log   文件中。当example.log   文件达到100KB   的是后,example.log文件将自动被更名为example.log.1   同时生成一个新的内容为空的example.log   文件。(在英语中被称为rolled   over)   。
        有一点值得强调的是,我们在修改log   信息输出时并不需要重新编译源程序。我们还可以简单的通过修改log配置文件就可以把log信息输出到Unix   Syslog   daemon   中或输出到NT   Event   logger   中。甚至我们还可以把log信息输出到远程的专用LOG服务器中。

Nested   diagnostic   contexts

        当前我们开发的许多系统都需要处理多客户端并发的问题。在这种典型的并发系统中,通常是由不同的线程去分别处理不同的客户端请求的。Log开发工具包在这种情况下发挥了很重要的作用,一般情况下考虑,lot4j可以分别给不同的线程创建不同的Category   从而能够从许多的Log信息中区分出那些log输出是属于某个线程的。但这种方式极大地加重了服务器的负担。
为了节约系统开销,log4j的设计者对处理同一类别任务的线程只定义一个Category   让他们共同使用,然后在Log信息中增加能够足以区分不同客户端请求的信息。这是通过NDC(Nested   Diagnostic   Context)类实现的:
public   class   NDC   {
//   Used   when   printing   the   diagnostic
public   static   String   get();
//   Remove   the   top   of   the   context   from   the   NDC.
public   static   String   pop();
//   Add   diagnostic   context   for   the   current   thread.
public   static   void   push(String   message);
//   Remove   the   diagnostic   context   for   this   thread.
public   static   void   remove();
}
        NDC   类为每一个线程单独维护一个保存线程上下文的堆栈。大家注意到,org.apache.log4j.NDC   类中所有的方法都是静态的。一旦NDC功能被使用,这些线程上下文堆栈里的信息将被自动地添加到log信息中,而用不着用户去干涉。程序员需要做的只是将正确的信息保存到NDC堆栈中,这是通过push()   和   pop()方法来实现的。
        为了更进一步说明这一点,我们举个例子。假设我们有一个Servlet   程序需要同时处理多个客户端的请求,这个Servlet程序在接到客户端的请求时首先建立创建一个新的线程,然后分配一个用来保存处理该请求的上下文的NDC堆栈,该上下文可能是发出请求的客户端的主机名、IP地址或其他能从请求信息中得到的并能区分不同客户端的信息,在WEB   服务器中这些信息一般是在Cookies中维护的。这样即便这个Servlet程序可能同时要处理多个客户端的请求,这些Log信息仍然能够被区分开来,因为不同的客户端处理线程具有不同的NDC堆栈。这看起来就象为不同的客户端请求分别实例化不同的category一样。
Performance

        一些人反对在程序中使用log开发工具包是因为他们认为,log处理增加了程序的开销并影响程序执行的速度。这种看法也有道理,因为即便是一般大小的应用程序至少也需要包含几千个log输出。这是所有log开发工具需要努力解决的问题。Log4j秉承的设计原则是:速度和效率第一,灵活性第二。
        然而,用户仍然需要清楚的了解下面这些与性能相关的数据:
1.   Logging   performance   when   logging   is   turned   off.
当log被禁止时,应用程序仍然需要在方法的辨别和实例化一些叁数上做一些多余的系统开销。如下面的程序段:
cat.debug( "Entry   number:   "   +   i   +   "   is   "   +   String.valueOf(entry[i]));
即便是log被禁止,变量i   和数组   entry[i]   仍然被初始化。为了尽量地减少这种开销我们最好把以上的代码段改为:
if(cat.isDebugEnabled()   {
cat.debug( "Entry   number:   "   +   i   +   "   is   "   +   String.valueOf(entry[i]));
}
        这样,程序首先判断log功能是否开启,然后再决定是否应该实例化这些在   Log   代码段里面的变量。然而即便如此,当log功能开启时,程序仍需要做一次多余的判断,但相比之下,isDebugEnabled()函数的执行时间只有整个log语句执行时间的1%。
        在log4j中,所有log信息的处理主要是由Category类来实现的。Category被设计成类而不是接口,主要是为了减少程序调用的开销,但这是以牺牲接口所能带来的灵活性为代价的。
2.   The   Performance   of   deciding   whether   to   log   or   not   to   log   when   logging   is   turned   on.
        接下来,最影响性能的因素是Category   的层次关系。当log功能被打开时,log4j仍然需要根据不同的Log请求去判断该log信息是否需要输出。并且,有些Category实例化时可能并没有被设定Log信息的优先级别,这种Category   必须从他的上一层Category上继承优先级,为了得到他们的优先级别设定,一些Category可能需要搜索他的Parent   category   甚至是它的ancestor   category。
        Log4j在这方面做了很大的努力,以便使层次间的获得优先级设定的搜索尽可能的快速。
3.   Actual   logging
最后一点可能对性能会产生较大影响的就是对log信息的格式化过程了。Log4j也充分地注意到了这一点,并做了优化。通常情况下,一个典型的格式化语句可能需要使用100到300微秒的处理时间。
        虽然,在Log4j开发工具包的设计过程中设计者尽量包含尽可能多的功能特性,但速度一直是他们首要考虑的因素。为了提升运行速度,log4j的许多部件曾经不止一次的被重写过。
Examples   in   the   release   package
        目前log4j的最新版本号为1.2.4。发行包所带的例子放在examples   目录下,下面简要的介绍一下发行包所附带的例子:
Trivial.java
package   sample;

import   org.apache.log4j.Category;
import   org.apache.log4j.Logger;
import   org.apache.log4j.BasicConfigurator;
import   org.apache.log4j.NDC;
public   class   Trivial   {
static   Logger   cat   =   Logger.getLogger(Trivial.class.getName());
public   static   void   main(String[]   args)   {
BasicConfigurator.configure();
NDC.push( "Client   #45890 ");  
cat.info( "Awake   awake.   Put   on   thy   strength. ");
Trivial.foo();
InnerTrivial.foo();
cat.info( "Exiting   Trivial. ");  
}
static   void   foo()   {
NDC.push( "DB ");  
cat.debug( "Now   king   David   was   old. ");  
NDC.pop();  
}
static   class   InnerTrivial   {
static   Category   cat   =   Category.getInstance(InnerTrivial.class.getName());

static   void   foo()   {
cat.info( "Entered   foo. ");  
}
}
}
        在编译运行这个例子以前,确保将log4j解压缩目录下的/dist/lib/   log4j-1.2.4.jar   包加到CLASSPATH中,然后编译,运行该程序,将在控制台上得到如下的输出信息:
0   [main]   INFO   sample.Trivial   Client   #45890   -   Awake   awake.   Put   on   thy   strength.
10   [main]   DEBUG   sample.Trivial   Client   #45890   DB   -   Now   king   David   was   old.
40   [main]   INFO   sample.Trivial$InnerTrivial   Client   #45890   -   Entered   foo.
40   [main]   INFO   sample.Trivial   Client   #45890   -   Exiting   Trivial.
1)   首先引入log4j包中的Category类、Logger类(Logger类是Category类的子类,将逐渐替代Category类)、BasicConfigurator类、NDC类。
2)   实例化一个静态的Logger类的实例,实例名为sample.Trivial(通过Trivial.class.getName()方法得到)。
3)   通过调用BasicConfigurator.configure()方法得到Log4j的默认配置(包括log信息优先级别、输出设备等)。
4)   通过调用NDC的push()方法将线程上下文压入NDC堆栈中。
5)   调用Logger   的info()方法输出INFO级别的log信息。
注:NDC堆栈内的信息将被保留除非调用了pop()方法。大家注意到在Trivial类里定义了一个名为InnerTrivial   的Inner   Class   并重新获得了一个Category   ,但InnerTrivial   中并不需要调用BasicConfigurator.configure()方法得到基本配置就可以直接调用info()函数输出log信息了,这是因为两个类中的Category具有相同的root   Category   ,并都从root   Category继承了配置信息。Sort.java和SortAlgo.java   给出了如何使用配置文件的例子。

简单的如下:
=============================================
import   org.apache.log4j.PropertyConfigurator;
import   org.apache.log4j.Logger;

public   class   log
{
    public   static   void   main(String[]   argv)   throws   Exception
    {
        Logger   logger   =   Logger.getLogger( "MYLOG ");
        PropertyConfigurator.configure(System.getProperty( "log4j.configuration "));
        while(true)
        {
            logger.info( "My   info ");
            logger.debug( "My   debug ");
            logger.error( "My   error ");
            Thread.sleep(3000);
        }
    }
}

=================log4j.config======================================
####   Use   two   appenders,   one   to   log   to   console,   another   to   log   to   a   file
log4j.rootLogger=debug,   stdout   ,R

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{yyyy-mm-dd   hh:mm},%6.6r]%-5p[%t]%x(%F:%L)   -   %m%n

log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.file=syslog.log
log4j.appender.R.datePattern= '. 'yyyy-MM-dd-HH-mm
log4j.appender.R.append=true
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=[%d{yyyy-mm-dd   hh:mm},%6.6r]%-5p[%t]%x(%F:%L)   -   %m%n

========================================================
java.exe   -Dlog4j.configuration=log4j.config     -classpath   "./;./log4j-1.2.4.jar "   log

 

 

 


 


 

1.1准备工作
一。Tomcat已正确配置与使用。
二。软件下载:log4j------http://www.apache.org/dist/jakarta/log4j/jakarta-log4j-1.2.8.zip

1.2.   Log4j简介

在强调可重用组件开发的今天,除了自己从头到尾开发一个可重用的日志操作类外,Apache为我们提供了一个强有力的日志操作包-Log4j。
Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX   Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
此外,通过Log4j其他语言接口,您可以在C、C++、.Net、PL/SQL程序中使用Log4j,其语法和用法与在Java程序中一样,使得多语言分布式系统得到一个统一一致的日志组件模块。而且,通过使用各种第三方扩展,您可以很方便地将Log4j集成到J2EE、JINI甚至是SNMP应用中。本文介绍的Log4j版本是1.2.8,怎样通过一个配置文件来灵活地进行配置,主要的应用平台是Tomcat4.

1.3。Log4j的配置。

首先到jakarta下载一个log4j的组件。把jakarta-log4j-1.2.8/dist/lib下的log4j-1.2.8.jar文件copy到classpath指定的目录下!可以是Tomcat的common/lib目录下,也可以是你需要用到log4j的application下的lib目录。
1.4在Application目录下的web.xml文件加入以后代码

log4j
com.apache.jakarta.log4j.Log4jInit

log4j
/WEB-INF/log4j.properties

1


这段代码的意思是说,在Tomcat启动时加载com.apache.jakarta.log4j.Log4jInit这个名叫Log4jInit.class这个类文件。其中Log4jInit.class的源代码如下

package   com.apache.jakarta.log4j;
import   org.apache.log4j.PropertyConfigurator;
import   javax.servlet.http.HttpServlet;
import   javax.servlet.http.HttpServletRequest;
import   javax.servlet.http.HttpServletResponse;
public   class   Log4jInit   extends   HttpServlet   {

public   void   init()   {
String   prefix   =   getServletContext().getRealPath( "/ ");
String   file   =   getInitParameter( "log4j ");
//   if   the   log4j-init-file   is   not   set,   then   no   point   in   trying
System.out.println( "................log4j   start ");
if(file   !=   null)   {
PropertyConfigurator.configure(prefix+file);
}
}
public   void   doGet(HttpServletRequest   req,   HttpServletResponse   res)   {
}
}
这段代码很简单,可以看出,在加载的过程中,程序会读取/WEB-INF/log4j.properties这个文件
这个文件就是本文的重点,也就是log4j的配置文件。

#   Set   root   logger   level   to   DEBUG   and   its   only   appender   to   A1  
#log4j中有五级logger  
#FATAL   0  
#ERROR   3  
#WARN   4  
#INFO   6  
#DEBUG   7  
#配置根Logger,其语法为:
#log4j.rootLogger   =   [   level   ]   ,   appenderName,   appenderName,   …
log4j.rootLogger=INFO,   A1   ,R
#这一句设置以为着所有的log都输出
#如果为log4j.rootLogger=WARN,   则意味着只有WARN,ERROR,FATAL
#被输出,DEBUG,INFO将被屏蔽掉.
#   A1   is   set   to   be   a   ConsoleAppender.  
#log4j中Appender有几层如控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX   Syslog守护进程等
#ConsoleAppender输出到控制台  
log4j.appender.A1=org.apache.log4j.ConsoleAppender  
#   A1   使用的输出布局,其中log4j提供4种布局.   org.apache.log4j.HTMLLayout(以HTML表格形式布局)
#org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
#org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
#org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

log4j.appender.A1.layout=org.apache.log4j.PatternLayout  
#灵活定义输出格式   具体查看log4j   javadoc   org.apache.log4j.PatternLayout  
#d   时间   ....  
log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd   HH:mm:ss}   [%c]-[%p]   %m%n  
#R   输出到文件   RollingFileAppender的扩展,可以提供一种日志的备份功能。
log4j.appender.R=org.apache.log4j.RollingFileAppender  
#日志文件的名称
log4j.appender.R.File=log4j.log  
#日志文件的大小
log4j.appender.R.MaxFileSize=100KB  
#   保存一个备份文件
log4j.appender.R.MaxBackupIndex=1  

log4j.appender.R.layout=org.apache.log4j.TTCCLayout
#log4j.appender.R.layout.ConversionPattern=%-d{yyyy-MM-dd   HH:mm:ss}   [%c]-[%p]   %m%n

配置以这里就差不多了,如果你想更深入了解配置文件的各个细节,可以去查看docs。还有,在文章的最后面我们提供配置文件中一些主要的语法。下面我们来看看怎样在程序中使用log4j.

1.4 Log4j的使用。
使用Log4j,第一步就是获取日志记录器,这个记录器将负责控制日志信息。其语法为:
public   static   Logger   getLogger(   String   name),
必须在使用前要把这个类导入
import   org.apache.log4j.Logger;

name一般是类文件的名字,如下:
static   Logger   logger   =   Logger.getLogger   ( " ".class.getName   ()   )   ;

您就可以轻松地使用不同优先级别的日志记录语句插入到您想记录日志的任何地方,其语法如下:
logger.debug   (   Object   message   )   ;
logger.info   (   Object   message   )   ;
logger.warn   (   Object   message   )   ;
logger.error   (   Object   message   )   ;

为什么这里要分级别的呢?试想一下,我们在写程序的时候,为了调试程序,会在很多会出错的地方加入大量的logger.info();信息。当然程序调试完毕,我们不需要这些输出信息了,那怎么办呢?以前的做法是把每个程序中的logger.info删除,但这是不现实的,如果程序不大还可以,但如果程序很多,做这些事情就很烦人了。但因为log4j分级别了,当我们不需要输出这样调试时用到的log.info()时,我们可以把输出的级别调高,如调到warn,或error级别,这样info级别及以下的级别就不会出输出了,是不是很方便的呢?

其实除了这种使用方式,log4j还有其它的使用方面,不需要配置文件,直接在程序中定义输入出级别,层次等信息,如果要了解这方法的使用,可以参考文档。

1.5。附注:
以下是配置文件的一些重要的语法
定义配置文件

其实您也可以完全不使用配置文件,而是在代码中配置Log4j环境。但是,使用配置文件将使您的应用程序更加灵活。

Log4j支持两种配置文件格式,一种是XML格式的文件,一种是Java特性文件(键=值)。下面我们介绍使用Java特性文件做为配置文件的方法:

配置根Logger,其语法为:

log4j.rootLogger   =   [   level   ]   ,   appenderName,   appenderName,   …
其中,level   是日志记录的优先级,分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来。
appenderName就是指定日志信息输出到哪个地方。您可以同时指定多个输出目的地。

配置日志信息输出目的地Appender,其语法为

log4j.appender.appenderName   =   fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1   =   value1

log4j.appender.appenderName.option   =   valueN
其中,Log4j提供的appender有以下几种:
org.apache.log4j.ConsoleAppender(控制台),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

配置日志信息的格式(布局),其语法为:

log4j.appender.appenderName.layout   =   fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1   =   value1

log4j.appender.appenderName.layout.option   =   valueN
其中,Log4j提供的layout有以下几种:
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

-----------------------------------------------------------------------------------------------------------------------------------------

--------------------------==------------------------------------------------------------------------------------------------------------

在强调可重用组件开发的今天,除了自己从头到尾开发一个可重用的日志操作类外,Apache为我们提供了一个强有力的日志操作包-Log4j。

Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

此外,通过Log4j其他语言接口,您可以在C、C++、.Net、PL/SQL程序中使用Log4j,其语法和用法与在Java程序中一样,使得多语言分布式系统得到一个统一一致的日志组件模块。而且,通过使用各种第三方扩展,您可以很方便地将Log4j集成到J2EE、JINI甚至是SNMP应用中。

说明:下面分为三部分,第一部分讲解如何配置log4j,第二部分为对log4j.properties配置文件中的各个属性的讲解,第三部分为对log4j的详细讲解,如果只想配置上log4j,那么只需要看前两个部分就可以,如果想对log4j深入了解,则还需看第三部分。

一、Log4j配置

第一步:加入log4j-1.2.8.jar到lib下。

第二步:在CLASSPATH下建立log4j.properties。内容如下:

1 log4j.rootCategory=INFO, stdout , R

2

3 log4j.appender.stdout=org.apache.log4j.ConsoleAppender

4 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

5 log4j.appender.stdout.layout.ConversionPattern=[QC] %p [%t] %C.%M(%L) | %m%n

6

7 log4j.appender.R=org.apache.log4j.DailyRollingFileAppender

8 log4j.appender.R.File=D:/Tomcat 5.5/logs/qc.log

9 log4j.appender.R.layout=org.apache.log4j.PatternLayout

10 log4j.appender.R.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n

11

12 log4j.logger.com.neusoft=DEBUG

13 log4j.logger.com.opensymphony.oscache=ERROR

14 log4j.logger.net.sf.navigator=ERROR

15 log4j.logger.org.apache.commons=ERROR

16 log4j.logger.org.apache.struts=WARN

17 log4j.logger.org.displaytag=ERROR

18 log4j.logger.org.springframework=DEBUG

19 log4j.logger.com.ibatis.db=WARN

20 log4j.logger.org.apache.velocity=FATAL

21

22 log4j.logger.com.canoo.webtest=WARN

23

24 log4j.logger.org.hibernate.ps.PreparedStatementCache=WARN

25 log4j.logger.org.hibernate=DEBUG

26 log4j.logger.org.logicalcobwebs=WARN

第三步:相应的修改其中属性,修改之前就必须知道这些都是干什么的,在第二部分讲解。

第四步:在要输出日志的类中加入相关语句:

定义属性:protected final Log log = LogFactory.getLog(getClass());

在相应的方法中:

if (log.isDebugEnabled())

{

log.debug(“System …..”);

}

二、Log4j说明

1 log4j.rootCategory=INFO, stdout , R

此句为将等级为INFO的日志信息输出到stdout和R这两个目的地,stdout和R的定义在下面的代码,可以任意起名。等级可分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL,如果配置OFF则不打出任何信息,如果配置为INFO这样只显示INFO, WARN, ERROR的log信息,而DEBUG信息不会被显示,具体讲解可参照第三部分定义配置文件中的logger。

3 log4j.appender.stdout=org.apache.log4j.ConsoleAppender

此句为定义名为stdout的输出端是哪种类型,可以是

org.apache.log4j.ConsoleAppender(控制台),

org.apache.log4j.FileAppender(文件),

org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),

org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)

org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

具体讲解可参照第三部分定义配置文件中的Appender。

4 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

此句为定义名为stdout的输出端的layout是哪种类型,可以是

org.apache.log4j.HTMLLayout(以HTML表格形式布局),

org.apache.log4j.PatternLayout(可以灵活地指定布局模式),

org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),

org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

具体讲解可参照第三部分定义配置文件中的Layout。

5 log4j.appender.stdout.layout.ConversionPattern= [QC] %p [%t] %C.%M(%L) | %m%n

如果使用pattern布局就要指定的打印信息的具体格式ConversionPattern,打印参数如下:

%m 输出代码中指定的消息

%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL

%r 输出自应用启动到输出该log信息耗费的毫秒数

%c 输出所属的类目,通常就是所在类的全名

%t 输出产生该日志事件的线程名

%n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”

%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921

%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。

[QC]是log信息的开头,可以为任意字符,一般为项目简称。

输出的信息

[TS] DEBUG [main] AbstractBeanFactory.getBean(189) | Returning cached instance of singleton bean 'MyAutoProxy'

具体讲解可参照第三部分定义配置文件中的格式化日志信息。

7 log4j.appender.R=org.apache.log4j.DailyRollingFileAppender

此句与第3行一样。定义名为R的输出端的类型为每天产生一个日志文件。

8 log4j.appender.R.File=D:/Tomcat 5.5/logs/qc.log

此句为定义名为R的输出端的文件名为D:/Tomcat 5.5/logs/qc.log

可以自行修改。

9 log4j.appender.R.layout=org.apache.log4j.PatternLayout

与第4行相同。

10 log4j.appender.R.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n

与第5行相同。

12 log4j.logger.com. neusoft =DEBUG

指定com.neusoft包下的所有类的等级为DEBUG。

可以把com.neusoft改为自己项目所用的包名。

13 log4j.logger.com.opensymphony.oscache=ERROR

14 log4j.logger.net.sf.navigator=ERROR

这两句是把这两个包下出现的错误的等级设为ERROR,如果项目中没有配置EHCache,则不需要这两句。

15 log4j.logger.org.apache.commons=ERROR

16 log4j.logger.org.apache.struts=WARN

这两句是struts的包。

17 log4j.logger.org.displaytag=ERROR

这句是displaytag的包。(QC问题列表页面所用)

18 log4j.logger.org.springframework=DEBUG

此句为Spring的包。

24 log4j.logger.org.hibernate.ps.PreparedStatementCache=WARN

25 log4j.logger.org.hibernate=DEBUG

此两句是hibernate的包。

以上这些包的设置可根据项目的实际情况而自行定制。

三、log4j详解

1、定义配置文件

Log4j支持两种配置文件格式,一种是XML格式的文件,一种是Java特性文件log4j.properties(键=值)。下面将介绍使用log4j.properties文件作为配置文件的方法:

①、配置根Logger

Logger 负责处理日志记录的大部分操作。

其语法为:

log4j.rootLogger = [ level ] , appenderName, appenderName, …

其中,level 是日志记录的优先级,分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者自定义的级别。Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别,只有等于及高于这个级别的才进行处理,则应用程序中所有DEBUG级别的日志信息将不被打印出来。ALL:打印所有的日志,OFF:关闭所有的日志输出。 appenderName就是指定日志信息输出到哪个地方。可同时指定多个输出目的地。

②、配置日志信息输出目的地 Appender

Appender 负责控制日志记录操作的输出。

其语法为:

log4j.appender.appenderName = fully.qualified.name.of.appender.class

log4j.appender.appenderName.option1 = value1

log4j.appender.appenderName.optionN = valueN

这里的appenderName为在①里定义的,可任意起名。

其中,Log4j提供的appender有以下几种:

org.apache.log4j.ConsoleAppender(控制台),

org.apache.log4j.FileAppender(文件),

org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),

org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件),可通过log4j.appender.R.MaxFileSize=100KB设置文件大小,还可通过log4j.appender.R.MaxBackupIndex=1设置为保存一个备份文件。

org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

例如:log4j.appender.stdout=org.apache.log4j.ConsoleAppender

定义一个名为stdout的输出目的地,ConsoleAppender为控制台。

③、配置日志信息的格式(布局)Layout

Layout 负责格式化Appender的输出。

其语法为:

log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class

log4j.appender.appenderName.layout.option1 = value1

log4j.appender.appenderName.layout.optionN = valueN

其中,Log4j提供的layout有以下几种:

org.apache.log4j.HTMLLayout(以HTML表格形式布局),

org.apache.log4j.PatternLayout(可以灵活地指定布局模式),

org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),

org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

2、格式化日志信息

Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,打印参数如下:

%m 输出代码中指定的消息

%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL

%r 输出自应用启动到输出该log信息耗费的毫秒数

%c 输出所属的类目,通常就是所在类的全名

%t 输出产生该日志事件的线程名

%n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”

%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921

%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。

3、在代码中使用Log4j

我们在需要输出日志信息的类中做如下的三个工作:

1、导入所有需的commongs-logging类:

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

2、在自己的类中定义一个org.apache.commons.logging.Log类的私有静态类成员:

private final Log log = LogFactory.getLog(getClass());

LogFactory.getLog()方法的参数使用的是当前类的class。

3、使用org.apache.commons.logging.Log类的成员方法输出日志信息:

if (log.isDebugEnabled())

{

log.debug("111");

}

if (log.isInfoEnabled())

{

log.info("222");

}

if (log.isWarnEnabled())

{

log.warn("333");

}

if (log.isErrorEnabled())

{

log.error("444");

}

if (log.isFatalEnabled())

{

log.fatal("555")

}

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/donkeyzheng/archive/2006/06/26/835678.aspx

 

 

以上内容来自csdn论坛:

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值