使用Logger的日志记录方法,仅仅是发出了日志记录的事件,具体日志要记录到什么地方,需要Appender的支持。在Log4J中,Appender定义了日志输出的目的地。在上面所有的示例当中,我们日志输出的目的地都是控制台,在Log4j中,还有非常多的Appender可供选择,可以将日志输出到文件,网络,数据库等等,这个后面再介绍。说到这里,可能有人就已经会思考,既然Logger对象的info()等方法仅仅是发出了日志记录的事件,还需要指定输出目的地;那么我们之前的示例代码也并没有为任何一个Logger设置Appender啊?其实这很好理解,我们回顾一下之前的Level,按道理,也应该为每一个Logger指定对应的日志输出级别,但是我们也并没有这样做,正是因为Logger本身存在一个完整的体系结构,而Level能够在这个结构中自下而上的继承。同理,Appender也具有这种继承的特性。下面给出一段代码,先看看怎么使用代码的方式指定Appender:
@Test
public void testLogAppender1(){
Logger log1=Logger.getLogger("cd");
log1.setLevel(Level.DEBUG);
log1.addAppender(new ConsoleAppender(new SimpleLayout()));
Logger log2=Logger.getLogger("cd.itcast.log");
log2.info("log2 info");
log2.debug("log2 debug");
}
注意在这段代码中的加粗的代码。第一句代码设置了日志级别为DEBUG;第二条代码,调用了Logger的addAppender方法添加了一个ConsoleAppender;从类的名字上看就知道这是一个把日志输出到控制台上的Appender,在创建ConsoleAppender的时候,又传入了一个SimpleLayout的实例;关于Layout下面再介绍,现在只需要关注Appender的继承特性。接下来,又创建了一个cd.itcast.log的子Logger;并且使用这个Logger输出了两条日志信息。运行测试,输出:
INFO - log2 info
DEBUG - log2 debug
请注意这个输出,很明显已经和之前的输出信息的格式完全不一样了,这里的输出格式就是由我们在cd这个Logger上面设置的ConsoleAppender+SimpleLayout所规定的。从这个例子中,我们可以看到,我们改变了cd这个Logger的Appender;他下面的子Logger自然就继承了这个Appender,输出了另外一种格式的信息。从这段代码中,我们能看出Logger的继承性,假如我们把代码修改为以下这样:
@Test
public void testLogAppender1(){
BasicConfigurator.configure();
Logger log1=Logger.getLogger("cd");
log1.setLevel(Level.DEBUG);
log1.addAppender(new ConsoleAppender(new SimpleLayout()));
Logger log2=Logger.getLogger("cd.itcast.log");
log2.info("log2 info");
log2.debug("log2 debug");
}
在这段代码中,我们仅仅只是添加了BasicConfigurator来完成一个基本的配置。我们先来分析下这段代码。首先我们完成了基本的配置,从前面的测试代码中,我们可以知道,这条代码为rootLogger设置了一个DEBUG的Level;另外,现在我们知道了,这条代码肯定还为rootLogger添加了一个ConsoleAppender。然后我们创建了名字为cd的Logger,并且另外添加了一个Appender;接着又创建了名字为cd.itcast.log的Logger,最后使用这个Logger输出了两条日志信息。根据前面的Level的表现,我们猜想,当使用cd.itcast.log这个Logger做日志的时候,因为这个Logger本身是没有添加任何Appender,所以他会向上查询任何一个添加了Appender的父Logger,即找到了cd这个Logger,最后使用cd这个Logger完成日志,那么我们预计的结果是这段代码和上一段代码输出相同。
我们来运行一下这段代码,输出:
INFO - log2 info
0 [main] INFO cd.itcast.log - log2 info
DEBUG - log2 debug
0 [main] DEBUG cd.itcast.log - log2 debug
和我们预测的结果不一样。log2 info和log2 debug分别被输出了两次。我们观察结果,两条日志的输出都是先有一条ConsoleAppender+SimpleLayout的方式输出的然后紧跟一条使用BasicConfigurator的输出方式。那我们就能大胆的猜测了,Logger上的Appender不光能继承其父Logger上的Appender,更重要的是,他不光只继承一个,而是只要是其父Logger,其上指定的Appender都会追加到这个子Logger之上。所以,这个例子中,cd.itcast.log这个Logger不光继承了cd这个Logger上的Appender,还得到了rootLogger上的Appender;所以输出了这样的结果。在Log4J中,这个特性叫做Appender的追加性。默认情况下,所有的Logger都自动具有追加性,通过一个表来说明:
但是,在某些情况下,这样做反而会引起日志输出的混乱。有些时候,我们并不希望Logger具有追加性。比如在上面这张表中,我们想让cd.itcast.log只需要继承A2和自己的A3Appender,而不想使用root上面的A1 Appender,又该怎么做呢?
其实很简单,在Logger上,都有一个setAdditivity方法,如果设置setAdditivity为false,则该logger的子类停止追加该logger之上的Appender;如果设置为true,则具有追加性。修改一下上表:
再来一段代码看看是否如此:
@Test
public void testLogAppender2() throws Exception{
BasicConfigurator.configure();
Logger log1=Logger.getLogger("cd");
log1.setAdditivity(false);
log1.addAppender(new ConsoleAppender(new SimpleLayout()));
Logger log2=Logger.getLogger("cd.itcast");
log2.addAppender(new FileAppender(new SimpleLayout(),"a0.log"));
Logger log3=Logger.getLogger("cd.itcast.log");
log3.info("log2 info");
}
先来分析这段代码,在这段代码中有一些新的知识,简单理解即可。首先,我们使用BasicConfigurator.configure()方法配置了rootLogger;接着定义了名称为cd的Logger;并为其添加了一个ConsoleAppender,但是这里,我们这里设置了additivity为false,即cd和cd之后的logger都不会再添加rootLogger的Appender了。接下来,我们创建了cd.itcast这个Logger,并且为这个Logger指定了一个FileAppender。FileAppender很简单,除了同样要指定一个Layout,这个在后面介绍,第二个参数还需要指定输出日志的名称;最后,我们创建了cd.itcast.log,并使用这个Logger输出日志。按照上面的表所展示的规律,因为cd.itcast.log没有指定任何的Appender,所以向上查询。找到cd.itcast,cd.itcast.log得到其上的FileAppender,因为cd.itcast没有设置additivity,默认为true,继续向上查找,cd.itcast.log会得到cd的ConsoleAppender;但是因为cd设置了additivity为false,所以不再向上查询,最后,cd.itcast.log会向FileAppender和ConsoleAppender输出日志。
运行测试,结果:
INFO - log2 info
并且在应用下增加一个a0.log文件,内容为INFO - log2 info。符合我们的预期。
在Log4J中,一个Logger可以添加多个Appender,不管是通过继承的方式还是通过调用Logger.addAppender方法添加。只要添加到了某一个Logger之上,在这个Logger之上的任何一个可以被输出的日志都会分别输出到所有的Appender之上。
@Test
public void testLogAppender1(){
Logger log1=Logger.getLogger("cd");
log1.setLevel(Level.DEBUG);
log1.addAppender(new ConsoleAppender(new SimpleLayout()));
Logger log2=Logger.getLogger("cd.itcast.log");
log2.info("log2 info");
log2.debug("log2 debug");
}
注意在这段代码中的加粗的代码。第一句代码设置了日志级别为DEBUG;第二条代码,调用了Logger的addAppender方法添加了一个ConsoleAppender;从类的名字上看就知道这是一个把日志输出到控制台上的Appender,在创建ConsoleAppender的时候,又传入了一个SimpleLayout的实例;关于Layout下面再介绍,现在只需要关注Appender的继承特性。接下来,又创建了一个cd.itcast.log的子Logger;并且使用这个Logger输出了两条日志信息。运行测试,输出:
INFO - log2 info
DEBUG - log2 debug
请注意这个输出,很明显已经和之前的输出信息的格式完全不一样了,这里的输出格式就是由我们在cd这个Logger上面设置的ConsoleAppender+SimpleLayout所规定的。从这个例子中,我们可以看到,我们改变了cd这个Logger的Appender;他下面的子Logger自然就继承了这个Appender,输出了另外一种格式的信息。从这段代码中,我们能看出Logger的继承性,假如我们把代码修改为以下这样:
@Test
public void testLogAppender1(){
BasicConfigurator.configure();
Logger log1=Logger.getLogger("cd");
log1.setLevel(Level.DEBUG);
log1.addAppender(new ConsoleAppender(new SimpleLayout()));
Logger log2=Logger.getLogger("cd.itcast.log");
log2.info("log2 info");
log2.debug("log2 debug");
}
在这段代码中,我们仅仅只是添加了BasicConfigurator来完成一个基本的配置。我们先来分析下这段代码。首先我们完成了基本的配置,从前面的测试代码中,我们可以知道,这条代码为rootLogger设置了一个DEBUG的Level;另外,现在我们知道了,这条代码肯定还为rootLogger添加了一个ConsoleAppender。然后我们创建了名字为cd的Logger,并且另外添加了一个Appender;接着又创建了名字为cd.itcast.log的Logger,最后使用这个Logger输出了两条日志信息。根据前面的Level的表现,我们猜想,当使用cd.itcast.log这个Logger做日志的时候,因为这个Logger本身是没有添加任何Appender,所以他会向上查询任何一个添加了Appender的父Logger,即找到了cd这个Logger,最后使用cd这个Logger完成日志,那么我们预计的结果是这段代码和上一段代码输出相同。
我们来运行一下这段代码,输出:
INFO - log2 info
0 [main] INFO cd.itcast.log - log2 info
DEBUG - log2 debug
0 [main] DEBUG cd.itcast.log - log2 debug
和我们预测的结果不一样。log2 info和log2 debug分别被输出了两次。我们观察结果,两条日志的输出都是先有一条ConsoleAppender+SimpleLayout的方式输出的然后紧跟一条使用BasicConfigurator的输出方式。那我们就能大胆的猜测了,Logger上的Appender不光能继承其父Logger上的Appender,更重要的是,他不光只继承一个,而是只要是其父Logger,其上指定的Appender都会追加到这个子Logger之上。所以,这个例子中,cd.itcast.log这个Logger不光继承了cd这个Logger上的Appender,还得到了rootLogger上的Appender;所以输出了这样的结果。在Log4J中,这个特性叫做Appender的追加性。默认情况下,所有的Logger都自动具有追加性,通过一个表来说明:
Logger | addAppender | 起作用的Appender |
root | A1 | A1 |
cd | A2 | A2,A1 |
cd.itcast | null | A2,A1 |
cd.itcast.log | A3 | A3,A2,A1 |
其实很简单,在Logger上,都有一个setAdditivity方法,如果设置setAdditivity为false,则该logger的子类停止追加该logger之上的Appender;如果设置为true,则具有追加性。修改一下上表:
Logger | addAppender | setAdditivity | 起作用的Appender |
root | A1 | true | A1 |
cd | A2 | false | A2 |
cd.itcast | null | true | A2 |
cd.itcast.log | A3 | true | A3,A2 |
@Test
public void testLogAppender2() throws Exception{
BasicConfigurator.configure();
Logger log1=Logger.getLogger("cd");
log1.setAdditivity(false);
log1.addAppender(new ConsoleAppender(new SimpleLayout()));
Logger log2=Logger.getLogger("cd.itcast");
log2.addAppender(new FileAppender(new SimpleLayout(),"a0.log"));
Logger log3=Logger.getLogger("cd.itcast.log");
log3.info("log2 info");
}
先来分析这段代码,在这段代码中有一些新的知识,简单理解即可。首先,我们使用BasicConfigurator.configure()方法配置了rootLogger;接着定义了名称为cd的Logger;并为其添加了一个ConsoleAppender,但是这里,我们这里设置了additivity为false,即cd和cd之后的logger都不会再添加rootLogger的Appender了。接下来,我们创建了cd.itcast这个Logger,并且为这个Logger指定了一个FileAppender。FileAppender很简单,除了同样要指定一个Layout,这个在后面介绍,第二个参数还需要指定输出日志的名称;最后,我们创建了cd.itcast.log,并使用这个Logger输出日志。按照上面的表所展示的规律,因为cd.itcast.log没有指定任何的Appender,所以向上查询。找到cd.itcast,cd.itcast.log得到其上的FileAppender,因为cd.itcast没有设置additivity,默认为true,继续向上查找,cd.itcast.log会得到cd的ConsoleAppender;但是因为cd设置了additivity为false,所以不再向上查询,最后,cd.itcast.log会向FileAppender和ConsoleAppender输出日志。
运行测试,结果:
INFO - log2 info
并且在应用下增加一个a0.log文件,内容为INFO - log2 info。符合我们的预期。
在Log4J中,一个Logger可以添加多个Appender,不管是通过继承的方式还是通过调用Logger.addAppender方法添加。只要添加到了某一个Logger之上,在这个Logger之上的任何一个可以被输出的日志都会分别输出到所有的Appender之上。