在第一小节中经过几步一个访问日志组件已成型,但为了增加用户自定义能力我们还是要继续做点事,对于用户自定义的实现最经典的做法就是引入变量表示,例如定义%a表示远程主机IP、%A表示本机IP等等,然后在写入之前用相应逻辑把变量替换成相应的值写入日志。这节我们来实现日志格式的自定义支持。
整个过程其实是先自定义变量组,再逐个把变量替换成相应值,最后把替换后的值写入文件。由于需要实现很多不同的变量,所以定义一个接口用于约束所有变量添加操作的定义,定义一个addElement方法,通过从request和response获取相应的变量值后添加到字符串buf中。
public interface AccessLogElement {
public void addElement(StringBuilder buf, Request request, Response response);
}
接着定义两个元素分别用于添加响应状态码和远程地址,使用时直接调用他们的addElement即可把状态码和远程地址添加到字符串中,
public class StatusCodelElement implements AccessLogElement {
public void addElement(StringBuilder buf, Request request, Response response) {
buf.append(response.getStatus());
}
}
public class RemoteAddrElement implements AccessLogElement {
public void addElement(StringBuilder buf, Request request, Response response) {
buf.append(request.getRemoteAddr());
}
}
现在还差一个映射器用于解析变量到各自AccessLogElement的映射,如下ElementMapping提供一个map方法把自定义的pattern解析成对应的访问日志元素并发对应的值替换原来的变量。
public class ElementMapping {
Response response;
Request reqeust;
public ElementMapping(Request request, Response response){
this.reqeust=request;
this.response=response;
}
public StringBuilder map(String pattern) {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < pattern.length(); i++) {
char ch = pattern.charAt(i);
if (ch == '%') {
ch = pattern.charAt(++i);
addElement(ch, buf);
} else {
buf.append(ch);
}
}
return buf;
}
private void addElement(char ch, StringBuilder buf) {
switch (ch) {
case 'a':
new RemoteAddrElement().addElement(buf, request, response);
break;
case 's':
new StatusCodeElement().addElement(buf, request, response);
break;
}
}
}
这节介绍的是如何引入变量使你的访问日志组件拥有自定义格式功能,并且使用了一个简单的案例说明,如果你想拥有更强大的自定义能力可以在本文基础上实现,例如可以把常用的变量组合简化为一个字符串表示,common字符串用于表示%h %l %u %t "%r" %s %b常用的变量组合,当然要实现这样的支持你必须在映射器中做对应的处理。