2、运行日志,记录tomcat 运行、异常、错误信息。
Tomcat 的日志记录常会被 log4j 或 slf4j 取代,不过这里不讨论另外日志组件,很纯粹地说一下tomcat 原生的访问日志。关于运行日志的分析,有机会再另写一篇。对于访问日志,tomcat 定义了以下接口:
public interface AccessLog {
// 记录访问日志
public void log(Request request, Response response, long time);
// ip
public static final String REMOTE_ADDR_ATTRIBUTE =
"org.apache.catalina.AccessLog.RemoteAddr";
// 主机名
public static final String REMOTE_HOST_ATTRIBUTE =
"org.apache.catalina.AccessLog.RemoteHost";
// 访问协议
public static final String PROTOCOL_ATTRIBUTE =
"org.apache.catalina.AccessLog.Protocol";
// 端口号
public static final String SERVER_PORT_ATTRIBUTE =
"org.apache.catalina.AccessLog.ServerPort";
// 设置是否记录ip,主机名,协议,端口号
public void setRequestAttributesEnabled(boolean requestAttributesEnabled);
public boolean getRequestAttributesEnabled();
}
一个默认的实现是 AccessLogValue(在 server.xml 配置的)。先看一下,如何配置和使用 AccessLogValue,在 $tomcat_home%/conf/server.xml 里,有一下代码:
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
参数的含义如下:
className:访问日志的实现类(implements AccessLog)
directory: 日志的位置
prefix:日志名称的前缀
suffix:日志名称的后缀
pattern:日志模式的参数,(模式参数的设置可以参考附录)
更多参数的设置可以查看 AccessLogValue 的参数。
对于 pattern ,tomcat 提供了两种便捷的 pattern 简写:common:%h %l %u %t "%r" %s %b;combined - %h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"
因为上述配置的方式,所以我们常看到日志记录文件如下(在 $tomcat_home$/logs/),下面日期的产生,是代码产生的:
对于其他基础的字段设置的配置与源码编写,理解起来应该不大(类似平常地解释 xml 文件),下面重点讲一下的是,如果根据 patten 来写日志(建议先阅读以下附录):
pattern 写法有两种 %XXX 或 %{XXX}XX,使用代码分析分析 pattern,再根据 pattern 获取对应的信息,将信息写到一个 StringBuilder 即可。对 pattern 的分析如下:对于各种配置的参数a,A等,都应该属于一种 XXXElenment,另外对于空格或其他字符,增加一个 StringElement,那在分析 pattern 时,每遇到一个特殊的字符,就创建一个指定的 element,反之,创建一个 StringElement,对pattern 的分析如下:
List<AccessLogElement> list = new ArrayList<AccessLogElement>();
boolean replace = false;
StringBuilder buf = new StringBuilder();
for (int i = 0; i < pattern.length(); i++) {
char ch = pattern.charAt(i);
if (replace) {
/*
* 用来处理 '{',如果在之后没有遇上 '}',将这个 '{'忽略,不处理。
* 处理一下三种情况:
* %{xxx}i 头字段信息
* %{xxx}c cookie 信息
* %{xxx}r ServletRequest 的某个 attribute
* %{xxx}s HttpSession 的某个 attribut
*/
if ('{' == ch) {
StringBuilder name = new StringBuilder();
int j = i + 1;
for (; j < pattern.length() && '}' != pattern.charAt(j); j++) {
name.append(pattern.charAt(j));
}
if (j + 1 < pattern.length()) {
// j+1,跳过字符 '}x'
j++;
list.add(createAccessLogElement(name.toString(),
pattern.charAt(j)));
i = j; // 跳过 %{xxx}x
} else {
// 单个字符,如 a,直接创建对应的 Element
list.add(createAccessLogElement(ch));
}
} else {
list.add(createAccessLogElement(ch));
}
replace = false;
} else if (ch == '%') {
replace = true;
list.add(new StringElement(buf.toString()));
buf = new StringBuilder();
} else {
buf.append(ch);
}
}
if (buf.length() > 0) {
list.add(new StringElement(buf.toString()));
}
通过上面的分析,我们就可以根据 pattern 得到需要的信息(存储在 list 里),对于各种 element 的创建如: