Mar 1, 2018

Log your RestTemplate Request and Response without destroying the body

When you’re working with REST services, it can be very useful for debugging to be able to log both the request and the response info. Fortunately, if you’re using the Spring framework’s RestTemplate its fairly easy to add an interceptor to do just that.

First let’s create our logger

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
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.io.IOException;
import java.nio.charset.Charset;
 
public class RequestResponseLoggingInterceptor implements ClientHttpRequestInterceptor {
 
    private final Logger log = LoggerFactory.getLogger(this.getClass());
 
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        logRequest(request, body);
        ClientHttpResponse response = execution.execute(request, body);
        logResponse(response);
        return response;
    }
 
    private void logRequest(HttpRequest request, byte[] body) throws IOException {
        if (log.isDebugEnabled()) {
            log.debug("===========================request begin================================================");
            log.debug("URI         : {}", request.getURI());
            log.debug("Method      : {}", request.getMethod());
            log.debug("Headers     : {}", request.getHeaders());
            log.debug("Request body: {}", new String(body, "UTF-8"));
            log.debug("==========================request end================================================");
        }
    }
 
    private void logResponse(ClientHttpResponse response) throws IOException {
        if (log.isDebugEnabled()) {
            log.debug("============================response begin==========================================");
            log.debug("Status code  : {}", response.getStatusCode());
            log.debug("Status text  : {}", response.getStatusText());
            log.debug("Headers      : {}", response.getHeaders());
            log.debug("Response body: {}", StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()));
            log.debug("=======================response end=================================================");
        }
    }
}

Now we simply to add it to our interceptor.

 
RestTemplate restTemplate = new RestTemplate();
 
restTemplate.setInterceptors(Collections.singletonList(new RequestResponseLoggingInterceptor()));
 
// use you restTemplate to make your REST call(s)

At this point there is one gotcha. The response body is a stream and if you read it in your interceptor it won’t be available for RestTemplate to deserialize it into your object model. In other words, when you call restTemplate.get… you’ll always get back empty objects (even as you see the object in your response. Fortunately you can fix that by using a BufferingClientHttpRequestFactory.

 
ClientHttpRequestFactory factory = new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory());
 
RestTemplate restTemplate = new RestTemplate(factory);
 
restTemplate.setInterceptors(Collections.singletonList(new RequestResponseLoggingInterceptor()));
 
// use you restTemplate to make your REST call(s)

About the Author

Scott Bock profile.

Scott Bock

Principal Technologist

Scott is a Senior Software Engineer with over 12 years of experience using Java, and 5 years experience in technical leadership positions. His strengths include troubleshooting and problem solving abilities, excellent repertoire with customers and management, and verbal and written communication. He develops code across the entire technology stack including database, application, and user interface.

One thought on “Log your RestTemplate Request and Response without destroying the body

  1. Jan Kowalski says:

    This solution is wrong. Please consider empty json object in response (“{}”).

    1. jose says:

      This solution has nothing to do with JSON.

  2. Nathanael Shergold says:

    Thanks for posting this, it has been helpful for debugging. I was going to say there’s a bit of a gotcha with the InputStream not being reset but as usual having just skimmed I missed your excellent fix for that as well.

    Nathanael Shergold
    Seventy-Nine Consulting
    https://79sconsulting.com.au

  3. Mr Developer says:

    Hi, nice solution however doesn’t work when using mockServer to test the server.

    1. John says:

      Would be curious to know if you found a way around this though? I am thinking of programmatically not logging during unit test

    2. Yuriy says:

      did you found out, how this solution can be used with mockServer?

  4. Sergey says:

    Reading inputStream due StreamUtils.copyToString(response.getBody(), Charset.defaultCharset())) or something else causes that body becomes empty and e.g. you will not be able to read body after GET method request. Seems like after response.getBody() inputStreas close and somehow body marks as read.

  5. Rashmi Shetty says:

    This messes up the error handling,

  6. jose says:

    Thank you. Works great.

  7. Raffaele Litto says:

    StreamUtils.copyToString and other StreamUtils methods do copy the InputStream but move the input stream index position to the end, so you won’t be able to reset the stream and consume it somewhere else.

  8. Robert Sullivan says:

    Nice, I think this is formatted a little better from the StackOverflow answer this was copied from. https://stackoverflow.com/questions/7952154/spring-resttemplate-how-to-enable-full-debugging-logging-of-requests-responses. And it seems Spring has some built-in capability to do this already: AbstractRequestLoggingFilter https://www.baeldung.com/spring-http-logging

    1. WiP says:

      This blog post copy paste the code without attribution. This code is incorrect for error handling and a solution is provided in the original source : https://stackoverflow.com/questions/7952154/spring-resttemplate-how-to-enable-full-debugging-logging-of-requests-responses/57252427#57252427

  9. Atul says:

    Awesome it works

  10. Akanksha says:

    I want the sample of junit test case for this class.

  11. Omer says:

    This solution is good but it has one issue. If you log every request and response, you will have a problem with memory. The bufferClientHttpRequestFactory allows the requests to be buffered which means they would be copied to memory so that the interceptors (logger) can read it. I would extend the BufferingClientHttpRequestFactory to only buffer based on uri and httpMethod.

  12. Pig says:

    Thank you. Works great!

  13. Murilo says:

    Te amo!

  14. Akila udara says:

    Use following code this will allow you to read response more than once

    /**
    * Build the RestTemplate used to make HTTP requests.
    * @return RestTemplate
    */
    protected RestTemplate buildRestTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    // This allows us to read the response more than once – Necessary for debugging.
    restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory()));
    return restTemplate;
    }

  15. Salmane says:

    Very good, it works

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Blog Posts
Simple improvements to making decisions in teams
Software development teams need to make a lot of decisions. Functional requirements, non-functional requirements, user experience, API contracts, tech stack, architecture, database schemas, cloud providers, deployment strategy, test strategy, security, and the list goes on. […]
JavaScript Bundle Optimization – Polyfills
If you are lucky enough to only support a small subset of browsers (for example, you are targeting a controlled set of users), feel free to move along. However, if your website is open to […]
Creating Mocks For Unit Testing in Go
Unit testing is an important part of any project, and Go built its framework with a testing package; making unit testing part of the language. This testing framework is good for most scenarios, but you […]
Resetting Database Between Spring Integration Tests
When tasked with having to write an integration test or a Spring Webflux test that uses a database, it can be cumbersome to have to reset the database between each test by using @DirtiesContext. Using […]