9.3示例1:日志Filter

9.3示例1:日志Filter

实例简介

作为第1个例子,将做一个简单的Filter

  • app09a的应用中把Request请求的URL记录到日志文本文件中
  • 日志文本文件名通过Filter的初始化参数来配置。
    • 此外,日志的每条记录都会有一个前缀,该前缀也由Filter初始化参数来定义。

通过日志文件,可以获得许多有用的信息,例如在应用中哪些资源访问最频繁;Web站点在一天中的哪个时间段访问量最多。

这个Filter的类名叫LoggingFilter,一般情况下,Filter的类名都以*Filter结尾。

LoggingFilter类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package filter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.util.Date;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;
@WebFilter(
filterName = "LoggingFilter",
urlPatterns ={"/*"},
initParams = {
@WebInitParam(
name = "logFileName",
value = "log.txt"
),
@WebInitParam(
name = "prefix",
value = "URI: "
)
}
)
public class LoggingFilter
implements
Filter
{
private PrintWriter logger;
private String prefix;
static DateFormat format=
DateFormat.getDateTimeInstance(
DateFormat.LONG,DateFormat.LONG);
@Override
public void init(FilterConfig filterConfig)
throws ServletException
{
//获取初始化参数
prefix = filterConfig.getInitParameter("prefix");
//获取初始化参数
String logFileName =
filterConfig.getInitParameter("logFileName");
String appPath =
filterConfig.getServletContext().getRealPath("/");
// without path info in logFileName, the log file will be
// created in $TOMCAT_HOME/bin
System.out.println(
"LoggingFilter的init()方法被执行"
+ "--->logFileName:"
+ logFileName);
try {
File file=new File(appPath, logFileName);
logger = new PrintWriter(new File(appPath,
logFileName));
System.out.println("日志文件绝对路径:"+
file.getAbsolutePath());
} catch (FileNotFoundException e) {
e.printStackTrace();
throw new ServletException(e.getMessage());
}
}
@Override
public void destroy()
{
System.out.println(
"LoggingFilter的destroy()方法被执行");
if(logger != null) {
logger.close();
}
}
@Override
public void doFilter(ServletRequest request,
ServletResponse response,FilterChain filterChain)
throws IOException,ServletException
{
System.out.println(
"LoggingFilter的doFilter()方法被执行");
HttpServletRequest httpServletRequest =
(HttpServletRequest) request;
logger.println(
format.format(new Date()) + " "
+prefix + httpServletRequest.getRequestURI());
logger.flush();
filterChain.doFilter(request, response);
}
}

下面来仔细分析一下该Filter类。

成员变量

首先,该Filter的类实现了Filter的接口并声明两个成员变量:PrintWriter类型的loggerString类型的prefix

1
2
private PrintWriter logger;
private String prefix:

其中PrintWriter用于记录日志到文本文件,prefix的字符串用于每条日志的前缀。

WebFilter注解

Filter的类使用了@WebFilterAnnotation,将两个参数(logFilteNameprefix)传入到该Filter中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@WebFilter(
filterName = "LoggingFilter",
urlPatterns ={"/ *"},
initParams = {
@WebInitParam(
name = "logFileName",
value = "log.txt"
),
@WebInitParam(
name = "prefix",
value = "URI: "
)
}
)

init方法

获取初始化参数

Filterinit方法中,通过形式参数FilterConfig对象的getInitParameter方法来获取prefixgetFileName的初始化参数。其中把prefix参数中赋给了类变量prefixlogFileName则用于创建一个PrintWriter

1
2
3
prefix = filterConfig.getInitParameter("prefix");
String logFileName = filterConfig
.getInitParameter("logFileName");

工作目录

如果Servlet/JSP应用是通过Servlet/JSP容器启动的,那么当前应用的工作目录是当前JDK所在的目录。
如果是在Tomcat中,该目录是Tomcat的安装目录。
在应用中创建日志文件,可以通过ServletContext.getRealPath来获取工作目录,结合应用工作目录以及初始化参数中的logFilterNmae,就可以得到日志文件的绝对路径:

1
2
3
4
5
6
7
8
9
10
11
String appPath = 
filterConfig.getServletContext().getRealPath("/");
// without path info in logFileName, the log file will be
// created in $TOMCAT_HOME/bin
try {
logger = new PrintWriter(new File(appPath,
logFileName));
} catch (FileNotFoundException e) {
e.printStackTrace();
throw new ServletException(e.getMessage());
}

Filterinit方法被执行时,日志文件就会创建出来。如果在应用的工作目录中该文件已经存在,那么该日志文件的内容将会被覆盖。

destroy方法

当应用关闭时,PrintWriter需要被关闭。因此在Filterdestroy方法中,需要:

1
2
3
if (logger != null) {
logger.close();
}

doFilter方法

FilterdoFilter实现中记录着所有从ServletRequestHttpServletRequestRequest,并调用了它的getRequestURI方法,该方法的返回值将记录通过PrintWriterprintln方法记录下来:

1
2
3
4
HttpServletRequest httpServletRequest = 
(HttpServletRequest) request;
logger.println(new Date() + " " + prefix
+ httpServletRequest.getRequestURI());

每条记录都有一个时间戳以及前缀,这样可以很方便地标识每条记录。接下来 FilterdoFilter实现调用PrintWriterflush方法以及FilterChain.doFilter,以唤起资源的调用:

1
2
logger.flush();
filterChain.doFilter(request, response);

如果使用TomcatFilter的初始化并不会等到第一个Request请求时才触发进行。这点可以在控制台中打印出来的logFileName参数值中可以看到。

test.jsp

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>测试</title>
</head>
<body>
<h2>测试</h2>
</body>
</html>

运行效果

启动项目

启动app09a项目,可以看到控制台部分输出如下图所示:
这里有一张图片
从控制台的输出我们可以知道:

  • 日志文件的绝对路径为:E:\apache-tomcat-8.5.35\webapps\app09a\log.txt.
  • 在项目启动阶段Filter初始化就开始了。

访问JSP页面

然后通过URL调用test.jsp页面
http://localhost:8080/app09a/test.jsp
此时,容器会调用doFilter方法,该方法控制台输出如下:
这里有一张图片

查看日志文件

这时候可以打开日志文件:E:\apache-tomcat-8.5.35\webapps\app09a\log.txt查看日志输出,如下图所示:
这里有一张图片

doFilter的执行次数和JSP页面的访问次数的关系

在该过滤器中,我们设置的过滤规则为:

1
urlPatterns ={"/*"}

这表示过滤所有的JSP页面,所以每访问一次JSP页面,过滤器就会执行一次.
再刷新两次test.jsp页面,则doFilter方法也会再执行两次,日志文件中也会多出两条记录,如下图所示:
这里有一张图片
这里有一张图片