Spring RestTemplate – como habilitar a debugging / registro completo de solicitações / respostas?

Eu tenho usado o Spring RestTemplate por um tempo e eu sempre acerto uma parede quando estou tentando depurar seus pedidos e respostas. Eu estou basicamente olhando para ver as mesmas coisas que vejo quando uso curl com a opção “verbose” ativada. Por exemplo :

curl -v http://twitter.com/statuses/public_timeline.rss 

Exibe os dados enviados e os dados recebidos (incluindo os headers, cookies, etc.).

Verifiquei algumas mensagens relacionadas, como: Como faço para registrar a resposta no Spring RestTemplate? mas eu não consegui resolver esse problema.

Uma maneira de fazer isso seria realmente alterar o código-fonte do RestTemplate e adicionar algumas instruções extra de registro lá, mas eu acharia essa abordagem realmente uma coisa de último recurso. Deve haver alguma maneira de informar ao Spring Web Client / RestTemplate para registrar tudo de uma maneira muito mais amigável.

Meu objective seria poder fazer isso com código como:

 restTemplate.put("http://someurl", objectToPut, urlPathValues); 

e, em seguida, para obter o mesmo tipo de informações de debugging (como obtenho com curl) no arquivo de log ou no console. Acredito que isso seria extremamente útil para qualquer um que use o Spring RestTemplate e tenha problemas. Usar o curl para depurar seus problemas do RestTemplate simplesmente não funciona (em alguns casos).

Apenas para concluir o exemplo com uma implementação completa de ClientHttpRequestInterceptor para rastrear solicitação e resposta:

 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; 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; public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor { final static Logger log = LoggerFactory.getLogger(LoggingRequestInterceptor.class); @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, 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.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 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); inputStringBuilder.append('\n'); line = bufferedReader.readLine(); } 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: {}", inputStringBuilder.toString()); log.debug("=======================response end================================================="); } } 

Em seguida, instancie o RestTemplate usando um BufferingClientHttpRequestFactory e o LoggingRequestInterceptor :

 RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory())); List interceptors = new ArrayList<>(); interceptors.add(new LoggingRequestInterceptor()); restTemplate.setInterceptors(interceptors); 

O BufferingClientHttpRequestFactory é necessário, pois queremos usar o corpo da resposta tanto no interceptor quanto no código de chamada inicial. A implementação padrão permite ler o corpo da resposta apenas uma vez.

Estendendo a resposta do @hstoerr com algum código:


Criar LoggingRequestInterceptor para registrar as respostas dos pedidos

 public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor { private static final Logger log = LoggerFactory.getLogger(LoggingRequestInterceptor.class); @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { ClientHttpResponse response = execution.execute(request, body); log(request,body,response); return response; } private void log(HttpRequest request, byte[] body, ClientHttpResponse response) throws IOException { //do logging } } 

Setup RestTemplate

 RestTemplate rt = new RestTemplate(); //set interceptors/requestFactory ClientHttpRequestInterceptor ri = new LoggingRequestInterceptor(); List ris = new ArrayList(); ris.add(ri); rt.setInterceptors(ris); rt.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()); 

no Spring Boot você pode obter o pedido / resposta completo definindo isso em propriedades (ou outro método de 12 fatores)

 logging.level.org.apache.http=DEBUG 

essas saídas

 -DEBUG .icDefaultHttpClientConnectionOperator : Connecting to localhost/127.0.0.1:41827 -DEBUG .icDefaultHttpClientConnectionOperator : Connection established 127.0.0.1:39546< ->127.0.0.1:41827 -DEBUG oahttp.impl.execchain.MainClientExec : Executing request POST /v0/users HTTP/1.1 -DEBUG oahttp.impl.execchain.MainClientExec : Target auth state: UNCHALLENGED -DEBUG oahttp.impl.execchain.MainClientExec : Proxy auth state: UNCHALLENGED -DEBUG org.apache.http.headers : http-outgoing-0 >> POST /v0/users HTTP/1.1 -DEBUG org.apache.http.headers : http-outgoing-0 >> Content-Type: application/json;charset=UTF-8 -DEBUG org.apache.http.headers : http-outgoing-0 >> Content-Length: 56 -DEBUG org.apache.http.headers : http-outgoing-0 >> Host: localhost:41827 -DEBUG org.apache.http.headers : http-outgoing-0 >> Connection: Keep-Alive -DEBUG org.apache.http.headers : http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.2 (Java/1.8.0_102) -DEBUG org.apache.http.headers : http-outgoing-0 >> Accept-Encoding: gzip,deflate -DEBUG org.apache.http.wire : http-outgoing-0 >> "POST /v0/users HTTP/1.1[\r][\n]" -DEBUG org.apache.http.wire : http-outgoing-0 >> "Content-Type: application/json;charset=UTF-8[\r][\n]" -DEBUG org.apache.http.wire : http-outgoing-0 >> "Content-Length: 56[\r][\n]" -DEBUG org.apache.http.wire : http-outgoing-0 >> "Host: localhost:41827[\r][\n]" -DEBUG org.apache.http.wire : http-outgoing-0 >> "Connection: Keep-Alive[\r][\n]" -DEBUG org.apache.http.wire : http-outgoing-0 >> "User-Agent: Apache-HttpClient/4.5.2 (Java/1.8.0_102)[\r][\n]" -DEBUG org.apache.http.wire : http-outgoing-0 >> "Accept-Encoding: gzip,deflate[\r][\n]" -DEBUG org.apache.http.wire : http-outgoing-0 >> "[\r][\n]" -DEBUG org.apache.http.wire : http-outgoing-0 >> "{"id":null,"email":"xenoterracide@gmail.com","new":true}" 

e resposta

 -DEBUG .icDefaultHttpClientConnectionOperator : Connecting to localhost/127.0.0.1:41827 -DEBUG .icDefaultHttpClientConnectionOperator : Connection established 127.0.0.1:39546< ->127.0.0.1:41827 -DEBUG oahttp.impl.execchain.MainClientExec : Executing request POST /v0/users HTTP/1.1 -DEBUG oahttp.impl.execchain.MainClientExec : Target auth state: UNCHALLENGED -DEBUG oahttp.impl.execchain.MainClientExec : Proxy auth state: UNCHALLENGED -DEBUG org.apache.http.headers : http-outgoing-0 >> POST /v0/users HTTP/1.1 -DEBUG org.apache.http.headers : http-outgoing-0 >> Content-Type: application/json;charset=UTF-8 -DEBUG org.apache.http.headers : http-outgoing-0 >> Content-Length: 56 -DEBUG org.apache.http.headers : http-outgoing-0 >> Host: localhost:41827 -DEBUG org.apache.http.headers : http-outgoing-0 >> Connection: Keep-Alive -DEBUG org.apache.http.headers : http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.2 (Java/1.8.0_102) -DEBUG org.apache.http.headers : http-outgoing-0 >> Accept-Encoding: gzip,deflate -DEBUG org.apache.http.wire : http-outgoing-0 >> "POST /v0/users HTTP/1.1[\r][\n]" -DEBUG org.apache.http.wire : http-outgoing-0 >> "Content-Type: application/json;charset=UTF-8[\r][\n]" -DEBUG org.apache.http.wire : http-outgoing-0 >> "Content-Length: 56[\r][\n]" -DEBUG org.apache.http.wire : http-outgoing-0 >> "Host: localhost:41827[\r][\n]" -DEBUG org.apache.http.wire : http-outgoing-0 >> "Connection: Keep-Alive[\r][\n]" -DEBUG org.apache.http.wire : http-outgoing-0 >> "User-Agent: Apache-HttpClient/4.5.2 (Java/1.8.0_102)[\r][\n]" -DEBUG org.apache.http.wire : http-outgoing-0 >> "Accept-Encoding: gzip,deflate[\r][\n]" -DEBUG org.apache.http.wire : http-outgoing-0 >> "[\r][\n]" -DEBUG org.apache.http.wire : http-outgoing-0 >> "{"id":null,"email":"xenoterracide@gmail.com","new":true}" 

ou apenas logging.level.org.apache.http.wire=DEBUG que parece conter todas as informações relevantes

Eu finalmente encontrei uma maneira de fazer isso da maneira certa. A maior parte da solução vem de Como configuro o Spring eo SLF4J para que eu possa obter o log?

Parece que há duas coisas que precisam ser feitas:

  1. Inclua a seguinte linha em log4j.properties: log4j.logger.httpclient.wire=DEBUG
  2. Certifique-se de que a mola não ignore sua configuração de registro

A segunda edição acontece principalmente para ambientes de primavera onde o slf4j é usado (como era o meu caso). Assim, quando o slf4j é usado, certifique-se de que as duas coisas a seguir aconteçam:

  1. Não existe uma biblioteca de logons comuns em seu caminho de class: isso pode ser feito adicionando os descritores de exclusão em seu pom:

       commons-logging commons-logging   
  2. O arquivo log4j.properties é armazenado em algum lugar no caminho de class em que a mola pode encontrá-lo / vê-lo. Se você tiver problemas com isso, uma solução de último recurso seria colocar o arquivo log4j.properties no pacote padrão (não é uma boa prática, mas apenas para ver se as coisas funcionam como esperado)

Nenhuma dessas respostas realmente resolve 100% do problema. mjj1409 obtém a maior parte, mas convenientemente evita o problema de registrar a resposta, o que demanda um pouco mais de trabalho. Paul Sabou fornece uma solução que parece realista, mas não fornece detalhes suficientes para implementar (e não funcionou para mim). Sofiene obteve o registro, mas com um problema crítico: a resposta não é mais legível porque o stream de input já foi consumido!

Recomendo usar um BufferingClientHttpResponseWrapper para agrupar o object de resposta para permitir a leitura do corpo da resposta várias vezes:

 public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor { private static final Logger logger = LoggerFactory.getLogger(LoggingRequestInterceptor.class); @Override public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException { ClientHttpResponse response = execution.execute(request, body); response = log(request, body, response); return response; } private ClientHttpResponse log(final HttpRequest request, final byte[] body, final ClientHttpResponse response) { final ClientHttpResponse responseCopy = new BufferingClientHttpResponseWrapper(response); logger.debug("Method: ", request.getMethod().toString()); logger.debug("URI: ", , request.getURI().toString()); logger.debug("Request Body: " + new String(body)); logger.debug("Response body: " + IOUtils.toString(responseCopy.getBody())); return responseCopy; } } 

Isso não consumirá o InputStream porque o corpo da resposta é carregado na memory e pode ser lido várias vezes. Se você não tiver o BufferingClientHttpResponseWrapper no seu classpath, você pode encontrar a implementação simples aqui:

https://github.com/spring-projects/spring-android/blob/master/spring-android-rest-template/src/main/java/org/springframework/http/client/BufferingClientHttpResponseWrapper.java

Para configurar o RestTemplate:

 LoggingRequestInterceptor loggingInterceptor = new LoggingRequestInterceptor(); restTemplate.getInterceptors().add(loggingInterceptor); 

A solução dada pela xenoterracida para usar

 logging.level.org.apache.http=DEBUG 

é bom, mas o problema é que, por padrão, o Apache HttpComponents não é usado.

Para usar o Apache HttpComponents, adicione ao seu pom.xml

  org.apache.httpcomponents httpasyncclient  

e configure o RestTemplate com:

 RestTemplate restTemplate = new RestTemplate(); restTemplate.setRequestFactory(new HttpComponentsAsyncClientHttpRequestFactory()); 

Sua melhor aposta é adicionar logging.level.org.springframework.web.client.RestTemplate=DEBUG ao arquivo application.properties .

Outras soluções, como a configuração de log4j.logger.httpclient.wire , nem sempre funcionarão, pois supõem que você use log4j e Apache HttpClient , o que nem sempre é verdade.

Observe, no entanto, que essa syntax funcionará apenas nas versões mais recentes do Spring Boot.

Além do log HttpClient descrito na outra resposta , você também pode introduzir um ClientHttpRequestInterceptor que lê o corpo da solicitação e a resposta e registra-o. Você pode querer fazer isso se outras coisas também usarem o HttpClient, ou se você quiser um formato de registro customizado. Cuidado: você vai querer dar ao RestTemplate um BufferingClientHttpRequestFactory de tal forma que você possa ler a resposta duas vezes.

Registrando RestTemplate

Opção 1. Abra o log de debugging.

Configurar o RestTemplate

  • Por padrão, o RestTemplate depende dos resources padrão do JDK para estabelecer conexões HTTP. Você pode alternar para usar uma biblioteca HTTP diferente, como o Apache HttpComponents

    @Bean public RestTemplate restTemplate (construtor RestTemplateBuilder) {RestTemplate restTemplate = builder.build (); return restTemplate; }

Configurar logging

  • application.yml

    logging: level: org.springframework.web.client.RestTemplate: DEBUG

Opção 2. Usando o Interceptor

Resposta do Invólucro

 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 final class BufferingClientHttpResponseWrapper implements ClientHttpResponse { private final ClientHttpResponse response; private byte[] body; BufferingClientHttpResponseWrapper(ClientHttpResponse response) { this.response = response; } public HttpStatus getStatusCode() throws IOException { return this.response.getStatusCode(); } public int getRawStatusCode() throws IOException { return this.response.getRawStatusCode(); } public String getStatusText() throws IOException { return this.response.getStatusText(); } public HttpHeaders getHeaders() { return this.response.getHeaders(); } public InputStream getBody() throws IOException { if (this.body == null) { this.body = StreamUtils.copyToByteArray(this.response.getBody()); } return new ByteArrayInputStream(this.body); } public void close() { this.response.close(); } } 

Implementar Interceptor

 package com.example.logging; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; 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; public class LoggingRestTemplate implements ClientHttpRequestInterceptor { private final static Logger LOGGER = LoggerFactory.getLogger(LoggingRestTemplate.class); @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { traceRequest(request, body); ClientHttpResponse response = execution.execute(request, body); return traceResponse(response); } private void traceRequest(HttpRequest request, byte[] body) throws IOException { if (!LOGGER.isDebugEnabled()) { return; } LOGGER.debug( "==========================request begin=============================================="); LOGGER.debug("URI : {}", request.getURI()); LOGGER.debug("Method : {}", request.getMethod()); LOGGER.debug("Headers : {}", request.getHeaders()); LOGGER.debug("Request body: {}", new String(body, "UTF-8")); LOGGER.debug( "==========================request end================================================"); } private ClientHttpResponse traceResponse(ClientHttpResponse response) throws IOException { if (!LOGGER.isDebugEnabled()) { return response; } final ClientHttpResponse responseWrapper = new BufferingClientHttpResponseWrapper(response); StringBuilder inputStringBuilder = new StringBuilder(); BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(responseWrapper.getBody(), "UTF-8")); String line = bufferedReader.readLine(); while (line != null) { inputStringBuilder.append(line); inputStringBuilder.append('\n'); line = bufferedReader.readLine(); } LOGGER.debug( "==========================response begin============================================="); LOGGER.debug("Status code : {}", responseWrapper.getStatusCode()); LOGGER.debug("Status text : {}", responseWrapper.getStatusText()); LOGGER.debug("Headers : {}", responseWrapper.getHeaders()); LOGGER.debug("Response body: {}", inputStringBuilder.toString()); LOGGER.debug( "==========================response end==============================================="); return responseWrapper; } } 

Configurar o RestTemplate

 @Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { RestTemplate restTemplate = builder.build(); restTemplate.setInterceptors(Collections.singletonList(new LoggingRestTemplate())); return restTemplate; } 

Configurar logging

  • Verifique o pacote de LoggingRestTemplate, por exemplo em application.yml :

    logging: level: com.example.logging: DEBUG

Opção 3. Usando httpcomponent

Importar dependência httpcomponent

  org.apache.httpcomponents httpasyncclient 

Configurar o RestTemplate

 @Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { RestTemplate restTemplate = builder.build(); restTemplate.setRequestFactory(new HttpComponentsAsyncClientHttpRequestFactory()); return restTemplate; } 

Configurar logging

  • Verifique o pacote de LoggingRestTemplate, por exemplo em application.yml :

    logging: level: org.apache.http: DEBUG

Supondo que o RestTemplate está configurado para usar o HttpClient 4.x, você pode ler a documentação de registro do HttpClient aqui . Os registradores são diferentes daqueles especificados nas outras respostas.

A configuração de registro para o HttpClient 3.x está disponível aqui .

Você pode usar o spring-rest-template-logger para registrar o tráfego HTTP RestTemplate .

Adicione uma dependência ao seu projeto Maven:

  org.hobsoft.spring spring-rest-template-logger 1.0.0  

Em seguida, personalize seu RestTemplate seguinte maneira:

 RestTemplate restTemplate = new RestTemplateBuilder() .customizers(new LoggingCustomizer()) .build() 

Agora todo o tráfego HTTP RestTemplate será registrado em org.hobsoft.spring.resttemplatelogger.LoggingCustomizer no nível de debugging.

ISENÇÃO DE RESPONSABILIDADE: Eu escrevi esta biblioteca.

O truque de configurar seu RestTemplate com um BufferingClientHttpRequestFactory não funciona se você estiver usando qualquer ClientHttpRequestInterceptor , o que você fará se estiver tentando fazer o log através de interceptores. Isso se deve à maneira como o InterceptingHttpAccessor (que subclasss do RestTemplate ) funciona.

Resumindo a história … use esta class no lugar do RestTemplate (observe que isso usa a API de log do SLF4J, edite conforme necessário):

 import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import javax.annotation.PostConstruct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.web.client.RestTemplate; /** * A {@link RestTemplate} that logs every request and response. */ public class LoggingRestTemplate extends RestTemplate { // Bleh, this class is not public private static final String RESPONSE_WRAPPER_CLASS = "org.springframework.http.client.BufferingClientHttpResponseWrapper"; private Logger log = LoggerFactory.getLogger(this.getClass()); private boolean hideAuthorizationHeaders = true; private Class< ?> wrapperClass; private Constructor< ?> wrapperConstructor; /** * Configure the logger to log requests and responses to. * * @param log log destination, or null to disable */ public void setLogger(Logger log) { this.log = log; } /** * Configure the logger to log requests and responses to by name. * * @param name name of the log destination, or null to disable */ public void setLoggerName(String name) { this.setLogger(name != null ? LoggerFactory.getLogger(name) : null); } /** * Configure whether to hide the contents of {@code Authorization} headers. * * 

* Default true. * * @param hideAuthorizationHeaders true to hide, otherwise false */ public void setHideAuthorizationHeaders(boolean hideAuthorizationHeaders) { this.hideAuthorizationHeaders = hideAuthorizationHeaders; } /** * Log a request. */ protected void traceRequest(HttpRequest request, byte[] body) { this.log.debug("xmit: {} {}\n{}{}", request.getMethod(), request.getURI(), this.toString(request.getHeaders()), body != null && body.length > 0 ? "\n\n" + new String(body, StandardCharsets.UTF_8) : ""); } /** * Log a response. */ protected void traceResponse(ClientHttpResponse response) { final ByteArrayOutputStream bodyBuf = new ByteArrayOutputStream(); HttpStatus statusCode = null; try { statusCode = response.getStatusCode(); } catch (IOException e) { // ignore } String statusText = null; try { statusText = response.getStatusText(); } catch (IOException e) { // ignore } try (final InputStream input = response.getBody()) { byte[] b = new byte[1024]; int r; while ((r = input.read(b)) != -1) bodyBuf.write(b, 0, r); } catch (IOException e) { // ignore } this.log.debug("recv: {} {}\n{}{}", statusCode, statusText, this.toString(response.getHeaders()), bodyBuf.size() > 0 ? "\n\n" + new String(bodyBuf.toByteArray(), StandardCharsets.UTF_8) : ""); } @PostConstruct private void addLoggingInterceptor() { this.getInterceptors().add(new ClientHttpRequestInterceptor() { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { // Log request if (LoggingRestTemplate.this.log != null && LoggingRestTemplate.this.log.isDebugEnabled()) LoggingRestTemplate.this.traceRequest(request, body); // Perform request ClientHttpResponse response = execution.execute(request, body); // Log response if (LoggingRestTemplate.this.log != null && LoggingRestTemplate.this.log.isDebugEnabled()) { final ClientHttpResponse bufferedResponse = LoggingRestTemplate.this.ensureBuffered(response); if (bufferedResponse != null) { LoggingRestTemplate.this.traceResponse(bufferedResponse); response = bufferedResponse; } } // Done return response; } }); } private ClientHttpResponse ensureBuffered(ClientHttpResponse response) { try { if (this.wrapperClass == null) this.wrapperClass = Class.forName(RESPONSE_WRAPPER_CLASS, false, ClientHttpResponse.class.getClassLoader()); if (!this.wrapperClass.isInstance(response)) { if (this.wrapperConstructor == null) { this.wrapperConstructor = this.wrapperClass.getDeclaredConstructor(ClientHttpResponse.class); this.wrapperConstructor.setAccessible(true); } response = (ClientHttpResponse)this.wrapperConstructor.newInstance(response); } return response; } catch (Exception e) { this.log.error("error creating {} instance: {}", RESPONSE_WRAPPER_CLASS, e); return null; } } private String toString(HttpHeaders headers) { final StringBuilder headerBuf = new StringBuilder(); for (Map.Entry> entry : headers.entrySet()) { if (headerBuf.length() > 0) headerBuf.append('\n'); final String name = entry.getKey(); for (String value : entry.getValue()) { if (this.hideAuthorizationHeaders && name.equalsIgnoreCase(HttpHeaders.AUTHORIZATION)) value = "[omitted]"; headerBuf.append(name).append(": ").append(value); } } return headerBuf.toString(); } }

Eu concordo que é bobagem que é preciso muito trabalho apenas para fazer isso.

Somando-se a discussão acima, isso representa apenas cenários felizes. provavelmente você não conseguirá registrar a resposta se chegar um erro .

Neste caso, além de todos os casos acima, você deve replace DefaultResponseErrorHandler e configurá-lo como abaixo

 restTemplate.setErrorHandler(new DefaultResponseErrorHandlerImpl()); 

Estranhamente, nenhuma dessas soluções funciona, pois o RestTemplate parece não retornar a resposta em alguns erros do cliente e do servidor 500x. Nesse caso, você terá que registrá-los também, implementando ResponseErrorHandler da seguinte maneira. Aqui está um código de rascunho, mas você entendeu:

Você pode definir o mesmo interceptor que o manipulador de erros:

 restTemplate.getInterceptors().add(interceptor); restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory())); restTemplate.setErrorHandler(interceptor); 

E a interceptação implementa as duas interfaces:

 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.HashSet; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus.Series; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.ResponseErrorHandler; public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor, ResponseErrorHandler { static final Logger log = LoggerFactory.getLogger(LoggingRequestInterceptor.class); static final DefaultResponseErrorHandler defaultResponseErrorHandler = new DefaultResponseErrorHandler(); final Set loggableStatuses = new HashSet(); public LoggingRequestInterceptor() { } public LoggingRequestInterceptor(Set loggableStatuses) { loggableStatuses.addAll(loggableStatuses); } public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { this.traceRequest(request, body); ClientHttpResponse response = execution.execute(request, body); if(response != null) { this.traceResponse(response); } return response; } private void traceRequest(HttpRequest request, byte[] body) throws IOException { 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 traceResponse(ClientHttpResponse response) throws IOException { if(this.loggableStatuses.isEmpty() || this.loggableStatuses.contains(response.getStatusCode().series())) { StringBuilder inputStringBuilder = new StringBuilder(); try { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), "UTF-8")); for(String line = bufferedReader.readLine(); line != null; line = bufferedReader.readLine()) { inputStringBuilder.append(line); inputStringBuilder.append('\n'); } } catch (Throwable var5) { log.error("cannot read response due to error", var5); } 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: {}", inputStringBuilder.toString()); log.debug("=======================response end================================================="); } } public boolean hasError(ClientHttpResponse response) throws IOException { return defaultResponseErrorHandler.hasError(response); } public void handleError(ClientHttpResponse response) throws IOException { this.traceResponse(response); defaultResponseErrorHandler.handleError(response); } } 

This might not be the correct way to do it, but I think this is the most simple approach to print requests and responses without filling too much in logs.

By adding below 2 lines application.properties logs all requests and responses 1st line in order to log the requests and 2nd line to log the responses.

logging.level.org.springframework.web.client.RestTemplate=DEBUG logging.level.org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor=DEBUG

Best solution now, just add dependency :

  com.github.zg2pro spring-rest-basis vx  

It contains a LoggingRequestInterceptor class you can add that way to your RestTemplate:

integrate this utility by adding it as an interceptor to a spring RestTemplate, in the following manner:

 restTemplate.setRequestFactory(LoggingRequestFactoryFactory.build()); 

and add an slf4j implementation to your framework like log4j.

or directly use “Zg2proRestTemplate” . The “best answer” by @PaulSabou looks so so, since httpclient and all apache.http libs are not necessarily loaded when using a spring RestTemplate.

Wanted to add my implementation of this as well. I apologize for all the missing semi-colons, this is written in Groovy.

I needed something more configurable than the accepted answer provided. Here’s a rest template bean that’s very agile and will log everything like the OP is looking for.

Custom Logging Interceptor Class:

 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.nio.charset.Charset class HttpLoggingInterceptor implements ClientHttpRequestInterceptor { private final static Logger log = LoggerFactory.getLogger(HttpLoggingInterceptor.class) @Override 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=================================================") } } } 

Rest Template Bean Definition:

 @Bean(name = 'myRestTemplate') RestTemplate myRestTemplate(RestTemplateBuilder builder) { RequestConfig requestConfig = RequestConfig.custom() .setConnectTimeout(10 * 1000) // 10 seconds .setSocketTimeout(300 * 1000) // 300 seconds .build() PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager() connectionManager.setMaxTotal(10) connectionManager.closeIdleConnections(5, TimeUnit.MINUTES) CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(connectionManager) .setDefaultRequestConfig(requestConfig) .disableRedirectHandling() .build() RestTemplate restTemplate = builder .rootUri("https://domain.server.com") .basicAuthorization("username", "password") .requestFactory(new BufferingClientHttpRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient))) .interceptors(new HttpLoggingInterceptor()) .build() return restTemplate } 

Implementação:

 @Component class RestService { private final RestTemplate restTemplate private final static Logger log = LoggerFactory.getLogger(RestService.class) @Autowired RestService( @Qualifier("myRestTemplate") RestTemplate restTemplate ) { this.restTemplate = restTemplate } // add specific methods to your service that access the GET and PUT methods private  T getForObject(String path, Class object, Map params = [:]) { try { return restTemplate.getForObject(path, object, params) } catch (HttpClientErrorException e) { log.warn("Client Error (${path}): ${e.responseBodyAsString}") } catch (HttpServerErrorException e) { String msg = "Server Error (${path}): ${e.responseBodyAsString}" log.error(msg, e) } catch (RestClientException e) { String msg = "Error (${path})" log.error(msg, e) } return null } private  T putForObject(String path, T object) { try { HttpEntity request = new HttpEntity<>(object) HttpEntity response = restTemplate.exchange(path, HttpMethod.PUT, request, T) return response.getBody() } catch (HttpClientErrorException e) { log.warn("Error (${path}): ${e.responseBodyAsString}") } catch (HttpServerErrorException e) { String msg = "Error (${path}): ${e.responseBodyAsString}" log.error(msg, e) } catch (RestClientException e) { String msg = "Error (${path})" log.error(msg, e) } return null } } 

Refer the Q/A for logging the request and response for the rest template by enabling the multiple reads on the HttpInputStream

Why my custom ClientHttpRequestInterceptor with empty response

Related to the response using ClientHttpInterceptor, I found a way of keeping the whole response without Buffering factories. Just store the response body input stream inside byte array using some utils method that will copy that array from body, but important, surround this method with try catch because it will break if response is empty (that is the cause of Resource Access Exception) and in catch just create empty byte array, and than just create anonymous inner class of ClientHttpResponse using that array and other parameters from the original response. Than you can return that new ClientHttpResponse object to the rest template execution chain and you can log response using body byte array that is previously stored. That way you will avoid consuming InputStream in the actual response and you can use Rest Template response as it is. Note, this may be dangerous if your’s response is too big

my logger config used xml

    

then you will get something like below:

 DEBUG org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:92) : Reading [com.test.java.MyClass] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@604525f1] 

through HttpMessageConverterExtractor.java:92,you need continue to debug,and in my case,i got this:

 genericMessageConverter.write(requestBody, requestBodyType, requestContentType, httpRequest); 

e isto:

 outputMessage.getBody().flush(); 

outputMessage.getBody() contains the message http(post type) sends