I've managed to enable access logging in Tomcat by editing the conf/server.xml and uncommenting the entry for the org.apache.catalina.valves.AccessLogValve Valve. I'd like to be able to dump the contents of POST's payload. It doesn't seem like any of the options in the pattern will do this. Is there a built in way to do this? Do I use the AccessLogValve?
Asked
Active
Viewed 4.5k times
4 Answers
9
Since there was not a suggestion of a built in way to get the POST payload, I went ahead and wrote a custom filter that dumps the contents of the payload. Specifically:
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
LOG.debug("payload: " + requestWrapper.getRequestBody());
and web.xml
:
<filter>
<filter-name>PayloadLoggingFilter</filter-name>
<filter-class>com.host.PayloadLoggingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>PayloadLoggingFilter</filter-name>
<url-pattern>/resources/*</url-pattern>
</filter-mapping>

StackzOfZtuff
- 2,534
- 1
- 28
- 25

Tim
- 6,851
- 11
- 42
- 46
-
9Where does the `requestWrapper` come from? Or is that supposed to be the `request` object? – andho Feb 06 '14 at 06:15
-
requestWrapper comes from here aka MyRequestWrapper. it works! http://natch3z.blogspot.co.uk/2009/01/read-request-body-in-filter.html – goat Mar 09 '15 at 04:48
-
Where should I put "public void doFilter(..." ? – Joe Jadamec Oct 23 '19 at 17:14
5
In your webapp web.xml add:
<filter> <filter-name>PayloadLoggingFilter</filter-name> <filter-class>your.MyLogFilter</filter-class> </filter> <filter-mapping> <filter-name>PayloadLoggingFilter</filter-name> <url-pattern>/path/*</url-pattern> </filter-mapping>
Create your Filter:
import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.Enumeration; import java.util.HashMap; import java.util.Locale; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.output.TeeOutputStream; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.util.servlet.ServletInputStreamWrapper; public class MyLogFilter implements Filter { private static final Log logger = LogFactoryUtil.getLog(MyLogFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) response; Map<String, String> requestMap = this.getTypesafeRequestMap(httpServletRequest); BufferedRequestWrapper bufferedReqest = new BufferedRequestWrapper(httpServletRequest); BufferedResponseWrapper bufferedResponse = new BufferedResponseWrapper(httpServletResponse); chain.doFilter(bufferedReqest, bufferedResponse); if(bufferedResponse.getContent()!=null){ //Response final StringBuilder resMessage = new StringBuilder(); resMessage.append(" [RESPONSE:") .append(bufferedResponse.getContent() ) .append("]"); logger.info(resMessage); } else{ //Request final StringBuilder reqMessage = new StringBuilder( "REST Request - ").append("[HTTP METHOD:") .append(httpServletRequest.getMethod()) .append("] [PATH INFO:") .append(httpServletRequest.getPathInfo()) .append("] [REQUEST PARAMETERS:").append(requestMap) .append("] [REQUEST BODY:") .append(bufferedReqest.getRequestBody()) .append("] [REMOTE ADDRESS:") .append(httpServletRequest.getRemoteAddr()).append("]"); logger.info(reqMessage); } } catch (Throwable a) { logger.error(a); } } private Map<String, String> getTypesafeRequestMap(HttpServletRequest request) { Map<String, String> typesafeRequestMap = new HashMap<String, String>(); Enumeration<?> requestParamNames = request.getParameterNames(); while (requestParamNames.hasMoreElements()) { String requestParamName = (String) requestParamNames.nextElement(); String requestParamValue = request.getParameter(requestParamName); typesafeRequestMap.put(requestParamName, requestParamValue); } return typesafeRequestMap; } @Override public void destroy() { } 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; } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(this.getInputStream())); } 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 teeStream; PrintWriter teeWriter; ByteArrayOutputStream bos; public BufferedResponseWrapper(HttpServletResponse response) { original = response; } public String getContent() { if(bos == null){ return null; } return bos.toString(); } @Override public PrintWriter getWriter() throws IOException { if (this.teeWriter == null) { this.teeWriter = new PrintWriter(new OutputStreamWriter( getOutputStream())); } return this.teeWriter; } @Override public ServletOutputStream getOutputStream() throws IOException { if (teeStream == null) { bos = new ByteArrayOutputStream(); teeStream = new TeeServletOutputStream( original.getOutputStream(), bos); } return teeStream; } @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 { if (teeStream != null) { teeStream.flush(); } if (this.teeWriter != null) { this.teeWriter.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); } } }

Joel Peltonen
- 13,025
- 6
- 64
- 100

Hao Deng
- 655
- 8
- 5
4
If the post is a form (application/x-www-urlencoded), you can use ExtendedAccessLogValve,
http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/valves/ExtendedAccessLogValve.html
You have to select individual parameters in the pattern like this,
x-P(param_name)

ZZ Coder
- 74,484
- 29
- 137
- 169
-
-
If it's not a form, you can't really log the post body. The log format is defined by http://www.w3.org/TR/WD-logfile.html. It doesn't support arbitrary post body, which can be binary and huge, in case of file upload. – ZZ Coder Oct 07 '09 at 04:00
-
1Unfortunately, this is not the case for me. I'm not posting content as parameters, though the payload size will not likely be more than a few kb. – Tim Oct 07 '09 at 16:37
-
Post payload doesn't belong to logs. You need to dump it somewhere else in your server. – ZZ Coder Oct 07 '09 at 17:15
-
Which is fine, but I want to be able to associate the access information for it. Is there some trick to doing this without writing a custom Valve or the like? – Tim Oct 07 '09 at 21:01
-
I don't think you will find anything inside Tomcat. If this is indeed for logging, you need to come up with a log format and hex-dump the request in your server. If it's for debugging, use Wireshark or Fiddler to watch the post body. – ZZ Coder Oct 07 '09 at 22:32
1
See RequestDumperValve or RequestDumperFilter here: http://tomcat.apache.org/tomcat-5.5-doc/config/valve.html#Request_Dumper_Valve it does what you want.

Janning Vygen
- 8,877
- 9
- 71
- 102
-
5RequestDumperFilter does not log the POST payload, only the Headers and URL (including query string) – ChristophK Feb 27 '15 at 08:46