Log4j有三个主要的组件:Logger、Appender和Layout。这三个组件相互配合使得我们可以获得非常强大的日志记录的能力。
Logger
Logger的名称是区分大小写的,依据名称可以确定其层次结构(即父子关系),规则如下:
- 如果Logger A的名称后跟一个点(.)是Logger B的名称的前缀就认为Logger A是LoggerB的祖先。
- 如果在Logger A和Logger B之间,Logger B没有任何其它的祖先就认为Logger A是LoggerB的父亲。
在Logger的层次结构的最顶层是root logger,它会永远存在,而且不能通过名字取到。
上面文字的描述可能不好的理解,为此我们给出了一张图,Logger的层次结构图,从中可以非常直观的看出三种主要组件的关系和各自所起的作用。
Loger x.y是Logger x.y.z的祖先,因为x.y.是x.y.z的前缀,这符合规则的前一条。另外在Loggerx.y和Logger x.y.z之间,Logger x.y.z没有其它的祖先,因此Logger x.y是Loggerx.y.z的父亲,这符合规则的后一条。这样我们依据上面的规则就可以构造出如图1所示的Logger的层次结构。
从图1中我们还可以看到每一个Logger都有一个Level,根据该Level的值Logger决定是否处理对应的日志请求。如果Level没有被设置,就象图1中的Loggerx.y一样,又该怎么办呢?答案是可以从祖先那里继承。
如果Logger C没有被设置Level,那么它将沿着它的层次结构向上查找,如果找到就继承并结束,否则会一直查找到rootlogger结束。因为log4j在设计时保证rootlogger会被设置一个默认的Level,所以任何logger都可以继承到Level。
图1中的Logger x.y没有被设置Level,但是根据上面的继承规则,Logger x.y继承了rootlogger的Level。
我们在来看看Logger选择日志记录请求(log request)的规则:
假设Logger M具有q级的Level,这个Level可能是设置的也可能是继承到的。
如果向LoggerM发出一个Level为p的日志记录请求,那么只有满足p>=q时这个日志记录请求才会被处理。
org.apache.log4j.Logger中的不同方法发出不同Level的日志记录请求,如下:
- public void debug(Object message),发出Level为DEBUG的日志记录请求
- public void info(Object message),发出Level为INFO的日志记录请求
- public void warn(Object message),发出Level为WARN的日志记录请求
- public void error(Object message),发出Level为ERROR日志记录请求
- public void fatal(Object message),发出Level为FATAL的日志请求
- public void log(Level l, Object message),发出指定Level的日志记录请求
其中的静态常量DEBUG、INFO、WARN、ERROR、FATAL是在org.apache.log4j.Level中定义的,除了使用这些预定义的Level之外,Log4j还支持自定义Level。
注:org.apache.log4j.Level中还预定义了一些其它的Level。
Appender
在Log4j中,Appender指的是日志记录输出的目的地。当前支持的Appender(目的地)有文件(file)、控制台(console)、java.io.OutputStream、java.io.Writer、远程服务器、远程UnixSyslog守护者、远程JMS监听者、NTEventLog或者发送e-mail。如果您在上面没有找到适合的Appender,那就需要考虑实现自己的自定义Appender了。
每个Logger可以有多个Appender,但是相同的Appender只会被添加一次。
Appender的附加性意味着Logger C会将日志记录发给它的和它祖先的所有Appender。在图1中Loggera会将日志记录发给它自己的JDBCAppender和它的祖先rootlogger的ConsoleAppender和FileAppender。Loggerx.y.z自己没有Appender,它将把日志记录发给它的祖先rootlogger的ConsoleAppender和FileAppender,如果Loggerx.y也含有Appender,那么它们也会包括在内。
Appender的附加性是可以被中断的。假设Logger C的一个祖先为Logger P,如果LoggerP的附加性标志(additivity flag)设置为假,那么Logger C会将日志记录只发给它的和在它和LoggerP之间的祖先(包括Logger P)的Appender,而不会发给LoggerP的祖先的Appender。Logger的附加性标志(additivity flag)默认值为ture。
在图1中如果没有设置Logger a的附加性标志(additivity flag),而是使用默认值true,那么Loggera会将日志记录发给它自己的JDBCAppender和它祖先rootlogger的ConsoleAppender和FileAppender,这和上面的描述相同。如果设置Loggera的附加性标志(additivity flag)的值false,那么Loggera会将日志记录发给它自己的JDBCAppender而不会在发给它祖先rootlogger的ConsoleAppender和FileAppender了。
Layout
Appender定制了输出目的地,通常我们还需要定制日志记录的输出格式,在Log4j中是通过将Layout和Appender关联到一起来实现的。Layout依据用户的要求来格式化日志记录。PatternLayout(标准Log4j组件)让用户依据类似于C语言printf函数的转换模式来指定输出格式。
例如,转换模式(conversion pattern)为"%r [%t] %-5p %c -%m%n"的PatternLayout将生成类似于以下内容的输出:
176 [main] INFO org.foo.Bar - Located nearest gas station.
在上面的输出中:
- 第一个字段表示自程序开始到发出日志记录请求时所消耗的毫秒数
- 第二个字段表示发出日志记录请求的线程
- 第三个字段表示日志记录请求的Level
- 第四个字段表示发出日志记录请求的Logger的名称
- 第五个字段(-后的文本)表示日志记录请求的消息
Log4j中还提到了一些其它的Layout,包括HTMLLayout、SimpleLayout、XMLLayout、TTCCLayout和DateLayout。如果这些不能满足您的要求,还可以自定义自己的Layout。