Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capture and log the response body [duplicate]

Tags:

java

servlets

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?

like image 332
Jony Avatar asked Jul 13 '10 23:07

Jony


2 Answers

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.

like image 73
BalusC Avatar answered Oct 11 '22 13:10

BalusC


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(); } 
like image 25
pdorgambide Avatar answered Oct 11 '22 12:10

pdorgambide