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 中,日志记录器与容器相关联,与其他组件相比相对简单。

Tomcatorg.apache.catalina.logger 包中提供了各种日志记录器。

本章附带的应用程序可在 ex07.pyrmont 包中找到。

与第 6 章中的应用程序相比,SimpleContextBootstrap 这两个类有所变化。

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.

FATALERRORWARNINGINFORMATIONDEBUG

可以使用 getVerbositysetVerbosity 方法来获取和设置这个值。

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 接口还有 getContainersetContainer 方法,用于将Logger实例与容器关联。

它还提供了 addPropertyChangeListenerremovePropertyChangeListener 方法,用于添加和移除 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 提供了三个日志记录器,它们的类分别是 FileLoggerSystemErrLoggerSystemOutLogger

这些类可在 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 所示。

image.png

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 方法,传入下列字符串之一来更改冗余度级别:FATALERRORWARNINGINFORMATIONDEBUGFATALERRORWARNINGINFORMATIONDEBUG

清单 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类中的startstop方法(从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 实例的生命周期内,日志方法可能会打开和关闭多个日志文件。

通常情况下,日志方法通过关闭当前文件并在日期发生变化时打开新文件来轮换日志文件。

让我们看看 opencloselog 方法是如何工作的。

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 的值,后者最初包含一个空字符串。

如果 tsDatedate 的值不一致,它会关闭当前日志文件,将 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 中最先进的日志记录器。


Xander
195 声望50 粉丝