Chapter 7: Logger
Overview
A logger is a component for recording messages. In Catalina a logger is associated with a container and is relatively simpler than other components. Tomcat provides various loggers in the org.apache.catalina.logger package. The application that accompanies this chapter can be found in the ex07.pyrmont package. Two classes, SimpleContext and Bootstrap, have changed from the application in Chapter 6.
记录器是用于记录消息的组件。
在 Catalina
中,日志记录器与容器相关联,与其他组件相比相对简单。
Tomcat
在 org.apache.catalina.logger
包中提供了各种日志记录器。
本章附带的应用程序可在 ex07.pyrmont
包中找到。
与第 6 章中的应用程序相比,SimpleContext
和 Bootstrap
这两个类有所变化。
There are three sections in this chapter. The first section covers the org.apache.catalina.Logger interface, the interface that all loggers must implement. The second section explains the loggers in Tomcat and the third details the application for this chapter that uses Tomcat's loggers.
本章共有三节。
第一节介绍所有日志记录器必须实现的 org.apache.catalina.Logger
接口。
第二节解释 Tomcat
中的日志记录器,第三节详细介绍本章中使用 Tomcat
日志记录器的应用程序。
The Logger Interface
A logger must implement the org.apache.catalina.Logger interface, which is given in Listing 7.1.
日志记录器必须实现 org.apache.catalina.Logger
接口,如清单 7.1 所示。
Listing 7.1: The Logger interface
清单 7.1: 日志记录器接口
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);
}
The Logger interface provides a number of log methods that the implementing class can choose to invoke. The simplest of these methods is the one that accepts a String as the message to be recorded.
Logger
接口提供了许多日志方法,实现该接口的类可以选择调用这些方法。
其中最简单的方法是接受一个字符串作为记录的消息。
The last two log methods accept a verbosity level. If the number passed is lower than the verbosity level set for the class's instance, the message is logged. Otherwise, the message is ignored. Five verbosity levels are defined as public Static variables:
最后两个日志方法接受一个详细程度级别。
如果传入的数字低于类实例设置的详细程度级别,那么消息将被记录。
否则,消息将被忽略。
定义了五个详细程度级别作为公共静态变量:
FATAL, ERROR, WARNING, INFORMATION, and DEBUG. The getVerbosity and setVerbosity methods are used to obtain and set this value.
FATAL
、ERROR
、WARNING
、INFORMATION
和 DEBUG
。
可以使用 getVerbosity
和 setVerbosity
方法来获取和设置这个值。
In addition, the Logger interface has the getContainer and setContainer methods to associate a Logger instance with a container. It also provides the addPropertyChangeListener and removePropertyChangeListener methods to add and remove a PropertyChangeListener.
此外,Logger
接口还有 getContainer
和 setContainer
方法,用于将Logger实例与容器关联。
它还提供了 addPropertyChangeListener
和 removePropertyChangeListener
方法,用于添加和移除 PropertyChangeListener
。
These methods will be clearer when you see their implementations in the Tomcat logger classes.
当您在Tomcat日志记录器类中看到它们的实现时,这些方法将更加清晰明了。
Tomcat's Loggers
Tomcat provides three loggers whose classes are FileLogger, SystemErrLogger, and SystemOutLogger. These classes can be found in the org.apache.catalina.logger package and they all extend the org.apache.catalina.logger.LoggerBase class. In Tomcat 4 the LoggerBase class implements the org.apache.catalina.Logger interface. In Tomcat 5, it also implements Lifecycle (discussed in Chapter 6) and MBeanRegistration (explained in Chapter 20).
Tomcat 提供了三个日志记录器,它们的类分别是 FileLogger
、SystemErrLogger
和 SystemOutLogger
。
这些类可在 org.apache.catalina.logger
包中找到,它们都扩展了 org.apache.catalina.logger.LoggerBase
类。
在 Tomcat 4 中,LoggerBase
类实现了 org.apache.catalina.Logger
接口。
在 Tomcat
5 中,它还实现了生命周期(Lifecycle
)(在第 6 章中讨论)和 MBeanRegistration
(在第 20 章中解释)。
The UML diagram of these classes is shown in Figure 7.1.
这些类的 UML 图如图 7.1 所示。
Figure 7.1: Tomcat's Loggers
图 7.1: Tomcat 的日志记录器
The LoggerBase Class
In Tomcat 5 the LoggerBase class is quite complex because it incorporates code for creating MBeans, which will only be discussed in Chapter 20. We therefore look at the LoggerBase class in Tomcat 4. You will be able to understand the LoggerBase class in Tomcat 5 if you understand the discussion in Chapter 20.
在Tomcat 5中,LoggerBase类非常复杂,因为它包含了用于创建MBeans的代码,这将在第20章中讨论。
因此,我们将在Tomcat 4
中查看 LoggerBase
类。如果您理解第20章的讨论,您将能够理解Tomcat 5中的 LoggerBase
类。
In Tomcat 4, the LoggerBase class is an abstract class that provides the implementation of all methods in the Logger interface except the log(String msg) method.
在Tomcat 4中,LoggerBase
类是一个抽象类,提供了 Logger
接口中除了 log(String msg)
方法之外的所有方法的实现。
public abstract void log(String msg);
This method overload is the one that does the logging in the child classes. All the other log method overloads call this overload. Because each child class logs messages to a different destination, this method overload is left blank in the LoggerBase class.
这个方法重载是在子类中记录日志的方法。
所有其他的日志方法重载都调用这个重载。
因为每个子类都会将消息记录到不同的目的地,所以在LoggerBase
类中,这个方法重载是空白的。
Now let's look at the verbosity level of this class. It is defined by a protected variable named verbosity, with ERROR as its default value:
现在让我们来看看这个类的详细级别。
它由一个名为verbosity
的受保护变量定义,默认值为ERROR
:
protected int verbosity = ERROR;
The verbosity level can be changed by calling the setVerbosity method, passing one of the following strings: FATAL, ERROR, WARNING, INFORMATION, or DEBUG. Listing 7.2 presents the setVerbosity method in the LoggerBase class.
可以通过调用 setVerbosity
方法,传入下列字符串之一来更改冗余度级别:FATAL
、ERROR
、WARNING
、INFORMATION
或 DEBUG
: FATAL
、ERROR
、WARNING
、INFORMATION
或 DEBUG
。
清单 7.2 介绍了日志基类中的 setVerbosity
方法。
Listing 7.2: The setVerbosity method
清单 7.2: setVerbosity
方法
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;
}
Two log methods accept an integer as the verbosity level. In these method overloads, the log(String message) overload is only called if the verbosity level passed is lower than the instance's verbosity levels. Listing 7.3 provides these method overloads.
有两个日志方法接受一个整数作为宽松度级别。
在这些方法重载中,只有当传递的冗余级别低于实例的冗余级别时,才会调用 log(String message)
重载。
清单 7.3 提供了这些方法重载。
Listing 7.3: The log method overloads that accept verbosity
清单 7.3:接受冗长度的日志方法重载
In the three child classes of LoggerBase discussed in the following sections, you will see the implementation of the log(String message) method overload.
在下面讨论的 LoggerBase
的三个子类中,你将看到 log(String message)
方法重载的实现。
The SystemOutLogger Class SystemOutLogger 类
This child class of LoggerBase provides the implementation of the log(String message) method overload. Every message received is passed to the System.out.println method. The SystemOutLogger class is given in Listing 7.4.
LoggerBase
的这个子类提供了 log(String message)
方法重载的实现。
收到的每条信息都会传递给 System.out.println
方法。清单 7.4 给出了 SystemOutLogger
类。
Listing 7.4: The SystemOutLogger Class
清单 7.4:SystemOutLogger
类
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
This class is very similar to the SystemOutLogger class, except that the message argument to the log(String message) method overload calls the System.err.println() method. The SystemErrLogger class is given in Listing 7.5.
该类与 SystemOutLogger
类非常相似,只是 log(String message)
方法重载的 message
参数调用了 System.err.println()
方法。清单 7.5 给出了 SystemErrLogger
类。
Listing 7.5: The SystemErrLogger class
清单 7.5:SystemErrLogger
类
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
The FileLogger class is the most sophisticated child class of LoggerBase. It writes the messages it receives from its associated container to a file and each message can be optionally time-stamped. When first instantiated, an instance of this class creates a file whose name contains the date information of today's date. If the date changes, it will create a new file for the new date and writes everything there. The class instance allows you to add a prefix and suffix to the name of its log file.
FileLogger
类是 LoggerBase
的最复杂的子类。
它将从其关联的容器接收到的消息写入文件,并且每条消息都可以选择添加时间戳。
当首次实例化时,该类的一个实例会创建一个文件,文件名包含今天的日期信息。
如果日期发生变化,它将为新日期创建一个新文件,并将所有内容写入其中。
该类实例允许您为其日志文件的名称添加前缀和后缀。
In Tomcat 4, the FileLogger class implements the Lifecycle interface so that it can be started and stopped just like any other components that implement org.apache.catalina.Lifecycle. In Tomcat 5, it is the LoggerBase class (the parent of FileLogger) that implements Lifecycle.
在Tomcat 4中,FileLogger
类实现了 Lifecycle
接口,以便可以像实现org.apache.catalina.Lifecycle
的其他组件一样启动和停止。
在Tomcat 5
中,实现Lifecycle
的是LoggerBase
类(FileLogger
的父类)。
The start and stop methods in the LoggerBase class in Tomcat 4 (inherited from the Lifecycle interface) do not do much more than trigger lifecycle events of the listeners interested in the starting and stopping of the file logger. The two methods are given in Listing 7.6. Note that the stop method also calls the private close method that closes the log file. The close method will be discussed later in this section.
Tomcat 4中LoggerBase
类中的start
和stop
方法(从Lifecycle接口继承)除了触发对文件记录器的启动和停止感兴趣的监听器的生命周期事件外,没有做更多的事情。
这两个方法如下所示(见代码清单7.6)。
请注意,stop
方法还调用了私有的close
方法,该方法关闭日志文件。稍后将讨论close
方法。
Listing 7.6: The start and stop methods
清单 7.6:启动和停止方法
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 ();
}
The most important method of the FileLogger class is the log method, given in Listing 7.7.
FileLogger
类最重要的方法是日志方法,如清单 7.7 所示。
Listing 7.7: The log method
清单 7.7:日志方法
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);
}
}
}
The log method receives a message and writes to a log file. During the lifetime of a FileLogger instance, the log method may open and close multiple log files. Typically, the log method rotates the log files by closing the current file and opening a new one if the date changes. Let's have a look at how the open, close, and log methods work.
日志方法接收信息并写入日志文件。
在 FileLogger
实例的生命周期内,日志方法可能会打开和关闭多个日志文件。
通常情况下,日志方法通过关闭当前文件并在日期发生变化时打开新文件来轮换日志文件。
让我们看看 open
、close
和 log
方法是如何工作的。
The open method(打开方法)
The open method, given in Listing 7.8, creates a new log file in the designated directory.
清单 7.8 中的 open 方法在指定目录下创建一个新的日志文件。
Listing 7.8: The open method
清单 7.8: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;
}
}
The open method first checks if the directory in which it is supposed to create a log file exists. If the directory does not exist, the method also creates the directory. The directory is stored in the class variable directory.
打开方法首先会检查要创建日志文件的目录是否存在。
如果该目录不存在,该方法也会创建该目录。
该目录存储在类变量 directory
中。
File dir = new File(directory);
if (!dir.isAbsolute())
dir = new File(System.getProperty("catalina.base"), directory);
dir.mkdirs();
It then composes the pathname of the file to open based on the directory path, the prefix, current date, and the suffix
然后,它会根据目录路径、前缀、当前日期和后缀,编译要打开的文件的路径名。
try (
String pathname = dir.getAbsolutePath() + File.separator +
prefix + date + suffix;
Next, it constructs an instance of java.io.PrintWriter whose writer is a java.io.FileWriter object that writes pathname. The PrintWriter instance is then assigned to the class variable writer. The log method uses writer to log the message.
接下来,它会构建一个 java.io.PrintWriter
实例,其写入器是一个写入路径名的 java.io.FileWriter
对象。
然后将 PrintWriter
实例分配给类变量 writer
。日志方法使用 writer
记录信息。
writer = new PrintWriter(new FileWriter(pathname, true), true);
The close method
The close method flushes the PrintWriter writer, flushes its content, closes the PrintWriter, sets the PrintWriter to null, and sets the date to an empty string. The close method is given in Listing 7.9.
close
方法会刷新 PrintWriter
写入器,刷新其内容,关闭 PrintWriter
,将 PrintWriter
设置为空,并将日期设置为空字符串。
关闭方法见清单 7.9。
Listing 7.9: The close method
清单 7.9:关闭方法
private void close() {
if (writer == null)
return;
writer.flush();
writer.close();
writer = null;
date = "";
}
The log method
The log method starts by creating an instance of java.sql.Timestamp class, which is a thin wrapper of the java.util.Date class. The purpose of instantiating the Timestamp class in the log method is to easily obtain the current date. The log method constructs a Timestamp instance by passing the current time in long to the Timestamp class's constructor.
日志方法首先创建一个 java.sql.Timestamp
类实例,该类是 java.util.Date
类的薄包装。
在日志方法中实例化 Timestamp
类的目的是轻松获取当前日期。
日志方法通过向 Timestamp
类的构造函数传递以 long
为单位的当前时间来构造 Timestamp
实例。
Timestamp ts = new Timestamp(System.currentTimeMillis());
Using the Timestamp class's toString method, you can get the string representation of the current date. The toString method's output has the following format.
使用 Timestamp
类的 toString
方法,可以获得当前日期的字符串表示。
toString
方法的输出格式如下。
yyyy-mm-dd hh:mm: SS.fffffffff
where fffffffff indicates the number of nanoseconds that has elapsed from 00:00:00. To get the date and hour only, the log method calls the substring method of the String class:
其中,ffffffffffff
表示从 00:00:00
开始经过的纳秒数。
要只获取日期和小时,日志方法会调用字符串类的子串方法:
String tsString = ts.toString().substring(0, 19);
Then, from tsString, to obtain the date part, the log method uses the following line:
然后,从 tsString
中获取日期部分,日志方法使用了下面一行:
String tsDate = tsString.substring(0, 10);
The log method then compares tsDate with the value of the String variable date, which initially contains an empty string. If the values of tsDate and date are not the same, it closes the current log file, assigns the value of tsDate to date, and opens a new log file.
然后,日志方法会比较 tsDate
和字符串变量 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();
}
}
}
Finally, the log method writes to the PrintWriter instance whose output stream is the log file. If the value of the boolean timestamp is true, it prefixes the message with the timestamp (tsString). Otherwise, it logs the message without a prefix.
最后,日志方法将信息写入 PrintWriter
实例,该实例的输出流就是日志文件。
如果布尔 timestamp
的值为 true
,则会在信息前加上时间戳 (tsString
) 的前缀。否则,日志将不加前缀。
// Log this message, timestamped if necessary
if (writer != null) {
if (timestamp) {
writer.println(tsString + " " + msg);
}
else {
writer.println(msg);
}
}
The Application
The application is very similar to the application in Chapter 6, except that you have a FileLogger that is associated with a SimpleContext object. The change to the application in Chapter 6 can be found in the ex07.pyrmont.startup.Bootstrap class's main method, given in Listing 7.10. In particular, take a close look at the highlighted code.
该应用程序与第 6 章中的应用程序非常相似,不同之处在于您有一个与 SimpleContext
对象关联的 FileLogger
。
对第 6 章中应用程序的修改可以在清单 7.10 中的 ex07.pyrmont.startup.Bootstrap
类的主方法中找到。
请仔细查看高亮显示的代码。
Listing 7.10: The Bootstrap class
清单 7.10:Bootstrap
类
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();
}
}
}
Summary
In this chapter you have learned about the logger components, reviewed the org.apache.catalina.Logger interface, and taken a close look of the three Tomcat implementations of the Logger interface. In addition, the application that accompanies this chapter uses the FileLogger class, the most advanced logger available in Tomcat.
在本章中,你已经了解了日志记录器组件,回顾了 org.apache.catalina.Logger
接口,并仔细研究了日志记录器接口的三种 Tomcat
实现。
此外,本章附带的应用程序使用了 FileLogger
类,这是 Tomcat
中最先进的日志记录器。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。