Skip to content

Instantly share code, notes, and snippets.

@thegeekyasian
Created February 25, 2020 10:58
Show Gist options
  • Save thegeekyasian/c21bb407827b32d2d9cb787f4cf72e3a to your computer and use it in GitHub Desktop.
Save thegeekyasian/c21bb407827b32d2d9cb787f4cf72e3a to your computer and use it in GitHub Desktop.
Spring Boot HttpServletRequest Logging Filter. A Request Logging Filter for Spring Boot applications, that allows you to read the request body multiple times. The request body is cached using an overridden HttpServletRequestWrapper. Then a reader that allows the client to read the body multiple times is returned.
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.util.*;
/**
* @author TheGeekyAsian
* Created at 2:22 PM, 2/18/20
*
* See <a href="http://thegeekyasian.com">TheGeekyAsian.com</a>
* @see <a href="http://github.com/thegeekyasian/">TheGeekyAsian on GitHub</a>
*/
@Component
@Order(value = Ordered.HIGHEST_PRECEDENCE)
public class HttpRequestLoggingFilter implements Filter {
/* Just to avoid logging credentials or related details. You can empty this list or remove it completely if you
want to log the security details too. */
private static final List<String> HEADERS_TO_SKIP = Arrays.asList("authorization", "token", "security", "oauth", "auth");
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
CachedRequestHttpServletRequest cachedRequestHttpServletRequest =
new CachedRequestHttpServletRequest((HttpServletRequest) servletRequest);
String log = String.format("URL: %s | Requester: %s | HTTP Method: %s | Headers: %s | QueryStringParams: %s | RequestBody: %s",
cachedRequestHttpServletRequest.getRequestURL(), cachedRequestHttpServletRequest.getRemoteAddr(),
cachedRequestHttpServletRequest.getMethod(), getRequestHeaders(cachedRequestHttpServletRequest),
cachedRequestHttpServletRequest.getQueryString(), getBody(cachedRequestHttpServletRequest));
// Log your request here!
System.out.println(log); //Try using a logger instead of the print statement.
chain.doFilter(cachedRequestHttpServletRequest, servletResponse);
}
private Map<String, String> getRequestHeaders(HttpServletRequest request) {
Map<String, String> headersMap = new HashMap<>();
Enumeration<String> headerEnumeration = request.getHeaderNames();
while (headerEnumeration.hasMoreElements()) {
String header = headerEnumeration.nextElement();
// Filter the headers that you need to skip.
// If you don't want to filter any headers and want to log all of them,
// you can remove the condition below.
if (HEADERS_TO_SKIP.stream().noneMatch(h -> h.toLowerCase().contains(header.toLowerCase())
|| header.toLowerCase().contains(h.toLowerCase()))) {
headersMap.put(header, request.getHeader(header));
}
}
return headersMap;
}
private String getBody(CachedRequestHttpServletRequest request) throws IOException {
StringBuilder body = new StringBuilder();
String line;
BufferedReader reader = request.getReader();
while ((line = reader.readLine()) != null) {
body.append(line);
}
return body.toString();
}
private static class CachedRequestHttpServletRequest extends HttpServletRequestWrapper {
private byte[] cachedBody;
public CachedRequestHttpServletRequest(HttpServletRequest request) throws IOException {
super(request);
this.cachedBody = StreamUtils.copyToByteArray(request.getInputStream());
}
@Override
public ServletInputStream getInputStream() {
return new CachedRequestServletInputStream(this.cachedBody);
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(new ByteArrayInputStream(this.cachedBody)));
}
}
private static class CachedRequestServletInputStream extends ServletInputStream {
private InputStream cachedBodyInputStream;
public CachedRequestServletInputStream(byte[] cachedBody) {
this.cachedBodyInputStream = new ByteArrayInputStream(cachedBody);
}
@Override
public boolean isFinished() {
try {
return cachedBodyInputStream.available() == 0;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
throw new UnsupportedOperationException();
}
@Override
public int read() throws IOException {
return cachedBodyInputStream.read();
}
}
}
@avinaxhchandwani
Copy link

Ah! This is awesome. I was trying to read my request body and I received an empty body on my controller.

This solved my problem and saved a lot of time. Thanks again!! ๐Ÿ‘ ๐Ÿ‘

@sraza123
Copy link

It was really helpful and I was managed to solve my problem. Great work!

@SalmanSaleem
Copy link

THANKS A BUNCH FOR SHARING THIS!!!!

Had a hard time trying to read the request body multiple times, Cached servlet request did the trick

@nstdio
Copy link

nstdio commented Oct 7, 2021

Perhaps, Spring does have out-of-box support for this: AbstractRequestLoggingFilter

@lgomezf16
Copy link

Thank You So Much!!!

@ffroliva
Copy link

ffroliva commented Nov 9, 2022

Thanks a lot! Worked like a charm!

@brknce
Copy link

brknce commented Aug 23, 2023

thanks a lot! This is awesome

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment