Tomcat:@Overriden日志滤波器混合输出及其未覆盖的变体



FullRequestDumperFilter是作为RequestDumperFilter的Java类扩展创建的 - Tomcat的一部分。

FullRequestDumperFilter是我的自定义 Tomcat 日志过滤器,用于记录完整的 HTTP 请求和响应(包括正文),而RequestDumperFilter仅记录消息标头。

在定义了 2 个日志文件logging.properties,所需的输出:

  • request-dumper.log- 标头 (RequestDumperFilter)
  • custom-dumper.log- 标题和正文 (FullRequestDumperFilter)

问题:部分输出被写入错误的日志文件,实际输出:

  • request-dumper.log-标头写入两次 (RequestDumperFilter)
  • custom-dumper.log-仅正文(FullRequestDumperFilter@Override的内容)

这可能是由FullRequestDumperFilter引起的,它继承自RequestDumperFilter,它被配置为输出到request-dumper.log而不是custom-dumper.log中。

如何让日志过滤器输出到正确的日志文件中?

${CATALINA_HOME}/logging.properties

handlers = 1catalina.org.apache.juli.FileHandler, 2localhost.org.apache.juli.FileHandler, 3manager.org.apache.juli.FileHandler, 4host-manager.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler, 1request-dumper.org.apache.juli.FileHandler, 1custom-dumper.org.apache.juli.FileHandler
...
1request-dumper.org.apache.juli.FileHandler.level = FINEST
1request-dumper.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
1request-dumper.org.apache.juli.FileHandler.prefix = request-dumper.
1request-dumper.org.apache.juli.FileHandler.formatter = org.apache.juli.VerbatimFormatter
org.apache.catalina.filters.RequestDumperFilter.level = FINEST
org.apache.catalina.filters.RequestDumperFilter.handlers = 
1request-dumper.org.apache.juli.FileHandler
1custom-dumper.org.apache.juli.FileHandler.level = FINEST
1custom-dumper.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
1custom-dumper.org.apache.juli.FileHandler.prefix = custom-dumper.
1custom-dumper.org.apache.juli.FileHandler.formatter = org.apache.juli.VerbatimFormatter
com.example.FullRequestDumperFilter.level = FINEST
com.example.FullRequestDumperFilter.handlers = 
1custom-dumper.org.apache.juli.FileHandler

FullRequestDumperFilter.java

package com.example;
import org.apache.catalina.filters.RequestDumperFilter;
import org.apache.commons.io.output.TeeOutputStream;
import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
public class FullRequestDumperFilter extends RequestDumperFilter {
/**
* The logger for this class.
*/
private static final Log log = LogFactory.getLog(FullRequestDumperFilter.class);
/**
* Log the interesting request parameters, invoke the next Filter in the
* sequence, and log the interesting response parameters.
*
* @param request  The servlet request to be processed
* @param response The servlet response to be created
* @param chain    The filter chain being processed
* @throws IOException      if an input/output error occurs
* @throws ServletException if a servlet error occurs
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
super.doFilter(request, response, chain);
try {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
BufferedRequestWrapper bufferedReqest = new BufferedRequestWrapper(httpServletRequest);
BufferedResponseWrapper bufferedResponse = new BufferedResponseWrapper(httpServletResponse);
doLog("       requestBodyn", bufferedReqest.getRequestBody());
chain.doFilter(bufferedReqest, bufferedResponse);
doLog("      responseBodyn", bufferedResponse.getContent());
} catch (Throwable a) {
log.error(a);
}
}
private void doLog(String attribute, String value) {
StringBuilder sb = new StringBuilder(80);
sb.append(Thread.currentThread().getName());
sb.append(' ');
sb.append(attribute);
sb.append('=');
sb.append(value);
log.info(sb.toString());
}
private static final class BufferedRequestWrapper extends HttpServletRequestWrapper {
private ByteArrayInputStream bais = null;
private ByteArrayOutputStream baos = null;
private BufferedServletInputStream bsis = null;
private byte[] buffer = null;

public BufferedRequestWrapper(HttpServletRequest req) throws IOException {
super(req);
// Read InputStream and store its content in a buffer.
InputStream is = req.getInputStream();
this.baos = new ByteArrayOutputStream();
byte buf[] = new byte[1024];
int letti;
while ((letti = is.read(buf)) > 0) {
this.baos.write(buf, 0, letti);
}
this.buffer = this.baos.toByteArray();
}

@Override
public ServletInputStream getInputStream() {
this.bais = new ByteArrayInputStream(this.buffer);
this.bsis = new BufferedServletInputStream(this.bais);
return this.bsis;
}

String getRequestBody() throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(this.getInputStream()));
String line = null;
StringBuilder inputBuffer = new StringBuilder();
do {
line = reader.readLine();
if (null != line) {
inputBuffer.append(line.trim());
}
} while (line != null);
reader.close();
return inputBuffer.toString().trim();
}
}

private static final class BufferedServletInputStream extends ServletInputStream {
private ByteArrayInputStream bais;
public BufferedServletInputStream(ByteArrayInputStream bais) {
this.bais = bais;
}
@Override
public int available() {
return this.bais.available();
}
@Override
public int read() {
return this.bais.read();
}
@Override
public int read(byte[] buf, int off, int len) {
return this.bais.read(buf, off, len);
}

}
public class TeeServletOutputStream extends ServletOutputStream {
private final TeeOutputStream targetStream;
public TeeServletOutputStream(OutputStream one, OutputStream two) {
targetStream = new TeeOutputStream(one, two);
}
@Override
public void write(int arg0) throws IOException {
this.targetStream.write(arg0);
}
public void flush() throws IOException {
super.flush();
this.targetStream.flush();
}
public void close() throws IOException {
super.close();
this.targetStream.close();
}
}

public class BufferedResponseWrapper implements HttpServletResponse {
HttpServletResponse original;
TeeServletOutputStream tee;
ByteArrayOutputStream bos;
public BufferedResponseWrapper(HttpServletResponse response) {
original = response;
}
public String getContent() {
return bos.toString();
}
public PrintWriter getWriter() throws IOException {
return original.getWriter();
}
public ServletOutputStream getOutputStream() throws IOException {
if (tee == null) {
bos = new ByteArrayOutputStream();
tee = new TeeServletOutputStream(original.getOutputStream(), bos);
}
return tee;
}
@Override
public String getCharacterEncoding() {
return original.getCharacterEncoding();
}
@Override
public String getContentType() {
return original.getContentType();
}
@Override
public void setCharacterEncoding(String charset) {
original.setCharacterEncoding(charset);
}
@Override
public void setContentLength(int len) {
original.setContentLength(len);
}
@Override
public void setContentType(String type) {
original.setContentType(type);
}
@Override
public void setBufferSize(int size) {
original.setBufferSize(size);
}
@Override
public int getBufferSize() {
return original.getBufferSize();
}
@Override
public void flushBuffer() throws IOException {
tee.flush();
}
@Override
public void resetBuffer() {
original.resetBuffer();
}
@Override
public boolean isCommitted() {
return original.isCommitted();
}
@Override
public void reset() {
original.reset();
}
@Override
public void setLocale(Locale loc) {
original.setLocale(loc);
}
@Override
public Locale getLocale() {
return original.getLocale();
}
@Override
public void addCookie(Cookie cookie) {
original.addCookie(cookie);
}
@Override
public boolean containsHeader(String name) {
return original.containsHeader(name);
}
@Override
public String encodeURL(String url) {
return original.encodeURL(url);
}
@Override
public String encodeRedirectURL(String url) {
return original.encodeRedirectURL(url);
}
@SuppressWarnings("deprecation")
@Override
public String encodeUrl(String url) {
return original.encodeUrl(url);
}
@SuppressWarnings("deprecation")
@Override
public String encodeRedirectUrl(String url) {
return original.encodeRedirectUrl(url);
}
@Override
public void sendError(int sc, String msg) throws IOException {
original.sendError(sc, msg);
}
@Override
public void sendError(int sc) throws IOException {
original.sendError(sc);
}
@Override
public void sendRedirect(String location) throws IOException {
original.sendRedirect(location);
}
@Override
public void setDateHeader(String name, long date) {
original.setDateHeader(name, date);
}
@Override
public void addDateHeader(String name, long date) {
original.addDateHeader(name, date);
}
@Override
public void setHeader(String name, String value) {
original.setHeader(name, value);
}
@Override
public void addHeader(String name, String value) {
original.addHeader(name, value);
}
@Override
public void setIntHeader(String name, int value) {
original.setIntHeader(name, value);
}
@Override
public void addIntHeader(String name, int value) {
original.addIntHeader(name, value);
}
@Override
public void setStatus(int sc) {
original.setStatus(sc);
}
@SuppressWarnings("deprecation")
@Override
public void setStatus(int sc, String sm) {
original.setStatus(sc, sm);
}
@Override
public int getStatus() {
return original.getStatus();
}
@Override
public String getHeader(String s) {
return original.getHeader(s);
}
@Override
public Collection<String> getHeaders(String s) {
return original.getHeaders(s);
}
@Override
public Collection<String> getHeaderNames() {
return original.getHeaderNames();
}
}
}

这是一个可接受的解决方案,我删除了logging.properties中的一个过滤器,并将两个过滤器的处理程序设置为1custom-dumper.org.apache.juli.FileHandler

但是,该解决方案仍然存在缺点:

  • 只能有一个日志文件
  • 处理程序属性必须为技术上相同的筛选器配置两次
  • 理想情况下,会有日志级别(例如INFO,FINEST),这将允许自定义过滤器的不同输出存储在不同的日志文件中(有或没有正文)

${CATALINA_HOME}/logging.properties

1custom-dumper.org.apache.juli.FileHandler.level = FINEST
1custom-dumper.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
1custom-dumper.org.apache.juli.FileHandler.prefix = custom-dumper.
1custom-dumper.org.apache.juli.FileHandler.formatter = org.apache.juli.VerbatimFormatter
com.example.FullRequestDumperFilter.level = FINEST
com.example.FullRequestDumperFilter.handlers = 
1custom-dumper.org.apache.juli.FileHandler
org.apache.catalina.filters.RequestDumperFilter.level = FINEST
org.apache.catalina.filters.RequestDumperFilter.handlers = 
1custom-dumper.org.apache.juli.FileHandler

最新更新