Coder 爱翻译 How Tomcat Works 第七章

[size=x-large]Chapter 7: Logger[/size]

日志是一个记录信息的组件。在Catalina中,它容器相关联的日志比其它组件相对简单得多。Tomcat在org.apache.catalina.logger包下提供多种类型的日志。
本章有三个部分:第一部分包括org.apache.catalina.Logger接口,所有的日志组件都必须实现这个接口。第二部分介绍了在Tomcat中的日志,第三章详细讲述了本章的应用程序使用Tomcat的日志。

[size=large]The Logger Interface[/size]

一个日志必须实现org.apache.catalina.Logger接口:

Listing 7.1: The Logger interface

package org.apache.catalina;
import java.beans.PropertyChangeListener;

public interface Logger {
public static final int FATAL = Integer.MIN_VALUE;
public static final int ERROR = 1;
public static final int WARNING = 2;
public static final int INFORMATION = 3;
public static final int DEBUG = 4;
public Container getContainer();
public void setContainer(Container container);
public String getInfo();
public int getVerbosity();
public void setVerbosity(int verbosity);
public void addPropertyChangeListener(PropertyChangeListener listener);
public void log(String message);
public void log(Exception exception, String msg);
public void log(String message, Throwable throwable);
public void log(String message, int verbosity);
public void log(String message, Throwable throwable, int verbosity);
public void removePropertyChangeListener(PropertyChangeListener listener);
}

Logger接口提供许多log方法供实现类选择调用。这些方法中最简单的方法是接受一个字符串,这个字符串被用来当做记录信息。

最后两个log方法接收一个详细记录级别。如果传递的这个数比这个类实例设置的详细记录级别低,这个信息就会被记录下来。否则,这个信息会被忽略。这里用public Static变量定义了5个详细记录级别:FATAL, ERROR, WARNING, INFORMATION和DEBUG。而getVerbosity和setVerbosity方法用来获取和设置相应的值。

此外,Logger接口有getContainer和setContainer方法把一个Logger实例和一个容器相关联起来。它也提供addPropertyChangeListener和removePropertyChangeListener方法,来增加和移除一个PropertyChangeListener。

当你看见在Tomcat中的logger类的实现,这些方法将会更清楚,容易理解。

[size=large]Tomcat's Loggers[/size]

Tomcat提供三个日志记录类:FileLogger, SystemErrLogger和SystemOutLogger。这些类都在org.apache.catalina.logger包下面,且它们都继承自org.apache.catalina.logger.LoggerBase类。在Tomcat 4中,LoggerBase类实现了org.apache.catalina.Logger接口。在Tomcat 5中,它也实现了Lifecycle和MbeanRegistration。类图:

[img]http://dl.iteye.com/upload/attachment/361175/6a35b4be-9e94-3df4-9c09-23508965eb94.jpg[/img]

[size=large]The LoggerBase Class[/size]

在Tomcat 5中,LoggerBase类相当复杂,因为它包含了创建MBeans的代码。这里我们看Tomcat 4中的LoggerBase类。

在Tomcat 4中,LoggerBase类是一个抽象类,它提供Logger接口的所有方法实现,除了
log(String msg)方法。

public abstract void log(String msg);

这个方法重载是在子类中记录信息。而其他所有的log方法重载都调用这个重载。因为每一个子类把记录信息记录到不同的地方,所以这个在LoggerBase类的方法重载为空。

现在看看这个类的详细记录等级。它通过一个protected的叫做verbosity的变量来定义的。ERROR是它的默认值:

protected int verbosity = ERROR;

详细记录等级可以通过调用setVerbosity方法来改变等级。传递下面的字符串:FATAL, ERROR, WARNING, INFORMATION或DEBUG。


Listing 7.2: The setVerbosity method

public void setVerbosityLevel(String verbosity) {
if ("FATAL".equalsIgnoreCase(verbosity))
this.verbosity = FATAL;
else if ("ERROR".egualsIgnoreCase(verbosity))
this.verbosity = ERROR;
else if ("WARNING".equalsIgnoreCase(verbosity))
this.verbosity = WARNING;
else if ("INFORMATION".equalsIgnoreCase(verbosity))
this.verbosity = INFORMATION;
else if ("DEBUG".equalsIgnoreCase(verbosity))
this.verbosity = DEBUG;
}


有两个log方法接收一个整型作为详细记录等级。在这些方法重载中,如果传递给详细记录等级的值比实例的详细记录的等级低时,log(String message)这个方法重载会被调用。

Listing 7.3: The log method overloads that accept verbosity

public void log(String message, int verbosity) {
if (this.verbosity >= verbosity)
log(message);
}
public void log(String message, Throwable throwable, int verbosity) {
if (this.verbosity >= verbosity)
log(message, throwable);
}

下面我们讨论LoggerBase类的三个子类,你将看到log(String message)方法重载的实现。

[size=large]The SystemOutLogger Class[/size]

LoggerBase类的子类提供了log(String message)方法重载的实现。每一个接收的message会被传递给System.out.println方法。

Listing 7.4: The SystemOutLogger Class

package org.apache.catalina.logger;
public class SystemOutLogger extends LoggerBase {

protected static final String info = "org.apache.catalina.logger.SystemOutLogger/1.0";
public void log(String msg) {
System.out.println(msg);
}
}


[size=large]The SystemErrLogger Class[/size]

这个类和SystemOutLogger相似。除了传递给log(String message)方法的重载的message参数的调用,message会传递给System.err.println()方法。

Listing 7.5: The SystemErrLogger class

package org.apache.catalina.logger;
public class SystemErrLogger extends LoggerBase {

protected static final String info = "org.apache.catalina.logger.SystemErrLogger/1.0";
public void log(String msg) {
System.err.println(msg);
}
}


[size=large]The FileLogger Class[/size]

FileLogger类是LoggerBase的最复杂的子类。它把接收到的信息写入一个文件。每个信息可以被标记上时间戳。当首先初始化时,这个类的实例创建一个文件,它的名字包含当天的日期信息。如果日期变化了,它将为这个新日期创建一个新的文件,然后记录下所有信息。这个类实例允许你添加一个前缀和后缀给日志文件的名字。

在Tomcat 4中。FileLogger类实现了Lifecycle接口,所以它可以被实现了org.apache.catalina.Lifecycle接口的任意组件来启动和停止它。在Tomcat 5中,LoggerBase类(FileLogger的父类)实现了Lifecycle。

在tomcat 4中LoggerBase(继承Lifecycle接口)类的start和stop方法只是触发了监听器相关的生命周期事件的启动和停止事件。注意stop方法也调用了private close方法关闭日志文件。

Listing 7.6: The start and stop methods

public void start() throws LifecycleException {
// Validate and update our current component state
if (started)
throw new LifecycleException (sm.getString("fileLogger.alreadyStarted"));
lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
}
public void stop() throws LifecycleException {
// Validate and update our current component state
if (!started)
throw new LifecycleException (sm.getString("fileLogger.notStarted"));
lifecycle.fireLifecycleEvent(STOP__EVENT, null);
started = false;
close ();
}

FileLogger类的最重要的方法是log方法。

Listing 7.7: The log method

public void log(String msg) {
// Construct the timestamp we will use, if reguested
Timestamp ts = new Timestamp(System.currentTimeMillis());
String tsString = ts.toString().substring(0, 19);
String tsDate = tsString.substring(0, 10);
// If the date has changed, switch log files
if (!date.equals(tsDate)) {
synchronized (this) {
if (!date.equals(tsDate)) {
close ();
date = tsDate;
open ();
}
}
}
// Log this message, timestamped if necessary
if (writer != null) {
if (timestamp) {
writer.println(tsString + " " + msg);
} else {
writer.println(msg);
}
}
}

log方法接收一个message,并写入到日志文件。在FileLogger实例的生命周期期间,log方法可能打开和关闭多个日志文件。典型地,如果日期变化了,log方法通过关闭当前文件,并打开一个新的文件来交替日志文件。让我们看下open、close和log方法怎么工作。

[size=large]The open method[/size]

open方法在制定的目录中创建一个新的log文件。

Listing 7.8: The open method

private void open() {
// Create the directory if necessary
File dir = new File(directory);
if (!dir.isAbsolute())
dir = new File(System.getProperty("catalina.base"), directory);
dir.mkdirs();
// Open the current log file
try {
String pathname = dir.getAbsolutePath() + File.separator + prefix + date + suffix;
writer = new PrintWriter(new FileWriter(pathname, true), true);
}
catch (IOException e) {
catch (IOException e) {
writer = null;
}
}

open方法首先检查创建日志文件的目录是否存在。如果这个目录不存在,这个方法也创建这个目录。这个目录被存储在类变量directory。

File dir = new File(directory);
if (!dir.isAbsolute())
dir = new File(System.getProperty("catalina.base"), directory);
dir.mkdirs();

然后它路径的文件打开,前缀当前日期和后缀。

try (
String pathname = dir.getAbsolutePath() + File.separator + prefix + date + suffix;

接下来,它构建一个java.io.PrintWriter实例,它的writer是一个记录了路径名的java.io.FileWriter对象。然后PrintWriter实例指配给类变量writer。log方法使用writer来记录message。

writer = new PrintWriter(new FileWriter(pathname, true), true);


[size=large]The close method[/size]

close方法刷新PrintWriter的writer,刷新它的内容,关闭PrintWriter,把PrintWriter设置为null,并设置日期为一个空字符串。

Listing 7.9: The close method

private void close() {
if (writer == null)
return;
writer.flush();
writer.close();
writer = null;
date = "";
}

[size=large]The log method[/size]

log方法通过创建一个java.sql.Timestamp类的实例开始。这个Timestamp类是一个java.util.Date类的轻微包装。在log方法里实例化Timestamp类的目是为了更容易获取当前日期。log方法用传递一个用long长整型表示的当前时间给Timestamp类的构造器来构建一个TimeStamp实例。

Timestamp ts = new Timestamp(System.currentTimeMillis());

使用Timestamp的toString方法,你可以获取当前日期的字符串表现形式。toString方法的输出下面格式:

yyyy-mm-dd hh:mm: SS.fffffffff

fffffffff表示从00:00:00经过的毫秒数。要获取日期和小时,log方法调用substring方法:

String tsString = ts.toString().substring(0, 19);

然后,从tsString获取日期部分,log方法使用下面方法:

String tsDate = tsString.substring(0, 10);

然后log方法比较tsDate和初始化为空字符串的String变量date,如果tsDate的值和date变量不相同,它就关闭当前日志文件,指配tsDate的值给date,打开这个新的日志文件。

// If the date has changed, switch log files
if (!date.equals(tsDate)) {
synchronized (this) {
if (!date.equals(tsDate)) {
close();
date = tsDate;
open();
}
}
}

最后,log方法把输出流PrintWriter实例写入日志文件。如果timestamp的变量值是true,它给信息加上时间戳前缀(tsString)。否则,它记录的信息,不加时间戳前缀。

// Log this message, timestamped if necessary
if (writer != null) {
if (timestamp) {
writer.println(tsString + " " + msg);
} else {
writer.println(msg);
}
}

[size=large]The Application[/size]

和第六章的应用程序类似。除了你有一个SimpleContext对象相关的FileLogger。

Listing 7.10: The Bootstrap class

package ex07.pyrmont.startup;

import ex07.pyrmont.core.SimpleContext;
import ex07.pyrmont.core.SimpleContextLifecycleListener;
import ex07.pyrmont.core.SimpleContextMapper;
import ex07.pyrmont.core.SimpleLoader;
import ex07.pyrmont.core.SimpleWrapper;
import org.apache.catalina.Connector;
import org.apache.catalina.Context;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Loader;
import org.apache.calalina.loggor.FileLogger;
import org.apache.catalina.Mapper;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.http.HttpConnector;

public final class Bootstrap {
public static void main(String[] args) {
Connector connector = new HttpConnector();
Wrapper Wrapper1 = new SimpleWrapper();
Wrapper1.setName("Primitive");
Wrapper1.setServletClass("PrimitiveServlet");
Wrapper wrapper2 = new SimpleWrapper();
wrapper2.setName("Modern");
Wrapper2.setServletClass("ModernServlet");
Loader loader = new SimpleLoader();
Context context = new SimpleContext();
context.addChild(wrapper1);
context.addChild(wrapper2);

Mapper mapper = new SimpleContextMapper();
mapper.setProtocol("http");
LifecycleListener listener = new SimpleContextLifecycleListener();
((Lifecycle) context).addLifecycleListener(listener);
context.addMapper(mapper);
context.setLoader(loader);
// context.addServletMapping(pattern, name);
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");
// ------ add logger --------
System.setProperty("catalina.base",
System.getProperty("user.dir"));
FileLogger logger = new FileLogger();
logger.setPrefix("FileLog_");
logger.setSuffix(".txt");
logger.setTimestamp(true);
logger.setDirectory("webroot");
context.setLogger(logger);
//--------------------------
connector.setContainer(context);
try {
connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) context).start();
// make the application wait until we press a key.
System.in.read();
((Lifecycle) context).stop();
} catch (Exception e) {
e.printStackTrace();
}
}
}


第七章 完
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值