When we are working with REST services, it can be very useful for debugging to be able to log both the request and the response info. We can do this very easy just by adding an interceptor in our project.
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import lombok.extern.log4j.Log4j2; @Log4j2 public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException { traceRequest(request, body); ClientHttpResponse response = execution.execute(request, body); traceResponse(response); return response; } private void traceRequest(HttpRequest request, byte[] body) throws IOException { log.info("=========================== Request Begin ==========================="); log.info("URI : " + request.getURI()); log.info("Method : " + request.getMethod()); log.info("Headers : " + request.getHeaders()); log.info("Body : " + new String(body, "utf-8")); log.info("============================ Request End ============================"); } private void traceResponse(ClientHttpResponse response) throws IOException { StringBuilder inputStringBuilder = new StringBuilder(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), "utf-8")); String line = bufferedReader.readLine(); while (line != null) { inputStringBuilder.append(line); line = bufferedReader.readLine(); } log.info("=========================== Response Begin ==========================="); log.info("Status code : {}", response.getStatusCode()); log.info("Status text : {}", response.getStatusText()); log.info("Headers : {}", response.getHeaders()); log.info("Body : {}", inputStringBuilder.toString()); log.info("============================ Response End ============================"); } }
Now create a BeanConfig class to create RestTemplate object.
import java.util.Collections; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.BufferingClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; import com.altafjava.misc.LoggingRequestInterceptor; @Configuration public class BeanConfig { @Bean public RestTemplate buildrestTemplate() { RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory())); restTemplate.setInterceptors(Collections.singletonList(new LoggingRequestInterceptor())); return restTemplate; } }
That's it! But you will run in a problem.
Actually Response body is a stream and if we read it in the interceptor it won’t be available for RestTemplate to deserialize it into object model. In other words, when we call restTemplate.get..() we will always get back empty objects (even as we see the object in the response. Fortunately we can fix that by using org.springframework.http.client.BufferingClientHttpResponseWrapper class.
But unfortunately, you cannot use this class because this class is not public. Hence we cannot create object of this class. Hence to solve this we need to manually create this class with public modifier. Hence to solve this finally lets follow some steps.
import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.client.ClientHttpResponse; import org.springframework.util.StreamUtils; public class BufferingClientHttpResponseWrapper implements ClientHttpResponse { private final ClientHttpResponse response; private byte[] body; public BufferingClientHttpResponseWrapper(ClientHttpResponse response) { this.response = response; } @Override public InputStream getBody() throws IOException { if (body == null) { body = StreamUtils.copyToByteArray(response.getBody()); } return new ByteArrayInputStream(body); } @Override public HttpStatus getStatusCode() throws IOException { return this.response.getStatusCode(); } @Override public int getRawStatusCode() throws IOException { return this.response.getRawStatusCode(); } @Override public String getStatusText() throws IOException { return this.response.getStatusText(); } @Override public HttpHeaders getHeaders() { return this.response.getHeaders(); } @Override public void close() { this.response.close(); } }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import lombok.extern.log4j.Log4j2; @Log4j2 public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException { traceRequest(request, body); ClientHttpResponse response = execution.execute(request, body); response = traceResponse(response); return response; } private void traceRequest(HttpRequest request, byte[] body) throws IOException { log.info("=========================== Request Begin ==========================="); log.info("URI : " + request.getURI()); log.info("Method : " + request.getMethod()); log.info("Headers : " + request.getHeaders()); log.info("Body : " + new String(body, "utf-8")); log.info("============================ Request End ============================"); } private ClientHttpResponse traceResponse(ClientHttpResponse response) throws IOException { ClientHttpResponse newCopiedResponse = new BufferingClientHttpResponseWrapper(response); StringBuilder inputStringBuilder = new StringBuilder(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(newCopiedResponse.getBody(), "UTF-8")); String line = bufferedReader.readLine(); while (line != null) { inputStringBuilder.append(line); line = bufferedReader.readLine(); } log.info("=========================== Response Begin ==========================="); log.info("Status code : {}", response.getStatusCode()); log.info("Status text : {}", response.getStatusText()); log.info("Headers : {}", response.getHeaders()); log.info("Response Body : {}", inputStringBuilder.toString()); log.info("============================ Response End ============================"); return newCopiedResponse; } }
import java.util.Collections; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.BufferingClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.web.client.RestTemplate; import com.altafjava.misc.LoggingRequestInterceptor; @Configuration public class BeanConfig { @Bean public RestTemplate buildrestTemplate() { RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory())); restTemplate.setInterceptors(Collections.singletonList(new LoggingRequestInterceptor())); return restTemplate; } }