1. Overview
In this tutorial, we're going to learn how to implement efficient RestTemplate request/response logging. This is especially useful to debug exchange between two servers.
Unfortunately, Spring Boot doesn't provide an easy way to inspect or log a simple JSON response body.
We're going to explore several methods to log either HTTP headers or, which is the most interesting part, the HTTP body.
2. Basic Logging With RestTemplate
Let's start configuring the RestTemplate logger in the application.properties file:
logging.level.org.springframework.web.client.RestTemplate=DEBUG
As a result, we can see only basic information like the request URL, method, body, and response status:
o.s.w.c.RestTemplate - HTTP POST http://localhost:8082/spring-rest/persons o.s.w.c.RestTemplate - Accept=[text/plain, application/json, application/*+json, */*] o.s.w.c.RestTemplate - Writing [my request body] with org.springframework.http.converter.StringHttpMessageConverter o.s.w.c.RestTemplate - Response 200 OK
However, the response body isn't logged here, which is unfortunate because it's the most interesting part.
To solve this, we'll choose either Apache HttpClient or a Spring interceptor.
3. Logging Headers/Body With Apache HttpClient
First, we have to make RestTemplate use the Apache HttpClient implementation.
We'll need the Maven dependency:
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.12</version> </dependency>
When creating the RestTemplate instance, we should tell it we're using Apache HttpClient:
RestTemplate restTemplate = new RestTemplate(); restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
Then, let's configure the client logger in the application.properties file:
logging.level.org.apache.http=DEBUG logging.level.httpclient.wire=DEBUG
Now we can see both request/response headers and body:
o.a.http.headers - http-outgoing-0 >> POST /spring-rest/persons HTTP/1.1 o.a.http.headers - http-outgoing-0 >> Accept: text/plain, application/json, application/*+json, */* // ... more request headers o.a.http.headers - http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.9 (Java/1.8.0_171) o.a.http.headers - http-outgoing-0 >> Accept-Encoding: gzip,deflate org.apache.http.wire - http-outgoing-0 >> "POST /spring-rest/persons HTTP/1.1[\r][\n]" org.apache.http.wire - http-outgoing-0 >> "Accept: text/plain, application/json, application/*+json, */*[\r][\n]" org.apache.http.wire - http-outgoing-0 >> "Content-Type: text/plain;charset=ISO-8859-1[\r][\n]" // ... more request headers org.apache.http.wire - http-outgoing-0 >> "[\r][\n]" org.apache.http.wire - http-outgoing-0 >> "my request body" org.apache.http.wire - http-outgoing-0 << "HTTP/1.1 200 [\r][\n]" org.apache.http.wire - http-outgoing-0 << "Content-Type: application/json[\r][\n]" // ... more response headers org.apache.http.wire - http-outgoing-0 << "Connection: keep-alive[\r][\n]" org.apache.http.wire - http-outgoing-0 << "[\r][\n]" org.apache.http.wire - http-outgoing-0 << "21[\r][\n]" org.apache.http.wire - http-outgoing-0 << "["Lucie","Jackie","Danesh","Tao"][\r][\n]"
However, these logs are verbose and not handy to debug.
We'll see how to solve this in the following chapter.
4. Logging Body With a RestTemplate Interceptor
As another solution, we can configure interceptors for RestTemplate.
Thus, we'll create a new LoggingInterceptor to customize our logs:
public class LoggingInterceptor implements ClientHttpRequestInterceptor { static Logger log = LoggerFactory.getLogger(LoggingInterceptor.class); @Override public ClientHttpResponse intercept(HttpRequest req, byte[] reqBody, ClientHttpRequestExecution ex) throws IOException { log.debug("Request body: {}", new String(reqBody, StandardCharsets.UTF_8)); ClientHttpResponse response = ex.execute(req, reqBody); InputStreamReader isr = new InputStreamReader(response.getBody(), StandardCharsets.UTF_8); String body = new BufferedReader(isr) .lines() .collect(Collectors.joining("\n")); log.debug("Response body: {}", body); return response; } }
As can be seen, we can much better read the useful information from the output:
c.b.r.l.LoggingInterceptor - Request body: my request body c.b.r.l.LoggingInterceptor - Response body: ["Lucie","Jackie","Danesh","Tao"]
5. Conclusion
RestTemplate request/response logging is not a straightforward matter, as Spring Boot doesn't include it out-of-the-box.
Fortunately, we've seen that we can use the Apache HttpClient logger to get a verbose trace of exchanged data.
Or, we can implement a custom interceptor to get more human-readable logs.
As always, the source code for this article is available over on GitHub.