the paises existe donde depuracion java debugging logging resttemplate

java - the - paises donde existe la depuracion



Spring RestTemplate: ¿cómo habilitar la depuración completa/registro de solicitudes/respuestas? (19)

He estado usando la Spring RestTemplate por un tiempo y siempre choco contra una pared cuando intento depurar sus solicitudes y respuestas. Básicamente busco ver las mismas cosas que veo cuando uso curl con la opción "verbose" activada. Por ejemplo :

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

Mostraría tanto los datos enviados como los recibidos (incluidos los encabezados, las cookies, etc.).

He revisado algunas publicaciones relacionadas como: ¿Cómo registro la respuesta en Spring RestTemplate? pero no he logrado resolver este problema.

Una forma de hacer esto sería cambiar realmente el código fuente de RestTemplate y agregar algunas declaraciones de registro adicionales allí, pero me gustaría encontrar este enfoque como un último recurso. Debería haber alguna forma de decirle a Spring Web Client / RestTemplate que registre todo de una manera mucho más amigable.

Mi objetivo sería poder hacer esto con un código como:

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

y luego para obtener el mismo tipo de información de depuración (como lo hago con curl) en el archivo de registro o en la consola. Creo que esto sería extremadamente útil para cualquiera que use Spring RestTemplate y tenga problemas. Usar curl para depurar los problemas de RestTemplate simplemente no funciona (en algunos casos).


Logging RestTemplate

Opción 1. Abra el registro de depuración.

Configura RestTemplate

  • Por defecto, RestTemplate depende de las instalaciones estándar de JDK para establecer conexiones HTTP. Puede cambiar para usar una biblioteca HTTP diferente, como Apache HttpComponents

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

Configurar el registro

  • application.yml

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

Opción 2. Usando el interceptor

Respuesta de Wrapper

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; } }

Configura RestTemplate

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

Configurar el registro

  • Verifique el paquete de LoggingRestTemplate, por ejemplo en application.yml :

    logging: level: com.example.logging: DEPURACIÓN

Opción 3. Usando httpcomponent

Importar dependencia de componentes http

<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpasyncclient</artifactId>

Configura RestTemplate

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

Configurar el registro

  • Verifique el paquete de LoggingRestTemplate, por ejemplo en application.yml :

    logging: level: org.apache.http: DEBUG


Además del registro HttpClient descrito en la otra respuesta , también puede introducir un ClientHttpRequestInterceptor que lee el cuerpo de la solicitud y la respuesta y lo registra. Es posible que desee hacer esto si otras cosas también usan el HttpClient o si desea un formato de registro personalizado. Precaución: querrá darle a RestTemplate un BufferingClientHttpRequestFactory para que pueda leer la respuesta dos veces.


Agregar a la discusión anterior esto solo representa escenarios felices. probablemente no podrá registrar la respuesta si aparece un error .

En este caso más todos los casos anteriores, debe anular DefaultResponseErrorHandler y configurarlo como se indica a continuación

restTemplate.setErrorHandler(new DefaultResponseErrorHandlerImpl());


El truco de configurar su RestTemplate con una BufferingClientHttpRequestFactory no funciona si está utilizando ClientHttpRequestInterceptor , que lo hará si está intentando iniciar sesión a través de interceptores. Esto se debe a la forma en que InterceptingHttpAccessor (qué subclases RestTemplate ) funciona.

Para RestTemplate simplemente use esta clase en lugar de RestTemplate (tenga en cuenta que usa la API de registro de SLF4J, edítela según sea necesario):

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. * * <p> * 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<String, List<String>> 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(); } }

Estoy de acuerdo en que es una tontería que se requiera tanto trabajo solo para hacer esto.


Extendiendo @hstoerr respuesta con algún código:

Crear LoggingRequestInterceptor para registrar respuestas de solicitudes

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 } }

Configurar RestTemplate

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


Extrañamente, ninguna de estas soluciones funciona, ya que RestTemplate no parece devolver la respuesta en algunos errores 500x del cliente y del servidor. En ese caso, también tendrá que iniciar sesión implementando ResponseErrorHandler de la siguiente manera. Aquí hay un código de borrador, pero entiendes el punto:

Puede configurar el mismo interceptor que el controlador de errores:

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

Y el intercepto implementa ambas 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<Series> loggableStatuses = new HashSet(); public LoggingRequestInterceptor() { } public LoggingRequestInterceptor(Set<Series> 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); } }


Finalmente encontré la manera de hacer esto de la manera correcta. La mayoría de la solución proviene de ¿Cómo configuro Spring y SLF4J para que pueda obtener el registro?

Parece que hay dos cosas que deben hacerse:

  1. Agregue la siguiente línea en log4j.properties: log4j.logger.httpclient.wire=DEBUG
  2. Asegúrate de que Spring no ignore tu configuración de registro

El segundo problema ocurre principalmente en los entornos de primavera donde se usa slf4j (como fue mi caso). Como tal, cuando se usa slf4j asegúrese de que ocurran las dos cosas siguientes:

  1. No hay una biblioteca de registro de recursos comunes en su classpath: esto se puede hacer agregando los descriptores de exclusión en su pom:

    <exclusions><exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions>

  2. El archivo log4j.properties se almacena en algún lugar de la ruta de clases donde Spring puede encontrarlo / verlo. Si tiene problemas con esto, una solución de último recurso sería colocar el archivo log4j.properties en el paquete predeterminado (no es una buena práctica, solo para ver que las cosas funcionen como espera)


La solución dada por xenoterracide para usar

logging.level.org.apache.http=DEBUG

es bueno, pero el problema es que, por defecto, Apache HttpComponents no se usa.

Para usar Apache, HttpComponents agrega a tu pom.xml

<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpasyncclient</artifactId> </dependency>

y configure RestTemplate con:

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


Ninguna de estas respuestas realmente resuelve el 100% del problema. mjj1409 obtiene la mayor parte, pero evita convenientemente el problema de registrar la respuesta, lo que requiere un poco más de trabajo. Paul Sabou ofrece una solución que parece realista, pero no proporciona los detalles suficientes para implementarla realmente (y no funcionó en absoluto para mí). Sofiene consiguió el registro pero con un problema crítico: ¡la respuesta ya no es legible porque el flujo de entrada ya se ha consumido!

Recomiendo usar un BufferingClientHttpResponseWrapper para envolver el objeto de respuesta para permitir leer el cuerpo de la respuesta varias veces:

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; } }

Esto no consumirá el InputStream porque el cuerpo de la respuesta se carga en la memoria y se puede leer varias veces. Si no tiene el BufferingClientHttpResponseWrapper en su classpath, puede encontrar la implementación simple aquí:

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

Para configurar RestTemplate:

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


Solo para completar el ejemplo con una implementación completa de ClientHttpRequestInterceptor para rastrear solicitud y respuesta:

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================================================="); } }

A continuación, RestTemplate una instancia de RestTemplate utilizando una BufferingClientHttpRequestFactory y LoggingRequestInterceptor :

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

Se requiere BufferingClientHttpRequestFactory porque queremos usar el cuerpo de respuesta tanto en el interceptor como para el código de llamada inicial. La implementación predeterminada permite leer el cuerpo de la respuesta solo una vez.


Su mejor logging.level.org.springframework.web.client.RestTemplate=DEBUG es agregar logging.level.org.springframework.web.client.RestTemplate=DEBUG al archivo application.properties .

Otras soluciones, como la configuración de log4j.logger.httpclient.wire , no siempre funcionarán, ya que suponen que utiliza log4j y Apache HttpClient , lo que no siempre es cierto.

Sin embargo, tenga en cuenta que esta sintaxis solo funcionará en las últimas versiones de Spring Boot.


Suponiendo que RestTemplate está configured para usar HttpClient 4.x, aquí puede leer la documentación de registro de HttpClient. Los registradores son diferentes a los especificados en las otras respuestas.

La configuración de registro para HttpClient 3.x está disponible here .


en Spring Boot puede obtener la solicitud / respuesta completa configurando esto en propiedades (u otro método de factor 12)

logging.level.org.apache.http=DEBUG

esta salida

-DEBUG .i.c.DefaultHttpClientConnectionOperator : Connecting to localhost/127.0.0.1:41827 -DEBUG .i.c.DefaultHttpClientConnectionOperator : Connection established 127.0.0.1:39546<->127.0.0.1:41827 -DEBUG o.a.http.impl.execchain.MainClientExec : Executing request POST /v0/users HTTP/1.1 -DEBUG o.a.http.impl.execchain.MainClientExec : Target auth state: UNCHALLENGED -DEBUG o.a.http.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":"[email protected]","new":true}"

y respuesta

-DEBUG .i.c.DefaultHttpClientConnectionOperator : Connecting to localhost/127.0.0.1:41827 -DEBUG .i.c.DefaultHttpClientConnectionOperator : Connection established 127.0.0.1:39546<->127.0.0.1:41827 -DEBUG o.a.http.impl.execchain.MainClientExec : Executing request POST /v0/users HTTP/1.1 -DEBUG o.a.http.impl.execchain.MainClientExec : Target auth state: UNCHALLENGED -DEBUG o.a.http.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":"[email protected]","new":true}"

o simplemente logging.level.org.apache.http.wire=DEBUG que parece contener toda la información relevante


Best solution now, just add dependency :

<dependency> <groupId>com.github.zg2pro</groupId> <artifactId>spring-rest-basis</artifactId> <version>v.x</version> </dependency>

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.



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


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 }

Implementation:

@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> T getForObject(String path, Class<T> object, Map<String, ?> 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> T putForObject(String path, T object) { try { HttpEntity<T> request = new HttpEntity<>(object) HttpEntity<T> 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 } }


You can use spring-rest-template-logger to log RestTemplate HTTP traffic.

Add a dependency to your Maven project:

<dependency> <groupId>org.hobsoft.spring</groupId> <artifactId>spring-rest-template-logger</artifactId> <version>1.0.0</version> </dependency>

Then customize your RestTemplate as follows:

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

Now all RestTemplate HTTP traffic will be logged to org.hobsoft.spring.resttemplatelogger.LoggingCustomizer at debug level.

DISCLAIMER: I wrote this library.


mi configuración de registrador usó xml

<logger name="org.springframework.web.client.RestTemplate"> <level value="trace"/> </logger>

entonces obtendrás algo como a continuación:

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]

a través de HttpMessageConverterExtractor.java:92, necesita seguir depurando, y en mi caso, obtuve esto:

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

y esto:

outputMessage.getBody().flush();

outputMessage.getBody () contiene el mensaje http (tipo de mensaje) envía