Spring Framework is a powerful must-have tool for any Java developer. Spring offers a convenient template to consume REST resources. One day I encountered problem in our production environment and realized that I need some mechanism to log exactly what requests SpringTemplate sends to REST endpoints. In this post we’ll deal with logging Spring REST template requests.
Fortunately, since Spring 3.1 it’s available through writing your own interceptor which implements ClientHttpRequestInterceptor
and override it’s intercept
method.
Writing custom ClientHttpRequestInterceptor
In this method you can get all info you need about the request was sent (http method, headers, URI, body) and response received (status code, headers, body).
Here we use public static byte[] toByteArray(InputStream in)
method from Guava library to read all bytes to byte array, but if you’re using Java 9 you can do it without additional libraries by using public byte[] readAllBytes() throws IOException
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
import com.google.common.io.ByteStreams;
import org.slf4j.*;
import org.springframework.http.*;
import java.io.IOException;
import java.nio.charset.Charset;
public class RequestLoggingInterceptor implements ClientHttpRequestInterceptor {
private final static Logger log = LoggerFactory.getLogger(RequestLoggingInterceptor.class);
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse response = execution.execute(request, body);
log.debug("request method: {}, request URI: {}, request headers: {}, request body: {}, response status code: {}, response headers: {}, response body: {}",
request.getMethod(),
request.getURI(),
request.getHeaders(),
new String(body, Charset.forName("UTF-8")),
response.getStatusCode(),
response.getHeaders(),
new String(ByteStreams.toByteArray(response.getBody()), Charset.forName("UTF-8")));
return response;
}
}
|
I’m using Logback here for logging.
Now you have to set your new interceptor when constructing RestTemplate:
1
2
|
RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
restTemplate.setInterceptors(Collections.singletonList(new RequestLoggingInterceptor()));
|
There are few things to notice:
- When constructing template you should wrap your request factory with
BufferingClientHttpRequestFactory
. In this case I’m using default SimpleClientHttpRequestFactory
. Otherwise response body will be null after you read all bytes from InputStream
.
- If you have other interceptors set and some of them are modifying request parameters , your logging interceptor should be the last one in chain.
Finally you can create helper method to instantiate template this way:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public static RestTemplate newInterceptingTemplate(ClientHttpRequestFactory requestFactory, ClientHttpRequestInterceptor... additionalInterceptors) {
RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(requestFactory));
restTemplate.setInterceptors(setInterceptors(additionalInterceptors));
return restTemplate;
}
private static List<ClientHttpRequestInterceptor> setInterceptors(ClientHttpRequestInterceptor... additionalInterceptors) {
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
if (additionalInterceptors.length > 0) {
interceptors.addAll(Arrays.asList(additionalInterceptors));
}
interceptors.add(new RequestLoggingInterceptor());
return interceptors;
}
|