本章将讨论Logger,一个用来记录信息的组件。Logger一般与一个容器关联,是一个相对比较简单的组件。你可以在org.apache.catalina.logger包中找到很多Tomcat提供的很多种不同的Logger。
Logger接口
所有Logger必须实现org.apache.catalina.Logger接口。
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);
}
接口的最后两个方法中提供了一个冗余级别,如传递的数字低于该类设置的冗余级,那么这条信息就会被记录,否则就忽略。在接口中定义了5个冗余级别:FATAL, ERROR, WARNING, INFORMATION, 和DEBUG。getVerbosity方法和setVerbosity方法用来获取和设置冗余级别。
另外,Logger接口中的getContainer和serContainer可以将这个Logger实例与一个容器关联起来。同时Logger接口也提供了addPropertyChangeListener和removePropertyChangeListener两个方法来增加和删除PropertyChangeListener。
Tomcat’s Loggers
Tomcat提供了三种Logger,分别是FileLogger,SystemErrLogger,和SystemOutLogger。这三个Logger都继承了org.apache.catalina.logger.LoggerBase,LoggerBase又实现了org.apache.catalina.Logger接口,下面是类图。
LoggerBase类
LoggerBase类是一个抽象类,它提供了Logger接口中除了log(String msg)方法以外其它所有方法的实现。
public abstract void log(String msg);
这个方法会在子类中实现,其它所有的Log方法都调用了这个方法。因为不同的子类会把日志记录到不同的地方,所以这个方法会留给子类来实现。
SystemOutLogger类
这个子类重载了LoggerBase类中的log方法,将接收到的所有数据都传递给了方法System.out.println
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);
}
}
SystemErrLogger类
同SystemOutLogger相似,只是重载的log方法将接收到的信息传递给了System.err.println方法
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);
}
}
FileLogger类
这个将是本章重点讨论的类
FileLogger类
FileLogger类是LoggerBase的子类中最复杂的,它将从与它关联的容器中收到的信息写到文件中,而且每条信息都可以加盖时间戳。当第一次被实例化时,这个类的实例会创建一个文件,文件的名称会包含当前日期。如果日期变了,它会为新的日期创建一个文件并将所有信息都写入这个新文件中。这个类的实例允许你为日志文件的名称添加一个前缀或是后缀。另外FileLogger类实现了Lifecycle接口,所以它可以像其它实现了Lifecycle接口那样启动和停止了。
继承自Lifecycle的start和stop方法,在这两个方法中没有做太多工作,只是在FileLogger的启动和停止过程中触发生命周期事件供对某个事件感兴趣的监听器使用。
start方法:
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;
}
stop方法:
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();
}
需要注意的是,在stop方法中调用了私有方法close去关闭日志文件,这个方法将在后面讨论。
在FileLogger类中最重要的方法就是log方法,下面是它的实现
public void log(String msg) {
// Construct the timestamp we will use, if requested
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方法接收消息并将消息写入日志文件。在FileLogger的生命周期内,log方法可能会打开或关闭多个日志文件。比如,当日期发生变化时,log方法会关闭当前日志文件然后再打开一个新日志文件。接下来主要讨论open,close和log方法的工作流程。
open方法
open方法可以在指定的目录下创建一个新的日志文件
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) {
writer = null;
}
}
open方法首先会检查指定目录中是否已存在要创建的目录。如果指定目录也不存在,那么它会创建这个目录。指定目录存放在环境变量中。
close方法
close方法清空PrintWriter的缓存和当前内容。关闭PrintWriter对象。
log方法
log方法中首先会创建一个java.sql.Timestamp对象,这是java.util.Date类的一个简单的封装,实例化一个Timestamp类只是为了方便获取时间戳。
public void log(String msg) {
// Construct the timestamp we will use, if requested
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);
}
}
}