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:
- Agregue la siguiente línea en log4j.properties:
log4j.logger.httpclient.wire=DEBUG
- 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:
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>
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í:
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.
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
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