Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/jquery-ui/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何读取和复制HTTP servlet响应输出流内容以进行日志记录_Java_Servlets_Logging_Servlet Filters - Fatal编程技术网

Java 如何读取和复制HTTP servlet响应输出流内容以进行日志记录

Java 如何读取和复制HTTP servlet响应输出流内容以进行日志记录,java,servlets,logging,servlet-filters,Java,Servlets,Logging,Servlet Filters,我在java Web服务器(实际上是appengine)中创建了一个过滤器,用于记录传入请求的参数。我还想记录我的Web服务器编写的结果响应。虽然我可以访问响应对象,但我不确定如何从中获得实际的字符串/内容响应 有什么想法吗?我对appengine不太熟悉,但你需要Tomcat中的一些东西。其属性模式;一种格式化布局,用于标识要记录的请求和响应中的各种信息字段,或标识“公用”或“组合”一词以选择标准格式 看起来appengine已经为其内置了功能 您需要创建一个自定义实现来包装ServletRe

我在java Web服务器(实际上是appengine)中创建了一个过滤器,用于记录传入请求的参数。我还想记录我的Web服务器编写的结果响应。虽然我可以访问响应对象,但我不确定如何从中获得实际的字符串/内容响应


有什么想法吗?

我对appengine不太熟悉,但你需要Tomcat中的一些东西。其属性模式;一种格式化布局,用于标识要记录的请求和响应中的各种信息字段,或标识“公用”或“组合”一词以选择标准格式

看起来appengine已经为其内置了功能

您需要创建一个自定义实现来包装
ServletResponse
参数,其中重写
getOutputStream()
getWriter()
,以返回一个自定义实现,在该实现中复制基抽象方法中的写入字节。然后,将包装好的自定义
HttpServletResponseWrapper
传递给
FilterChain#doFilter()
调用,最后应该能够在调用后获得复制的响应

换句话说,
过滤器

@WebFilter("/*")
public class ResponseLogger implements Filter {

    @Override
    public void init(FilterConfig config) throws ServletException {
        // NOOP.
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        if (response.getCharacterEncoding() == null) {
            response.setCharacterEncoding("UTF-8"); // Or whatever default. UTF-8 is good for World Domination.
        }

        HttpServletResponseCopier responseCopier = new HttpServletResponseCopier((HttpServletResponse) response);

        try {
            chain.doFilter(request, responseCopier);
            responseCopier.flushBuffer();
        } finally {
            byte[] copy = responseCopier.getCopy();
            System.out.println(new String(copy, response.getCharacterEncoding())); // Do your logging job here. This is just a basic example.
        }
    }

    @Override
    public void destroy() {
        // NOOP.
    }

}
public class HttpServletResponseCopier extends HttpServletResponseWrapper {

    private ServletOutputStream outputStream;
    private PrintWriter writer;
    private ServletOutputStreamCopier copier;

    public HttpServletResponseCopier(HttpServletResponse response) throws IOException {
        super(response);
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        if (writer != null) {
            throw new IllegalStateException("getWriter() has already been called on this response.");
        }

        if (outputStream == null) {
            outputStream = getResponse().getOutputStream();
            copier = new ServletOutputStreamCopier(outputStream);
        }

        return copier;
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        if (outputStream != null) {
            throw new IllegalStateException("getOutputStream() has already been called on this response.");
        }

        if (writer == null) {
            copier = new ServletOutputStreamCopier(getResponse().getOutputStream());
            writer = new PrintWriter(new OutputStreamWriter(copier, getResponse().getCharacterEncoding()), true);
        }

        return writer;
    }

    @Override
    public void flushBuffer() throws IOException {
        if (writer != null) {
            writer.flush();
        } else if (outputStream != null) {
            copier.flush();
        }
    }

    public byte[] getCopy() {
        if (copier != null) {
            return copier.getCopy();
        } else {
            return new byte[0];
        }
    }

}
自定义
HttpServletResponseWrapper

@WebFilter("/*")
public class ResponseLogger implements Filter {

    @Override
    public void init(FilterConfig config) throws ServletException {
        // NOOP.
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        if (response.getCharacterEncoding() == null) {
            response.setCharacterEncoding("UTF-8"); // Or whatever default. UTF-8 is good for World Domination.
        }

        HttpServletResponseCopier responseCopier = new HttpServletResponseCopier((HttpServletResponse) response);

        try {
            chain.doFilter(request, responseCopier);
            responseCopier.flushBuffer();
        } finally {
            byte[] copy = responseCopier.getCopy();
            System.out.println(new String(copy, response.getCharacterEncoding())); // Do your logging job here. This is just a basic example.
        }
    }

    @Override
    public void destroy() {
        // NOOP.
    }

}
public class HttpServletResponseCopier extends HttpServletResponseWrapper {

    private ServletOutputStream outputStream;
    private PrintWriter writer;
    private ServletOutputStreamCopier copier;

    public HttpServletResponseCopier(HttpServletResponse response) throws IOException {
        super(response);
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        if (writer != null) {
            throw new IllegalStateException("getWriter() has already been called on this response.");
        }

        if (outputStream == null) {
            outputStream = getResponse().getOutputStream();
            copier = new ServletOutputStreamCopier(outputStream);
        }

        return copier;
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        if (outputStream != null) {
            throw new IllegalStateException("getOutputStream() has already been called on this response.");
        }

        if (writer == null) {
            copier = new ServletOutputStreamCopier(getResponse().getOutputStream());
            writer = new PrintWriter(new OutputStreamWriter(copier, getResponse().getCharacterEncoding()), true);
        }

        return writer;
    }

    @Override
    public void flushBuffer() throws IOException {
        if (writer != null) {
            writer.flush();
        } else if (outputStream != null) {
            copier.flush();
        }
    }

    public byte[] getCopy() {
        if (copier != null) {
            return copier.getCopy();
        } else {
            return new byte[0];
        }
    }

}
自定义
ServletOutputStream

public class ServletOutputStreamCopier extends ServletOutputStream {

    private OutputStream outputStream;
    private ByteArrayOutputStream copy;

    public ServletOutputStreamCopier(OutputStream outputStream) {
        this.outputStream = outputStream;
        this.copy = new ByteArrayOutputStream(1024);
    }

    @Override
    public void write(int b) throws IOException {
        outputStream.write(b);
        copy.write(b);
    }

    public byte[] getCopy() {
        return copy.toByteArray();
    }

}
虽然在大多数情况下都可以工作,但您必须小心使用
flush
调用-它提交响应,并且不可能进行其他写入,例如通过以下过滤器。 在Websphere环境中,我们发现非常相似的方法存在一些问题,在这种环境中,传递的响应只是部分响应

根据这一点,flush根本不应该被调用,你应该让它在内部被调用

我通过使用
TeeWriter
(它将流拆分为2个流)和在“分支流”中使用非缓冲流来记录日志,解决了刷新问题。然后调用
flush
是不必要的

private HttpServletResponse wrapResponseForLogging(HttpServletResponse response, final Writer branchedWriter) {
    return new HttpServletResponseWrapper(response) {
        PrintWriter writer;

        @Override
        public synchronized PrintWriter getWriter() throws IOException {
            if (writer == null) {
                writer = new PrintWriter(new TeeWriter(super.getWriter(), branchedWriter));
            }
            return writer;
        }
    };
}
然后您可以这样使用它:

protected void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException {
    //...
    StringBuilderWriter branchedWriter = new org.apache.commons.io.output.StringBuilderWriter();
    try {
        chain.doFilter(request, wrapResponseForLogging(response, branchedWriter));
    } finally {
        log.trace("Response: " + branchedWriter);
    }
}

代码为brewity简化。

BalusC解决方案还可以,但有点过时。Spring现在有了它的功能。您只需使用
[contentcachingressonsewrapper]
,它具有方法
公共字节[]getContentAsByteArray()


我建议创建WrapperFactory,它将允许对其进行配置,无论是使用默认的ResponseWrapper还是ContentCachingResponseWrapper。

而不是创建自定义的HttpServletResponseWrapper。您可以使用ContentCachingResponseWrapper,因为它提供了方法getContentAsByteArray()


你如何写你的回复
response.getWriter().write(您的responseString)
???还是你在做一些不同的事情?您是否也想写错误?(换句话说,在执行
response.senderError(yourError)
??)时,是否要记录响应?这可能会给您一个hint@Dave只需使用response.getWriter().write(您的responseString)正如您所提到的,这是我想要捕获的旧输出。使用TeeOutputStream在同一时间写入两个OutputStream:“
HttpServletResponseCode
的构造函数名称不正确,我无法编辑它,因为编辑长度应该超过6个字符,并且我不想更改关于答案的任何其他内容。想知道为什么获取响应正文如此复杂。它应该类似于response.getContent()。背后一定有一些确凿的原因:)@ant:这占用了大量内存,通常对Web应用程序本身不感兴趣。@ant:只需设置一个请求属性。对于Spring,从4.1.3版开始,还有一个。如何“使用”它?从使用它的角度来看,似乎您用ContentCachingResponseRapper替换了HttpServletResponseCopier——对吗?上面BalusC的解决方案对我不起作用,但这个解决方案需要spring