Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
RestTemplate-interceptor that logs outgoing requests and resulting responses
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StreamUtils;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
* Allows logging outgoing requests and the corresponding responses.
* Requires the use of a {@link org.springframework.http.client.BufferingClientHttpRequestFactory} to log
* the body of received responses.
public class LoggingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
protected Logger requestLogger;
protected Logger responseLogger;
* Creates an interceptor with request logger {@code spring.web.client.MessageTracing.sent}
* and response logger {@code spring.web.client.MessageTracing.received}, loosely following
* Spring-WS logger naming conventions.
public LoggingClientHttpRequestInterceptor() {
* @param requestLogger the logger used to log sent requests
* @param responseLogger the logger used to log received responses
public LoggingClientHttpRequestInterceptor(Logger requestLogger, Logger responseLogger) {
this.requestLogger = requestLogger;
this.responseLogger = responseLogger;
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
logRequest(request, body);
ClientHttpResponse response = execution.execute(request, body);
logResponse(request, response);
return response;
protected void logRequest(HttpRequest request, byte[] body) {
if (requestLogger.isDebugEnabled()) {
StringBuilder builder = new StringBuilder("Sending ").append(request.getMethod()).append(" request to ").append(request.getURI());
if (body.length > 0 && hasTextBody(request.getHeaders())) {
String bodyText = new String(body, determineCharset(request.getHeaders()));
builder.append(": [").append(bodyText).append("]");
protected void logResponse(HttpRequest request, ClientHttpResponse response) {
if (responseLogger.isDebugEnabled()) {
try {
StringBuilder builder = new StringBuilder("Received \"")
.append(response.getRawStatusCode()).append(" ").append(response.getStatusText()).append("\" response for ")
.append(request.getMethod()).append(" request to ").append(request.getURI());
HttpHeaders responseHeaders = response.getHeaders();
long contentLength = responseHeaders.getContentLength();
if (contentLength != 0) {
if (hasTextBody(responseHeaders) && !isMockedResponse(response)) {
String bodyText = StreamUtils.copyToString(response.getBody(), determineCharset(responseHeaders));
builder.append(": [").append(bodyText).append("]");
} else {
if (contentLength == -1) {
builder.append(" with content of unknown length");
} else {
builder.append(" with content of length ").append(contentLength);
MediaType contentType = responseHeaders.getContentType();
if (contentType != null) {
builder.append(" and content type ").append(contentType);
} else {
builder.append(" and unknown content type");
} catch (IOException e) {
responseLogger.warn("Failed to log response for {} request to {}", request.getMethod(), request.getURI(), e);
protected boolean hasTextBody(HttpHeaders headers) {
MediaType contentType = headers.getContentType();
if (contentType != null) {
if ("text".equals(contentType.getType())) {
return true;
String subtype = contentType.getSubtype();
if (subtype != null) {
return "xml".equals(subtype) || "json".equals(subtype) ||
subtype.endsWith("+xml") || subtype.endsWith("+json");
return false;
protected Charset determineCharset(HttpHeaders headers) {
MediaType contentType = headers.getContentType();
if (contentType != null) {
try {
Charset charSet = contentType.getCharset();
if (charSet != null) {
return charSet;
} catch (UnsupportedCharsetException e) {
// ignore
return StandardCharsets.UTF_8;
private boolean isMockedResponse(ClientHttpResponse response) {
return "MockClientHttpResponse".equals(response.getClass().getSimpleName());
Copy link

Thank you, it helped me a lot!

Copy link

Thx 👍

Copy link

Thanks, this helped me a lot :-)

Copy link

Thanks, this is so helpful!

Copy link

rmargam commented Aug 29, 2018

Hi, I have used a version of this code doing something like"Response body: {}", StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()))

this exhausts the body and makes it so there is no body when the actual resources want to use it.
Is there a better way to do it ?

for example this spock test fails.

def 'fetch access token integration test'() {

    RestTemplate rt = new RestTemplate()
    RequestResponseLoggingInterceptor ri = new RequestResponseLoggingInterceptor()

    ResponseEntity<String> responseEntity="", HttpMethod.GET, null, String.class)



Copy link

jkuipers commented Sep 4, 2018

@rmargam register a BufferingClientHttpRequestFactory with your RestTemplate as stated in the type-level JavaDoc to avoid that issue. It wraps the response in a class that buffers the response body internally, so that it can be consumed multiple times.
One way to automatically do that for all your RestTemplate beans is shown here, assuming you're using Spring Boot's RestTemplateBuilder:

Copy link

troeng commented Sep 20, 2019

Thank you for this, so helpful. Finally a solution that works with the BufferingClientHttpRequestFactory that do not close the stream when reading.

Copy link

hi, how can i use this interceptor in my project? I am trying to store response and request into mongodb, but i am confused how i can use this class. Can you help me please?

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