Chapter 7: Logger
日志是一个记录信息的组件。在Catalina中,它容器相关联的日志比其它组件相对简单得多。Tomcat在org.apache.catalina.logger包下提供多种类型的日志。
本章有三个部分:第一部分包括org.apache.catalina.Logger接口,所有的日志组件都必须实现这个接口。第二部分介绍了在Tomcat中的日志,第三章详细讲述了本章的应用程序使用Tomcat的日志。
The Logger Interface
一个日志必须实现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);
}
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)方法重载的实现。
The SystemOutLogger Class
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);
}
}
The SystemErrLogger Class
这个类和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);
}
}
The FileLogger Class
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方法怎么工作。
The open method
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;
}
}
writer = new PrintWriter(new FileWriter(pathname, true), true);
The close method
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 = "";
}
The log method
log方法通过创建一个java.sql.Timestamp类的实例开始。这个Timestamp类是一个java.util.Date类的轻微包装。在log方法里实例化Timestamp类的目是为了更容易获取当前日期。log方法用传递一个用long长整型表示的当前时间给Timestamp类的构造器来构建一个TimeStamp实例。
Timestamp ts = new Timestamp(System.currentTimeMillis());
// If the date has changed, switch log files
if (!date.equals(tsDate)) {
synchronized (this) {
if (!date.equals(tsDate)) {
close();
date = tsDate;
open();
}
}
}