java - Tomcat:@Overriden 日志过滤器混合输出及其非覆盖变体

标签 java tomcat logging tomcat7 servlet-filters

FullRequestDumperFilter 是作为 RequestDumperFilter 的 Java 类扩展创建的 - part of Tomcat .

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

logging.properties中定义了2个日志文件,期望的输出:

  • request-dumper.log - header (RequestDumperFilter)
  • custom-dumper.log - 标题和主体 (FullRequestDumperFilter)

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

  • request-dumper.log - header 写入两次 (RequestDumperFilter)
  • custom-dumper.log - 只有主体(FullRequestDumperFilter@Override 的内容)<

这可能是 FullRequestDumperFilter 造成的,它继承自 RequestDumperFilter,它被配置为输出到 request-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("       requestBody\n", bufferedReqest.getRequestBody());
            chain.doFilter(bufferedReqest, bufferedResponse);
            doLog("      responseBody\n", 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

关于java - Tomcat:@Overriden 日志过滤器混合输出及其非覆盖变体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45102542/

相关文章:

java - 如何使用 JTS 中的 Delaunay 三角剖分为点插值 Z 值的示例

tomcat - 我无法在 JSF 2.2 的方法上传递参数

Tomcat 6 不复制我的 context.xml

java - 如何将 Web 应用程序中的集成小程序部署到 Tomcat 中

c# - 写入xml格式的日志文件

javascript - 在 Java 中使用 EXSLT 验证 XML?

Java:从图像中裁剪出具有特定颜色的区域

java - 使用 USB 电缆打印问题

python - Python 的日志记录格式字符串是如何工作的?

windows - 适用于 Windows 的开源 Syslog 守护程序