Created
February 25, 2020 10:58
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} | |
} |
It was really helpful and I was managed to solve my problem. Great work!
THANKS A BUNCH FOR SHARING THIS!!!!
Had a hard time trying to read the request body multiple times, Cached servlet request did the trick
Perhaps, Spring does have out-of-box support for this: AbstractRequestLoggingFilter
Thank You So Much!!!
Thanks a lot! Worked like a charm!
thanks a lot! This is awesome
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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!! ๐ ๐