tomcat(7)日志记录器

【0】README
0.1)本文部分文字描述转自:“深入剖析tomcat”,旨在学习 “tomcat的日志记录器” 的基础知识;
0.2)intro to 日志记录器:日志记录器是用来记录消息的组件;
0.3)for complete source code, please visit https://github.com/pacosonTang/HowTomcatWorks/tree/master/chapter7
0.4) 温馨建议:建议阅读本文之前,已阅读过 tomcat(1~6)的系列文章,因为它们是环环相扣的;

【1】Logger接口(日志记录器都必须实现 org.apache.catalina.Logger接口)
1)Logger接口提供了一些log() 方法来写日志;
2)Logger的某些log() 方法接受一个日志级别,定义了5种日志级别:FATAL, ERROR, WARNING, INFORMATION , DEBUG;
3)获取和设置日志级别:getVerbosity() and setVerbosity();
4)将日志记录器和某servlet容器关联: getContainer() and setContainer();
5)添加或移除PropertyChangeListener:addPropertyChangeListener() and removePropertyChangeListener();

【2】Tomcat的日志记录器
1)Tomcat提供了3种日志记录器,分别是:FileLogger, SystemErrorLogger, SystemOutLogger;类图如下:(干货——Tomcat提供了3种日志记录器

【2.1】LoggerBase类(以tomcat4的 LoggerBase为例)
1)LoggerBase类:实现了Logger接口中的除log(String name)外的所有方法;
2)LoggerBase类的日志等级设置:日志等级使用一个名为 verbosity来设置,默认值为 ERROR;
protected int verbosity = ERROR; // LoggerBase.java
3)接收日志等级的两个log重载方法:只有当传入的日志等级比当前实例中verbosity变量指定的等级低时,才会调用重载方法log(String msg)来记录日志;
public void log(String message, int verbosity) {  // LoggerBase.java
        if (this.verbosity >= verbosity)
            log(message);
    }
    public void log(String message, Throwable throwable, int verbosity) {  // LoggerBase.java
        if (this.verbosity >= verbosity)
            log(message, throwable);
    }
【2.2】SystemOutLogger类
1)该类的特点:接收到的每条日志消息都会传递给 System.out.println();
【2.3】SystemErrLogger类
1)该类的特点:接收到的每条日志消息都会传递给 System.err.println();

【2.4】FileLogger类
1)intro: 它将servlet容器中接收到的日志消息写到一个文件,并且可以选择是否要为每条消息添加时间戳;
2)LoggerBase.start() 方法 和 LoggerBase.stop()方法:Tomcat4中,LoggerBase() 类的start方法和stop方法只负责启动和关闭文件日志记录器时触发触发生命周期事件,并不做其他事情,源代码如下:
3)FileLogger.log()方法(最重要的方法):该方法会将接收到的日志消息写到一个日志文件中,(当日期发生变化时,log方法会关闭当前日志文件,并打开一个新文件),下面看一下open方法,close方法和log方法的具体实现:
3.1)open()方法:
step1)检查要创建的日志文件所在的目录是否存在,不存在,就创建该目录。目录位置是存储在类变量 directory中的;
step2)根据目录路径,创建位于指定位置的日志文件,并添加相应的前缀,当前日志和后缀;
step3)创建 java.io.PrintWriter实例,具体执行写路径名操作的是 java.io.FileWriter对象的实例;
step4)将PrintWriter实例赋值给 writer变量;
private void open() { // FileLogger.open() 方法
        // Create the directory if necessary
        File dir = new File(directory);
        if (!dir.isAbsolute()) // step1
            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; // step2
            writer = new PrintWriter(new FileWriter(pathname, true), true); //step3,4
        } catch (IOException e) {
            writer = null;
        }
    }
3.2)close()方法:
step1)intro:close方法负责确保将 PrintWriter 实例中所有的日志消息都写到文件中,关闭 PrintWriter 实例,将其引用设置为null,并将日期字符串清空;
private void close() { // FileLogger.close() 方法
        if (writer == null)
            return;
        writer.flush();
        writer.close();
        writer = null;
        date = "";
    }
3.3)log()方法:
step1)该方法会先创建 java.sql.Timestamp类 的一个实例;(使用 Timestamp类的toString() 方法可以获取当前日期的字符串表示形式,toString() 方法返回的日期格式为 yyyy-mm-dd hh:mm: ss.fffffffff, fffffffff 表示从00:00:00 起经过的纳秒数,若是只想要获取日期和小时,可以调用 subString方法,String substr = ts.toString().substring(0,19))
step2)获取日期部分;
String tsDate = tsString.substring(0,10);
step3)log方法将tsDate与 字符串形式的变量date 相比较。若不等,则log方法会关闭当前日志文件,并打开一个新日志文件;
step4)log方法将日志信息写到 PrintWriter实例中,而PrintWriter实例将输出流写入到日志文件中。(是否加上时间戳,依据布尔变量 timestamp)
public void log(String msg) { FileLogger.log() 方法
        // Construct the timestamp we will use, if requested
        Timestamp ts = new Timestamp(System.currentTimeMillis()); // step1
        String tsString = ts.toString().substring(0, 19); // step1
        String tsDate = tsString.substring(0, 10); // step2

        // If the date has changed, switch log files
        if (!date.equals(tsDate)) { // step3
            synchronized (this) {
                if (!date.equals(tsDate)) {
                    close();
                    date = tsDate;
                    open(); // this highlight line.
                }
            }
        }

        // Log this message, timestamped if necessary
        if (writer != null) { // step4
            if (timestamp) {
                writer.println(tsString + " " + msg);
            } else {
                writer.println(msg);
            }
        }
    }

【3】应用程序
1)本文的应用程序同章节6的应用程序相似,区别在于, SimpleContext 对象关联了一个 FileLogger 组件。
public final class Bootstrap {
  public static void main(String[] args) {
    Connector connector = new HttpConnector();
    Wrapper wrapper1 = new SimpleWrapper(); // 创建Wrapper容器
    wrapper1.setName("Primitive"); // 填充容器
    wrapper1.setServletClass("servlet.PrimitiveServlet");
    Wrapper wrapper2 = new SimpleWrapper();
    wrapper2.setName("Modern");
    wrapper2.setServletClass("servlet.ModernServlet");
    Loader loader = new SimpleLoader(); // 创建类加载器

    Context context = new SimpleContext(); // 创建Context容器,可以包含多个Wrapper容器
    context.addChild(wrapper1);
    context.addChild(wrapper2);

    Mapper mapper = new SimpleContextMapper(); // 创建映射器,提供请求资源的 URI到 请求资源(servlet)名称的映射
    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 -------- highlight
    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);

    //--------------------------- highlight over.

    connector.setContainer(context);
    try {
      connector.initialize(); // 获得服务器套接字
      ((Lifecycle) connector).start(); // 接收client发出的 http请求,并做处理。
      ((Lifecycle) context).start(); // 触发生命周期事件

      // make the application wait until we press a key.
      System.in.read();
      ((Lifecycle) context).stop();
    }
    catch (Exception e) {
      e.printStackTrace();
    }  }}

Attention)照惯例,本文总结了上述测试用例的调用过程示例图
A1)写在前面:在SimpleContext的start() 方法中调用了 log方法;
A2)log方法的调用steps 如下图所示:


2)运行参数
E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\webroot com.tomcat.chapter7.startup.B
ootstrap
SimpleContextLifecycleListener's event before_start
Starting SimpleLoader
Starting Wrapper Primitive
Starting Wrapper Modern
SimpleContextLifecycleListener's event start
Starting context.
SimpleContextLifecycleListener's event after_start
ModernServlet -- init

3)运行结果
3.1)生成的日志文件为(日志路径在 SystemgetProperty("user.dir") + “/webroot”):
2016-04-17 17:16:58 HttpConnector Opening server socket on all host IP addresses
2016-04-17 17:16:58 HttpConnector[8080] Starting background thread
2016-04-17 17:16:58 HttpProcessor[8080][0] Starting background thread
2016-04-17 17:16:58 HttpProcessor[8080][1] Starting background thread
2016-04-17 17:16:58 HttpProcessor[8080][2] Starting background thread
2016-04-17 17:16:58 HttpProcessor[8080][3] Starting background thread
2016-04-17 17:16:58 HttpProcessor[8080][4] Starting background thread
2016-04-17 17:16:58 starting Context
2016-04-17 17:16:58 Context started
3.2)访问记录

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值