首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Coder 爱通译 How Tomcat Works 第七章

2012-09-09 
Coder 爱翻译 How Tomcat Works 第七章Chapter 7: Logger日志是一个记录信息的组件。在Catalina中,它容器相

Coder 爱翻译 How Tomcat Works 第七章
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); } 

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

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

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

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

Tomcat's Loggers

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



The LoggerBase Class

在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)方法重载的实现。

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;    } } 

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);


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());

使用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);      }    }

The Application

和第六章的应用程序类似。除了你有一个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();      }    } }


                                                                         第七章 完

热点排行