log4j手册
在看hive源码的时候,因为对log4j不了解而苦于不知道该如何打印log来进行调试。虽然最后我选择了直接使用System.err.println来打印
log,但是log4j是一个很好的log框架,在很多多线程系统中都有用到,所以值得花点时间好好学习下。
简介:
log4j是apache的一个开源项目,是一个流行的java log框架,如今已经有了c,c++,python,shell等各语言的相关版本,如使用于shell的log4shell.
打印log是最简单和最直接的程序调试方法。即便如此,打印log有时却是唯一可以用的程序调试方法,尤其是对于大型的多线程或分布式系统。它们很难通过gdb等工具就进行单步调试。
打印log是软件开发过程中非常重要的一环。log提供了程序的相关信息。丰富的log信息有利于对程序的了解与审核。当然log也有它的缺点,冗余的log影响了程序的执行效率,导致满屏的文字信息。正是基于这点的考虑,log4j被设计成可靠,快速而灵活的框架。log4j简单易用,可以方便的控制log的粒度,从而让程序员专注于主程序的业务逻辑。
Loggers, Appenders and Layouts
log4j由3个主要模块组成:loggers,appenders,layouts,开发人员通过这3个模块,控制程序运行时log输出的级别,目的地和格式等。
logger层级
log4j相较于直接使用System.out.println的最主要的一个优势是,它可以方便的控制在运行时哪些log生效而哪些log失效。这种能力是通过把log语句块根据用户定义的category(类别)划分到不同的logging空间来实现的。因此category在log4j中很重要的一个概念。
loggers是一个具名实体。logger的名字是大小写敏感的,遵循层级命名规则。
比如,名字为"com.foo"的logger是名字为"com.foo.Bar"的父亲,这种层级的命名方式对大多开发人员来说应该并不陌生。
root logger处于logger层级的最顶层,它相对其它logger有两个特点,
-
1. root logger是必须存在的
-
2. 它不能通过名字来获取
root logger只能通过静态方法Logger.getRootLogger来获取。其它的logger通过静态方法Logger.getLogger来获取,并且需要传递名字作为参数。
Logger的基本方法的使用如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package org.apache.log4j; public class Logger { // Creation & retrieval methods: public static Logger getRootLogger(); public static Logger getLogger(String name); // printing methods: public void trace(Object message); public void debug(Object message); public void info(Object message); public void warn(Object message); public void error(Object message); public void fatal(Object message); // generic printing method: public void log(Level l, Object message); } |
loggers具有level属性,默认的level集合为:TRACE,DEBUG,INFO,WARN,ERROR and FATAL。level集合在org.apache.log4j.level类中定义。我们可以通过继承level类来定义自己的level,但是我们并不鼓励这样做。
如果一个logger没有指定level属性,那么它将继承离它最近的那个祖先的level。
logging请求通过调用logger实例的打印方法实现,而打印方法决定了打印的level,如,c是一个logger实例,则c.info(...)将响应一条INFO级别的logging请求。logging请求只有在其level大于等于logger本身的level时才会被响应。
这个规则是log4j的核心,它假定level之间是有序的。在标准级别中,DEBUG<INFO<WARN<ERROR<FATAL。
如下代码体现了这一规则。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
// get a logger instance named "com.foo" Logger logger = Logger.getLogger( "com.foo" ); // Now set its level. Normally you do not need to set the // level of a logger programmatically. This is usually done // in configuration files. logger.setLevel(Level.INFO); Logger barlogger = Logger.getLogger( "com.foo.Bar" ); // This request is enabled, because WARN >= INFO. logger.warn( "Low fuel level." ); // This request is disabled, because DEBUG < INFO. logger.debug( "Starting search for nearest gas station." ); // The logger instance barlogger, named "com.foo.Bar", // will inherit its level from the logger named // "com.foo" Thus, the following request is enabled // because INFO >= INFO. barlogger.info( "Located nearest gas station." ); // This request is disabled, because DEBUG < INFO. barlogger.debug( "Exiting gas station search" ); |
调用Logger.getLoggger方法时如果传入的是同一个名字参数,那么返回的将是同一个logger实例。所以即使不通过指针也可以在不同的代码段中调用同一个logger。与现实中的父子关系不同,log4j logger的创建和配置是没有顺序要求的,也就是说父logger可以后于子孙logger创建。
logger的命名是完全自由的。一种比较好的做法是用所在类的类名来命名logger。这种简单的定义logger的方法非常管用。因为输出的log将携带创建该log的logger的名字信息。因此可以方便的识别出某条log是在哪个类中输出的。
待续。。。。。。