Log4j Study(3)---记录器的层次

        记录器的层次Logger Hierarchy
       首先,我们先看一下何为层次,以我们最熟悉的继承为例
假如类B是类C的父类,类A是类C的祖先类,类D是类C的子类。这些类之间就构成一种层次关系。在这些具有层次关系的类中,子类都可继承它的父类的特征,如类B的对象能调用类A中的非private实例变量和函数;而类C由于继承自类B,所以类B的对象可以同时调用类A和类B中的非private实例变量和函数。
在log4j中,处于不同层次中的Logger也具有象类这样的继承关系。

     如果一个应用中包含了上千个类,那么也几乎需要上千个Logger实例。如何对这上千个Logger实例进行方便地配置,就是一个很重要的问题。Log4J采用了一种树状的继承层次巧妙地解决了这个问题。在Log4J中Logger是具有层次关系的。它有一个共同的根,位于最上层,其它Logger遵循类似包的层次。

根记录器root logger
      就象一个Java中的Object类一样,log4j中的logger层次中有一个称之为根记录器的记录器,其它所有的记录器都继承自这个根记录器。根记录器有两个特征

  1) 根记录器总是存在。就像Java中的Object类一样,因为用log4j输出日志信息是通过记录器来实现的,所以只要你应用了log4j,根记录器就肯定存在的。
  2) 根记录器没有名称,所以不能通过名称来取得根记录器。但在Logger类中提供了getRootLogger()的方法来取得根记录器。

记录器的层次
    Logger遵循类似包的层次。如
static Logger rootLog = Logger.getRootLogger();
static Logger log1 = Logger.getLogger("test4j");
static Logger log2 = Logger.getLogger("test4j.test4j2");
static Logger log3 = Logger.getLogger("test4j.test4j2.test4j2");
    那么rootLog是log2的祖先子记录器,log1是log2的父子记录器,log3是log2的子记录器。记录器象Java中的类继承一样,子记录器可以继承父记录器的设置信息,也可以可以覆写相应的信息。

   首先先看一下记录器层次中的继承有什么用处。假设程序中的每个包都具有一些基本的日志信息,而包中的不同包可能会有些额外的日志信息要输出,这种情况就可以象处理Java中的类一样,运用Logger中的层次关系来达到目的。假设有个名为A的包,我包下的所有类都要把日志信息输出到控制台中;A.B包除了输出到控制台外还要输出到文件中;A.C包除了输出到控制台中还要输出到HTML文档中。这样我们就可以通过定义一个父记录器A,它负责把日志信息输出到控制台中;定义一个A的子记录器A.B,它负责把日志信息输出到文件中;定义一个A的子记录器A.C,它负责把日志信息输出到HTML文档中。


       记录器遵循的是类似包的层次,这样做为我们带来了大大的方便。Logger类中的getLogger()方法可以取得Logger对象,这个方法有三种参数形式String、Class和URL,其实不论是用哪一种,最终都是通过记录器的名字来取得记录器对象的。如果要取得一个名为A.B的记录器对象,我们可以Logger.getLogger(“A.B”)。但从上面的例子中,我们都是通过Logger.getLogger(TestLog4j.class.getName())这种方法来取得记录器对象。这是为什么呢?现在我们假设A.B的包下有一个类BClass,那么我们调用BClass.class.getName()得到的是这个类的全名A.B.BClass。所以当调用Logger.getLogger(BClass.class.getName())时,最理想的情况是返回名为A.B.BClass的记录器对象。但是如果不存在名为A.B.BClass的记录器时它会怎样呢?其实通过Logger类的getLogger方法取得记录器时存在下面两种情况:
1) 如果存在与所要找的名字完全相同的记录器,则返回相应的记录器对象。
当调用Logger.getLogger(BClass.class.getName())时,如果定义了名为A.B.BClass的记录器,它就返回该记录器的对象。
2) 但如果找不到,它会尝试返回在记录器层次中与所要找的记录器最接近的记录器对象。
     当调用Logger.getLogger(BClass.class.getName())时,如果没有定义了名为A.B.BClass的记录器,那会尝试返回名为A.B的记录器的对象;如果又没有定义名为A.B的记录器,它会尝试返回名为A的记录器的对象;如果也没定义名为A的记录器,它就会返回根记录器的对象,而根记录器是必须存在的,所以你总能得到一个记录器对象。
好了,现在我们回到前面的问题,我们为什么总要通过Logger.getLogger(BClass.class.getName())这种以类全名作为参数来取得记录器对象呢?其实这是为了管理方便。因为我们在定义设计Logger时也遵循类似包的规则,使设计器的名称与程序中的类包对应。如上面的假设中我们的程序中有A包,A包下有B包和C包,B包下又有类BClass,那么我们就可使设计器的名为A、A.B、A.C、A.B.BClass,以此类推。那么当我们通过类命名来取得设计器对象时,总能取到与所要的设计器最接近的设计器对象。

     如何应用记录器的层次
如果定义及获取不同层次的记录器
任何一个记录器的使用都有两个步骤:
   1) 在配置文件中定义相应的记录器。
     在配置文件中定义记录器的格式有两种
定义根记录器的格式为
log4j.rootLogger = [ level ], appendName1, appendName2, …appendNameN

     定义一个非根记录器的格式为
log4j.logger.loggerName1 = [ level ], appendName1,…appendNameN……
log4j.logger.loggerNameM = [ level ], appendName1, …appendNameN……
   2) 在代码中调用Logger类的取得记录器方法取得相应的记录器对象。
要取得根记录器对象可通过Logger.getRootLogger()函数,要取得非根记录器可通过Logger.getLogger()函数。
理论知道就讲到这里,纸上得来终觉浅,下面,实例一下。

package TestC;
import org.apache.log4j.*;
import TestC.TestD.TestD;
public class TestC
{
 static Logger logger = Logger.getLogger(TestC.class.getName());
 public TestC()
 {}
 public static void main(String args[])
 {
  PropertyConfigurator.configure("log4j2.properties");
  logger.debug("debug test");
     logger.info("info test");
     logger.warn("warn test");
     logger.error("error test");
     logger.log(Priority.WARN, "alternate Test");
     logger.debug(TestC.class.getName());
     TestD td = new TestD();
     td.testLog();
 }
}

package TestC.TestD;
import org.apache.log4j.*;
public class TestD
{
 static Logger logger = Logger.getLogger(TestD.class.getName());
 public void testLog()
 {
  PropertyConfigurator.configure("log4j2.properties");
  logger.debug("D debug test");
     logger.info("D info test");
     logger.warn("D warn test");
     logger.error("D error test");
     logger.log(Priority.ERROR, "Testing a log message use a alternate form");
 }
}

看一下配置文件。
#1区
#### Use two appenders, one to log to console, another to log to a file
log4j.rootLogger = debug, stdout

#2区
#Print only messages of priority WARN or higher for your category
log4j.logger.TestC , R
log4j.logger.TestC.TestD=WARN

#3区
#### First appender writes to console
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

#4区
#### Second appender writes to a file
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=example.log

# Control the maximum log file size
log4j.appender.R.MaxFileSize=100KB
# Archive log files (one backup file here)
log4j.appender.R.MaxBackupIndex=1

log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd hh:mm:ss}:%p %t %c - %m%n


先看一下运行结果。
DEBUG [main] (TestC.java:12) - debug test
 INFO [main] (TestC.java:13) - info test
 WARN [main] (TestC.java:14) - warn test
ERROR [main] (TestC.java:15) - error test
 WARN [main] (TestC.java:16) - alternate Test
DEBUG [main] (TestC.java:17) - TestC.TestC
 WARN [main] (TestD.java:12) - D warn test
ERROR [main] (TestD.java:13) - D error test
ERROR [main] (TestD.java:14) - Testing a log message use a alternate form

输出文件的结果为:
2005-10-29 08:55:54:DEBUG main TestC.TestC - debug test
2005-10-29 08:55:54:INFO main TestC.TestC - info test
2005-10-29 08:55:54:WARN main TestC.TestC - warn test
2005-10-29 08:55:54:ERROR main TestC.TestC - error test
2005-10-29 08:55:54:WARN main TestC.TestC - alternate Test
2005-10-29 08:55:54:DEBUG main TestC.TestC - TestC.TestC
2005-10-29 08:55:54:WARN main TestC.TestD.TestD - D warn test
2005-10-29 08:55:54:ERROR main TestC.TestD.TestD - D error test
2005-10-29 08:55:54:ERROR main TestC.TestD.TestD - Testing a log message use a alternate form

解释:

在1区定义了一个跟记录器改记录器具有bug级别并有一个名称为stdout的输出端

2区中的内容是这一节的重点,也是应用到记录器层次的地方,但其实也只有两句,充分体现了log4j的简单性。在这里,我们定义了两个名称分别为TestC和TestC.TestD设计器。

    在定义TestC记录器时没有指定级别,所以它的级别继承自它的父记录器,即要记录器,所以它的级别也为DEBUG。在定义TestC记录器时又定义了一个名称为R的输出端,所以它的输出端有两个,一个从根记录器继承而来的名为stdout的输出端,另一个为在此定义的名为R的输出端。在此需要注意的是,在定义记录器时必须先定义记录器的级别,然后才是记录器的输出端。如果只想定义输出端而不定义级别,则虽然级别可以为空,但逗号分隔符不能省略。如定义TestC记录器的做法。

  在定义TestC.TestD记录器时又指定了它的级别,由于一个记录器的级别只能有一个,所以新指定的级别将覆写掉它的父记录器的级别(这就象Java中的多态)。我们没有定义TestC.TestD记录器的输出端,所以它的输出端将从它的父记录器中继承而来。它的父记录器为TestC记录器,所以它和TestC记录器一样具有两个名称分别为stdout和R的输出端。
  剩下的3区和4区分别设置了两个输出端的参数值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值