4. 配置
插入日志请求到应用程序的代码中需要大量的预先计划和最终努力。观察显示大约4%的代码是用来输出的。
因此,大小适度的程序都被嵌入有成千个日志输出语句。为了以无需手工的方式管理这些日志的输出状态,给日志输出以编号和规范变得势在必行。
Log4j在程序中有充分的可配置性。然而,用配置文件配置Log4j具有更大的弹性。目前,它的配置文件支持xml和java properties(key=value)文件两种格式。
让我们以一个例子来演示它是如何做的。假定有一个用了Log4j的程序MyApp。
import com.foo.Bar;
// Import Log4j classes.
import org.apache.Log4j.Logger;
import org.apache.Log4j.BasicConfigurator;
public class MyApp {
// Define a static logger variable so that it references the
// Logger instance named "MyApp".
static Logger logger = Logger.getLogger(MyApp.class);
public static void main(String[] args) {
// Set up a simple configuration that logs on the console.
BasicConfigurator.configure();
logger.info("Entering application.");
Bar bar = new Bar();
bar.doIt();
logger.info("Exiting application.");
}
}
MyApp以引入Log4j的相关类开始,接着它定义了一个静态logger变量,并给予值为"MyApp"类的全路径名称。
MYApp用了定义在包com.foo中的类Bar.
package com.foo;
import org.apache.Log4j.Logger;
public class Bar {
static Logger logger = Logger.getLogger(Bar.class);
public void doIt() {
logger.debug("Did it again!");
}
}
调用BasicConfigurator.configure()方法创建了一个相当简单的Log4j的设置。它加入一
个ConsoleAppender到根logger。输出将被采用了"%-4r [%t] %-5p %c %x - %m%n"模式
的PatternLayout所格式化。
注意,根logger默认被分配了Level.DEBUG的级别。
MyApp的输出为:
0 [main] INFO MyApp - Entering application.
36 [main] DEBUG com.foo.Bar - Did it again!
51 [main] INFO MyApp - Exiting application.
随后的图形描述了在调用BasicConfigurator.configure()方法后MyApp的对象图。
一边要提醒的是,Log4j的子logger只连接到已经存在的它们的父代。特别的是,名为
com.foo.bar的logger是直接连接到根logger,而不是围绕着没用的com或com.foo
logger。这显著的提高了程序性能并且减少的内存占用。
MyApp类配置Log4j是通过调用BasicConfigurator.configure 方法。其它的类仅仅
需要引入org.apache.Log4j.Logger 类,找到它们希望用的logger,并且用它就行。
以前的例子通常输出同样的日志信息。幸运的是,修改MyApp是容易的,以便日志输
出可以在运行时刻被控制。这里是一个小小修改的版本。
import com.foo.Bar;
import org.apache.Log4j.Logger;
import org.apache.Log4j.PropertyConfigurator;
public class MyApp {
static Logger logger = Logger.getLogger(MyApp.class.getName());
public static void main(String[] args) {
// BasicConfigurator replaced with PropertyConfigurator.
PropertyConfigurator.configure(args[0]);
logger.info("Entering application.");
Bar bar = new Bar();
bar.doIt();
logger.info("Exiting application.");
}
}
修改后的 MyApp通知程序调用PropertyConfigurator()方法解析一个配置文件,并且根
据这个配置文件来设置日志。
这里是一个配置文件的例子,它将产生同以前BasicConfigurator 基本例子一样
的输出结果。
# Set root logger level to DEBUG and its only appender to A1.
Log4j.rootLogger=DEBUG, A1
# A1 is set to be a ConsoleAppender.
Log4j.appender.A1=org.apache.Log4j.ConsoleAppender
# A1 uses PatternLayout.
Log4j.appender.A1.layout=org.apache.Log4j.PatternLayout
Log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
假设我们不在对com.foo包的任何类的输出感兴趣的话,随后的配置文件向我们展示
了实现这个目的的方法之一。
Log4j.rootLogger=DEBUG, A1
Log4j.appender.A1=org.apache.Log4j.ConsoleAppender
Log4j.appender.A1.layout=org.apache.Log4j.PatternLayout
# Print the date in ISO 8601 format
Log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
# Print only messages of level WARN or above in the package com.foo.
Log4j.logger.com.foo=WARN
以这个配置文件配置好的MyApp将输出如下:
2000-09-07 14:07:41,508 [main] INFO MyApp - Entering application.
2000-09-07 14:07:41,529 [main] INFO MyApp - Exiting application.
当logger com.foo.bar没有被分配一个级别,它将从com.foo继承,在配置文件中
它被设置了WARN的级别。在Bar.doIt方法中定义的log为DEBUG级别,低于WARN,
因此doIt() 方法的日志请求被禁用。
这里是另外一个配置文件,它使用了多个appenders.
Log4j.rootLogger=debug, stdout, R
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
Log4j.appender.R=org.apache.Log4j.RollingFileAppender
Log4j.appender.R.File=example.log
Log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
Log4j.appender.R.MaxBackupIndex=1
Log4j.appender.R.layout=org.apache.Log4j.PatternLayout
Log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
以这个配置文件调用加强了的MyApp类将输出如下信息.
INFO [main] (MyApp2.java:12) - Entering application.
DEBUG [main] (Bar.java:8) - Doing it again!
INFO [main] (MyApp2.java:15) - Exiting application.
另外,因为根logger有被分配第二个appender,所以输出也将被定向到example.log文件。
这个文件大小达到100kb时将自动备份。备份时老版本的example.log文件自动被移到
文件example.log.1中。
注意我们不需要重新编译代码就可以获得这些不同的日志行为。我们一样可以容易
的使日志输出到UNIX Syslog daemon, 重定向所有的com.foo到NT Event logger,
或者转发日志到一个远程的Log4j服务器,它根据本地server的策略来进行日志输出。例
如转发日志事件到第二个Log4j服务器.
5. 默认的初始化过程
Log4j类库不对它的环境做任何假设。特别是没有默认的Log4j appender。在一些特别
的有着良好定义的环境下,logger的静态inializer将尝试自动的配置Log4j。
java语言的特性保证类的静态initializer当且仅当装载类到内存之时只会被调用一次。
要记住的重要一点是,不同的类装载器可能装载同一个类的完全不同的拷贝。
这些同样类的拷贝被虚拟机认为是完全不相干的。
默认的initialization是非常有用的,特别是在一些应用程序所依靠的运行环境被准确的
定位的情况下。例如,同一样的应用程序可以被用做一个标准的应用程序,或一个
applet,或一个在web-server控制下的servlet。
准确的默认的initialization原理被定义如下:
1.设置系统属性Log4j.defaultInitOverride为"false"以外的其它值,那么Log4j将
跳过默认的initialization过程。
2.设置资源变量字符串给系统属性Log4j.configuration。定义默认initialization
文件的最好的方法是通过系统属性Log4j.configuration。万一系统属性
Log4j.configuration没有被定义,那么设置字符串变量resource 给它的默认值
Log4j.properties。
3.尝试转换resource 变量为一个URL。
4.如果变量resource的值不能被转换为一个URL,例如由于MalformedURLException违
例,那么通过调用
org.apache.Log4j.helpers.Loader.getResource(resource, Logger.class) 方法从
classpath中搜索resource,它将返回一个URL,并通知"Log4j.properties"的值是一个错
误的URL。
看See Loader.getResource(java.lang.String) 查看搜索位置的列表。
5.如果没有URL被发现,那么放弃默认的initialization。否则用URL配置Log4j。
PropertyConfigurator将用来解析URL,配置Log4j,除非URL以".xml"为结尾。
在这种情况下的话DOMConfigurator将被调用。你可以有机会定义一个自定义的
configurator。
系统属性Log4j.configuratorClass 的值取自你的自定义的类名的全路径。
你自定义的configurator必须实现configurator接口。
6. 配置范例 6.1 Tomcat下的初始化
默认的Log4j initialization典型的应用是在web-server 环境下。在tomcat3.x和tomcat4.x
下,你应该将配置文件Log4j.properties放在你的web应用程序的WEB-INF/classes 目录
下。
Log4j将发现属性文件,并且以此初始化。这是使它工作的最容易的方法。
你也可以选择在运行tomcat前设置系统属性Log4j.configuration 。对于tomcat 3.x,
TOMCAT_OPTS 系统变量是用来设置命令行的选项。对于tomcat4.0,用系统环境变
量CATALINA_OPTS 代替了TOMCAT_OPTS。
Example 1
UNIX 命令行
export TOMCAT_OPTS="-DLog4j.configuration=foobar.txt"
告诉Log4j用文件foobar.txt作为默认的配置文件。这个文件应该放在WEB-INF/classes
目录下。这个文件将被PropertyConfigurator所读。每个web-application将用不同的默认
配置文件,因为每个文件是和它的web-application 相关的。
Example 2
UNIX 命令行
export TOMCAT_OPTS="-DLog4j.debug -DLog4j.configuration=foobar.xml"
告诉Log4j输出Log4j-internal的调试信息,并且用foobar.xml作为默认的配置文件。
这个文件应该放在你的web-application的WEB-INF/classes 目录下。因为有.xml的
扩展名,它将被DOMConfigurator所读。每个web-application将用不同的默认
配置文件。因为每个文件都和它所在的web-application 相关的。
Example 3
UNIX 命令行
set TOMCAT_OPTS=-DLog4j.configuration=foobar.lcf -DLog4j.configuratorClass=com.foo.BarConfigurator
告诉Log4j用文件foobar.lcf作为默认的配置文件。这个文件应该放在你的
web-application的WEB-INF/classes 目录下。因为定义了Log4j.configuratorClass 系统属
性,文件将用自定义的com.foo.barconfigurator类来解析。每个web-application将用不
同的默认配置文件。因为每个文件都和它所在的web-application 相关的。
Example 4
UNIX 命令行
set TOMCAT_OPTS=-DLog4j.configuration=file:/c:/foobar.lcf
告诉Log4j用文件foobar.lcf作为默认的配置文件。这个配置文件用URL file:/c:/foobar.lcf
定义了全路径名。这样同样的配置文件将被所有的web-application所用。
不同的web-application将通过它们自己的类装载器来装载Log4j。这样,每个Log4j的环
境将独立的运作,而没有任何的相互同步。例如:在多个web-application中定义了
完全相同的输出源的FileAppenders将尝试写同样的文件。结果好象是缺乏安全性的。
你必须确保每个不同的web-application的Log4j配置没有用到同样的系统资源。
6.2 Servlet 的初始化
用一个特别的servlet来做Log4j的初始化也是可以的。如下是一个例子:
package com.foo;
import org.apache.Log4j.PropertyConfigurator;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.IOException;
public class Log4jInit extends HttpServlet {
public void init() {
String prefix = getServletContext().getRealPath("/");
String file = getInitParameter("Log4j-init-file");
// if the Log4j-init-file is not set, then no point in trying
if(file != null) {
PropertyConfigurator.configure(prefix+file);
}
}
public void doGet(HttpServletRequest req, HttpServletResponse res) {
}
}
在web.xml中定义随后的servlet为你的web-application。
<servlet>
<servlet-name>Log4j-init</servlet-name>
<servlet-class>com.foo.Log4jInit</servlet-class>
<init-param>
<param-name>Log4j-init-file</param-name>
<param-value>WEB-INF/classes/Log4j.lcf</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
写一个初始化的servlet是最有弹性的初始化Log4j的方法。代码中没有任何限制,你可
以在servlet的init方法中定义它。
本文地址:http://com.8s8s.com/it/it18219.htm