Bare-bones Spring Web application does not provide a mechanism for logging bodies of servlet responses. This may be achieved by using Spring Boot Actuator, but an additional dependency has to be introduced into a project. Also more programmatic flexibility may be needed.
Note: Servlet request logging can be achieved via the CommonsRequestLoggingFilter
class, provided by Spring by default.
Please take into consideration that response body can be read only once from the response object's input stream. After that, it becomes unavailable. To mitigate that the response object is wrapped into a content caching wrapper.
@Component
public class LoggingFilterBean extends GenericFilterBean {
private static final Logger log = LoggerFactory.getLogger(LoggingFilterBean.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ContentCachingRequestWrapper requestWrapper = requestWrapper(request);
ContentCachingResponseWrapper responseWrapper = responseWrapper(response);
chain.doFilter(requestWrapper, responseWrapper);
logRequest(requestWrapper);
logResponse(responseWrapper);
}
private void logRequest(ContentCachingRequestWrapper request) {
StringBuilder builder = new StringBuilder();
builder.append(headersToString(list(request.getHeaderNames()), request::getHeader));
builder.append(new String(request.getContentAsByteArray()));
log.info("Request: {}", builder);
}
private void logResponse(ContentCachingResponseWrapper response) throws IOException {
StringBuilder builder = new StringBuilder();
builder.append(headersToString(response.getHeaderNames(), response::getHeader));
builder.append(new String(response.getContentAsByteArray()));
log.info("Response: {}", builder);
response.copyBodyToResponse();
}
private String headersToString(Collection<String> headerNames, Function<String, String> headerValueResolver) {
StringBuilder builder = new StringBuilder();
for (String headerName : headerNames) {
String header = headerValueResolver.apply(headerName);
builder.append("%s=%s".formatted(headerName, header)).append("\n");
}
return builder.toString();
}
private ContentCachingRequestWrapper requestWrapper(ServletRequest request) {
if (request instanceof ContentCachingRequestWrapper requestWrapper) {
return requestWrapper;
}
return new ContentCachingRequestWrapper((HttpServletRequest) request);
}
private ContentCachingResponseWrapper responseWrapper(ServletResponse response) {
if (response instanceof ContentCachingResponseWrapper responseWrapper) {
return responseWrapper;
}
return new ContentCachingResponseWrapper((HttpServletResponse) response);
}
}