log4j 简明手册
时间:2006-09-21 来源:wuwenbin
log4j 简明手册
Ceki Gülcü
March 2002
Copyright© 2000-2004 The Apache Software Foundation. 版权所有。Log4j软件是在遵守Apache Software License 1.1版的条例下发行的,Apache Software License的复制件被包括在log4j发布的LICENSE.txt文件里。这个简短手册也借用了The complete log4j manual 里的一些内容,The complete log4j manual包含最新的更为详尽的信息。 The complete log4j manual
摘要
这个文档资料描述了log4j API,它的独特的特性和设计原理。Log4j是由许多 作者共同参与的开放源代码项目。它允许开发人员以任意的精细程度控制哪些日志说明被输出。通过使用外部的配置文件,可以在运行时配置它。最好的 是,log4j 开发包很容易上手。注意,它也可能会使一些开发人员着迷。
简介
几乎每个大的应用程序都有它自己的日志和跟踪程序的API。顺应这一规则,E.U. SEMPER项目组决定编写它自己的程序跟踪API(tracing API)。这开始于1996年早期。经过无数的工作,更改和性能加强,这个API终于成为一个十分受欢迎的Java日志软件包,那就是log4j。这个软件包的发行遵守open source动议认证的Apache Software License。最新的log4j版本包括全部的源代码,类文件和文档资料,可以在 http://logging.apache.org/log4j/找到它们。另外,log4j已经被转换成 C, C++, C#, Perl, Python, Ruby, 和 Eiffel 语言。
把log statements插入到你的代码中是一种排错的低技能办法。这也许是唯一的方法,因为排错工具并不总是可以被使用或者适用于你的程序。对于多线程的应用程序和多数发行的应用程序,通常就是这样的情形。
经验告诉我们logging是开发过程中重要的一环。它具有多种优点。首先,它能精确地提供运行时的上下文(context)。一旦在程序中加入了Log 代码,它就能自动的生成并输出logging信息而不需要人为的干预。另外,log信息的输出可以被保存到一个固定的地方,以备以后研究。除了在开发过程 中发挥它的作用外,一个性能丰富的日志记录软件包能当作一个审计工具(audit tool)使用。
Brian W. Kernighan 和 Rob Pike 在他们的"The Practice of Programming" 书中这样写到: "The Practice of Programming"
作为个人的选择,除了得到一大堆程序跟踪信息或一两个变量值以外,我们倾
向於不使用排错器。一个原因是在详细而复杂的数据结构和控制流程中很容易
迷失;我们发现认真思考并在关键处加入自我检查代码和输出指令,比起一步
步看程序要效率高。在日志说明里查找比在明智地放置自我检查代码后的输出
里查找要费时。而决定在哪里放置打印指令要比在日志说明里一步步找到关键
的代码要省时间。更重要的是,自我检查的排错指令和程序并存;而排错
sessions是暂时的。
Logging确实也有它的缺陷。它降低了程序运行的速度。它太冗长,查看时很容易错过。为了减少这些负面影响,log4j 被设计得可靠,高效和灵活。因为,记录日志很少是一个应用程序的主要焦点,log4j API 尽量做到容易被理解和使用。
Loggers, Appenders and Layouts
Log4j 有三个主要组件:loggers, appenders和layouts。这三类组件一起应用,可以让开发人员能够根据日志的类型和级别进行记录,并且能在程序运行时控制log信息输出的格式和往什么地方输出信息。
Logger hierarchy
任何logging API 与简单的System.out.println输出调试信息方法比较,最主要的优点在于它能够关闭一些调试信息输出而不影响其他人的调试。这种能力的实现是假设这些logging空间,也就是所有的可能发生的日志说明空间,可以根据程序开发人员选择的标准进行分类。这一观察以前使得我们选择了category作为这个软件包的中心概念。但是,在log4j 1.2版本以后,Logger类取代了Category类。对于那些熟悉早先版本的log4j的开发人员来说,Logger类只不过是Category类的一个别名。
Loggers是被命名的实体。Logger的名字大小写有区别(case-sensitive),并且它们遵守阶层式的命名规则:
Named Hierarchy 如果一个logger 的名字后面跟着一个点号(dot),它就是点号(dot)后面的那个logger的前辈( ancestor),是这个晚辈(descendant) 的前缀。如果在它自己和这个晚辈之间没有其它的前辈,它和这个晚辈之间就是父子关系。 |
例如,叫做"com.foo"的logger是叫做 "com.foo.Bar"的logger的父辈 。同样地,"java"是"java.util" 的父辈,是"java.util.Vector"的前辈。大多数开发人员都熟悉这种命名方法。 "com.foo" "com.foo.Bar" "java" "java.util" "java.util.Vector"
根(root)logger 位于logger 阶层的最上层。它在两个方面很特别:
- 它总是存在的,
- 不能通过使用它的名字直接得到它。
通过这个类的静态方法Logger.getRootLogger得到它(指RootLogger)。所有其他的loggers是通过静态方法Logger.getLogger来实例化并获取的。这个方法Logger.getLogger把所想要的logger的名字作为参数。 Logger类的一些其它基本方法在下面列出:
package org.apache.log4j; public class Logger { // Creation and retrieval methods: public static Logger getRootLogger(); public static Logger getLogger(String name); // printing methods: 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可以被指派优先级别。DEBUG, INFO, WARN, ERROR 和FATAL这组级别在org.apache.log4j.Level类中有定义。你也可以通过Level类的子类去定 义你自己的优先级别,尽管我们不鼓励你这样做。在后面我们会讲到一个更好 的方法。
如果一个logger没有被指定优先级别,它将继承最接近的祖先所被指定的优先级别。下面是更多关于优先级别的信息:
Level Inheritance 对于一个给定的logger C,它继承的级别等于logger阶层里,从C开始往root logger上去的第一个non-null级别。 |
要保证所有的loggers最终都继承一个优先级别,root logger总是有一个被指派的优先级。
下面是具有各种指派优先级别值的四个表格,以及根据上面的规则所得出的继承优先级别。
Logger |
指派 |
继承 |
根 |
Proot |
Proot |
X |
none |
Proot |
X.Y |
none |
Proot |
X.Y.Z |
none |
Proot |
例子 |
在上面的示例1中,只有root logger被指派了级别。这个级别的值,Proot,被其它的loggers X, X.Y 和 X.Y.Z继承了。
Logger |
指派 |
继承 |
根 |
Proot |
Proot |
X |
Px |
Px |
X.Y |
Pxy |
Pxy |
X.Y.Z |
Pxyz |
Pxyz |
例子 |
在上面的示例2中,所有的loggers都有一个指派的级别值。不需要级别继承。
Logger |
指派 |
继承 |
根 |
Proot |
Proot |
X |
Px |
Px |
X.Y |
none |
Px |
X.Y.Z |
Pxyz |
Pxyz |
例子 |
在示例3中,loggers root, X 和 X.Y.Z 分别被指派级别值Proot, Px 和Pxyz。Logger X.Y 从它的父辈X那里继承它的级别值。
Logger |
指派 |
继承 |
根 |
Proot |
Proot |
X |
Px |
Px |
X.Y |
none |
Px |
X.Y.Z |
none |
Px |
例子 |
在示例4中,loggers root和X 分别被指派级别值Proot和Px。Logger X.Y和X.Y.Z继承它们最接近的父辈X的被指派的级别值。
日志请求是通过调用一个日志实例的打印方法(之一)而产生的。这些打印方法是 log4j/Logger.html#debug(java.lang.Object)">debug, info, warn, error, fatal 和 log。
根据定义,打印方法决定一个日志请求的级别。例如,如果c是一个日志实例,那么语句c.info("..") 就是级别为INFO的一个日志请求。 c.info("..")
只有一个日志请求(A logging request)的级别高于或等于它的logger级别的时候才能够被执行。否则,则被认为这个日志请求不能被执行。一个没有被定义优先级别的logger将从层次关系中的前辈那里继承优先级别。这个规则总结如下:
<>
在一个级别为q(被指定的或继承的)的logger里,一个级别为p的日志请求,只有在p >= q 时才能够被执行。 |
这个规则是log4j的核心。它假设级别是有先后顺序的。对于标准的优先级别来说,DEBUG < INFO < WARN < ERROR < FATAL。
这里是一个关于这个规则的例子:
<>
// 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"); |
以一样的叁数名字调用getLogger方法,返回的reference总是指向完全相同的logger对象。
例如,在这里:
<>
Logger x = Logger.getLogger("wombat"); Logger y = Logger.getLogger("wombat"); |
x和y指向完全相同的logger对象。
因此,通过这种方式可以配置一个logger,而不需要传递references就能在其 他地方得到相同的实例。在生物的父子关系中父母总是排放在孩子们前面, log4j loggers与此有相互矛盾的地方,那就是log4j loggers可以以任何顺序被产生和配置。特别的是,一个"parent" logger 会找到并连接他的后代,即使他 是在他们之后被定义。
Log4j环境通常是在程序被初始化的时候被配置的。最好的方式是通过阅读一个配置文件去配置。我们会马上讨论到这方面的内容。
Log4j使得通过软件组件的名称去定义loggers的名字很容易。这可以通过在每 个类中静态地instantiating一个logger,让logger的名字与这个合格的java类文 件名相同来完成。这是一种有用并且直观的定义loggers的方式。因为日志的输出带有产生它们的logger的名字,这种命名策略使我们能够很方便地识别这 些log信息的来源。不过,尽管这是通用的一种loggers命名策略,Log4j没有限 制怎样对loggers进行命名。开发程序员可以根据自己的喜好随意定义 loggers。 software component
当然,至今所知的最好的命名策略还是以它们所在的类的名称来命名 loggers。
Appenders and Layouts
基于自身的logger选择性地使用或不使用日志请求(logging requests )的能 力仅仅整个Log4j能力的一部分。Log4j允许将log信息输出到许多不同的输出 设备中。用log4j的语言来说,一个log信息输出目的地就叫做一个appender。 目前,log4j 的appenders可以将log信息输出到console,files,GUI components,remote socket servers, JMS,NT Event Loggers,和 remote UNIX Syslog daemons。它还可以同时将log信息输出到多个输出设备中。 NT Event Loggers
多个appenders可以和一个logger连接在一起。
使用addAppender方法把一个appender加入到给定的logger上。一个给定的 logger的每一个被允许的日志请求都会被传递给 这个logger的所有appenders,以及阶层中高级别的appenders。 换句话说appenders是从logger阶层中不断添加地被继承的。例如,一个 console appender加给了root logger,那么,这个root logger所有被允许输出 的日志信息将被输出到console。如果你又给一个名字为C的logger添加了一个 file appender,那么C 以及C的子辈的所有被允许的日志信息将被同时输出到 file appender和console appender。可以通过把additivity flag设置为false来覆 盖这个默认的行为从而使appender的继承关系不再是添加性的。 Each enabled logging request for a given logger will be forwarded to all the appenders in that logger as well as the appenders higher in the hierarchy. setting the additivity flag
支配appender添加性的规则总结如下:
<>
Logger C的log输出信息将被输出到C的所有appenders和它的前辈的 appenders。这就是"appender additivity"的意思。 但是,如果logger C的前辈,比如说P,P的additivity flag被设置为 false,那 么,C的输出信息将被输出到C的所有appenders中去,以及它的前辈的——截 止在P那里,包括P在内的,appenders中去,但是不会输出到P的前辈的 appenders中去。 默认情况下,Loggers的additivity flag设置为true。 |
下面的表格显示一个示例:
<> <> <> <> <><> <> <> <> <><> <> <> <> <><> <> <> <> <>
Logger |
添加的 |
Additivity |
输出目标 |
注释 |
根 |
A1 |
not applicable |
A1 |
Root logger是无名的,但是可以通过Logger.getRootLogger() 来访问。Root logger没有附带默认的appender。 |
x |
A-x1, A-x2 |
true |
A1, A-x1, A-x2 |
"x" 和root logger里的Appenders。 |
x.y |
none |
true |
A1, A-x1, A-x2 |
"x" 和root logger里的Appenders。 |