开发者

Capture and log the response body [duplicate]

开发者 https://www.devze.com 2023-01-07 05:44 出处:网络
This question already has answers here: How to read and copy the HTTP servlet response output stream content for logging
This question already has answers here: How to read and copy the HTTP servlet response output stream content for logging (6 answers) 开发者_如何学Python Closed 6 years ago.

I have a servlet that handle certain HTTP requests and responses. I want to log the response body before sending back to the client. Is there any way that I can capture the response body before it is send as a HttpServletResponse object from the servlet?


If I understand you correctly, you want to log the response body? That's a pretty expensive task, but if that's the business requirement...

As @duffymo pointed, a Filter is a suitable place for this. You can capture the response body by replacing the passed-in ServletResponse with a HttpServletResponseWrapper implementation which replaces the HttpServletResponse#getWriter() with an own implementation which copies the response body into some buffer. After continuing the filter chain with the replaced response, just log the copy.

Here's a kickoff example how the doFilter() method can look like:

public void doFilter(ServletRequest request, final ServletResponse response, FilterChain chain) throws IOException, ServletException {
    final CopyPrintWriter writer = new CopyPrintWriter(response.getWriter());
    chain.doFilter(request, new HttpServletResponseWrapper((HttpServletResponse) response) {
        @Override public PrintWriter getWriter() {
            return writer;
        }
    });
    logger.log(writer.getCopy());
}

Here's how the CopyPrintWriter can look like:

public class CopyPrintWriter extends PrintWriter {

    private StringBuilder copy = new StringBuilder();

    public CopyPrintWriter(Writer writer) {
        super(writer);
    }

    @Override
    public void write(int c) {
        copy.append((char) c); // It is actually a char, not an int.
        super.write(c);
    }

    @Override
    public void write(char[] chars, int offset, int length) {
        copy.append(chars, offset, length);
        super.write(chars, offset, length);
    }

    @Override
    public void write(String string, int offset, int length) {
        copy.append(string, offset, length);
        super.write(string, offset, length);
    }

    public String getCopy() {
        return copy.toString();
    }

}

Map this filter on an url-pattern for which you'd like to log responses for. Keep in mind that binary/static content like images, CSS, JS files and so on won't be logged this way. You'd like to exclude them by using a specific enough url-pattern, e.g. *.jsp or just on the servlet-name of the servlet in question. If you want to log binary/static content anyway (for which I don't see any benefit), then you need to replace the HttpServletResponse#getOutputStream() the same way as well.


An alternative to BalusC answer Using the TeeOutputStream to write into two outputstreams at time.

public void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    final PrintStream ps = new PrintStream(baos);

    chain.doFilter(req,new HttpServletResponseWrapper(res) {
         @Override
         public ServletOutputStream getOutputStream() throws IOException {
            return new DelegatingServletOutputStream(new TeeOutputStream(super.getOutputStream(), ps)
            );
         }
         @Override
         public  PrintWriter getWriter() throws IOException {
            return new PrintWriter(new DelegatingServletOutputStream (new TeeOutputStream(super.getOutputStream(), ps))
            );
         }
      });

    //Get Response body calling baos.toString();
}


Maybe a servlet filter can help you. Think of it as aspect-oriented programming for HTTP.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号