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?
3 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.
-
You should override not just `getWriter()`, but also `getOutputStream()` – pihentagy Jan 13 '11 at 09:42
-
@pihentagy: 1) If you need to. 2) Use the same which is set by `setCharacterEncoding()` method. – BalusC Jan 13 '11 at 12:52
-
@BalusC: 1) how can you know which one is called by other `Filter`s or by the code, which processes the request? – pihentagy Jan 13 '11 at 14:31
-
AFAIK overriding `void write(int c)` is sufficient. – pihentagy Jan 14 '11 at 12:47
-
PrintWriter possibly also uses the OutputStream, so supporting a custom ServletOutputStream should be enough (which only needs a write(int) override). – ron May 30 '11 at 11:09
-
11@BalusC When you instantiate the `CopyPrintWriter` you already consume the response output stream by calling `getWriter` and as a result the wrapper object is not consistent anymore, leading to an `IllegalStateException`. – vahidg Jun 26 '12 at 09:55
-
@Chishi: read the paragraph below the code snippet. It's just a kickoff example to give you the idea. – BalusC Jun 26 '12 at 10:14
-
It gives me the idea, but the code you posted does not compile! At the moment I can't figure out how can I call **getWriter** without leading to an IllegalStateException like @Chishi pointed – thermz Oct 30 '12 at 10:56
-
@thermz: your comment is confusing. I don't see any compilation errors in the code. Aren't you mixing runtime exceptions with compilation errors? – BalusC Oct 30 '12 at 11:02
-
1you are right, I'm abolutely wrong! I used the wrong word, the code compile, but does not run for the reason explained before – thermz Oct 30 '12 at 11:10
-
I'd really like to know how to solve the runtime exception being thrown. – lm2s Dec 28 '12 at 02:53
-
3Solved, check https://gist.github.com/4400492 for working example. The problem, for me, was extending PrintWriter instead of ServletOutputStream. – lm2s Dec 28 '12 at 18:17
-
Causes problem in my case, the `CopyPrintWriter` makes the jsp pages that allow user send Arabic letters to send as HTML entities. – Muhammad Hewedy Sep 09 '14 at 14:58
-
1@Muhammad: set response character encoding before getting writer. – BalusC Sep 09 '14 at 15:16
-
Perfect!, I tried to set the response character encoding `after` the `getWriter` but didn't work. but After your advice, it is perfectly working. – Muhammad Hewedy Sep 09 '14 at 15:26
-
@BalusC, I still need to know why it was working in the past without setting the char encoding, but after introducing your `CopyPrintWriter` it won't work unless I set the char enc? – Muhammad Hewedy Sep 09 '14 at 15:32
-
2@Muhammad: JSP will set it if `@page pageEncoding` is specified. However, this filter already gets the writer before JSP has the chance to set it, it's too late then to set the encoding. – BalusC Sep 09 '14 at 16:05
-
@lm2s When using servlet API 3.1 or later, one gets a compile time error, because you didn't override two methods `isReady` and `setWriteListener` – Adam Burley Nov 16 '15 at 21:40
-
2spring boot 1.5.6 have got a runtime error: java.lang.IllegalStateException: getWriter() has already been called for this response – user2602807 Oct 13 '17 at 13:51
-
@[Im2s] looks like gist.github.com/4400492 this gist no longer exists. – Paramesh Korrakuti Jun 01 '23 at 17:43
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();
}

- 1
- 1

- 1,787
- 19
- 33
-
6For the benefit of future generations... which libraries are `TeeOutputStream` and `DelegatingServletOutputStream` from? – Nick Grealy Jun 24 '16 at 02:49
-
It's from spring and apache io. 1) https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/mock/web/DelegatingServletOutputStream.html 2)https://commons.apache.org/proper/commons-io/javadocs/api-1.4/org/apache/commons/io/output/TeeOutputStream.html – Happier Nov 01 '17 at 13:32
-
This is exactly what I needed. Every solution I found was either long and drawn out or was specifically for calling getWriter... this allows the reading of the response body and having the application return that response to the user as well. – gcerkez Apr 13 '18 at 17:32
Maybe a servlet filter can help you. Think of it as aspect-oriented programming for HTTP.

- 305,152
- 44
- 369
- 561
-
4Funny how every other answer cites mine, but it wasn't the accepted one.... – duffymo Jul 14 '10 at 18:34
-
13It was a helpful hint really (upvoted), but misses the hint about copying the `PrintWriter` – pihentagy Jan 17 '11 at 09:11