我已经使用Spring RestTemplate有一段时间了,当我试图调试它的请求和响应时,我总是碰壁。我基本上希望看到与打开“verbose”选项时使用curl时相同的东西。例如:

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

将显示发送的数据和接收的数据(包括头、cookie等)。

我看了一些相关的帖子,比如: 如何在Spring RestTemplate中记录响应? 但我还没能解决这个问题。

实现这一点的一种方法是实际更改RestTemplate源代码,并在那里添加一些额外的日志记录语句,但我认为这种方法确实是最后的办法。应该有某种方法告诉Spring Web Client/RestTemplate以一种更友好的方式记录所有内容。

我的目标是能够用如下代码做到这一点:

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

然后在日志文件或控制台中获得相同类型的调试信息(就像我使用curl获得的一样)。 我相信这对于任何使用Spring RestTemplate并且遇到问题的人来说都是非常有用的。使用curl来调试RestTemplate问题是行不通的(在某些情况下)。


当前回答

在Apache HttpClient的帮助下登录Logback:

在类路径中需要Apache HttpClient:

<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpclient</artifactId>
  <version>4.5.10</version>
</dependency>

配置您的RestTemplate以使用HttpClient:

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

要记录请求和响应,请添加到Logback配置文件:

<logger name="org.apache.http.wire" level="DEBUG"/>

或者记录更多:

<logger name="org.apache.http" level="DEBUG"/>

其他回答

用一些代码扩展@hstoerr answer:


创建LoggingRequestInterceptor来记录请求响应

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

设置创建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());

假设RestTemplate配置为使用HttpClient 4。x,你可以在这里阅读HttpClient的日志文档。这些记录器与其他答案中指定的记录器不同。

HttpClient 3的日志配置。X在这里可用。

现在最好的解决方案,只需添加依赖项:

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

它包含一个LoggingRequestInterceptor类,你可以这样添加到你的RestTemplate:

通过将它作为拦截器添加到spring RestTemplate中来集成这个实用程序,方法如下:

restTemplate.setRequestFactory(LoggingRequestFactoryFactory.build());

并将slf4j实现添加到您的框架,如log4j。

或直接使用“Zg2proRestTemplate”。@PaulSabou的“最佳答案”看起来一般,因为httpclient和所有apache。使用spring RestTemplate时,不一定会加载http库。

这些答案都不能完全解决问题。Mjj1409实现了大部分功能,但方便地避免了记录响应的问题,因为这需要更多的工作。Paul Sabou提供了一个看似现实的解决方案,但没有提供足够的细节来实际实现(对我来说根本没用)。Sofiene得到了日志记录,但遇到了一个严重的问题:响应不再可读,因为输入流已经被消耗了!

我建议使用BufferingClientHttpResponseWrapper来包装响应对象,以允许多次读取响应体:

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

}

这将不会消耗InputStream,因为响应体被加载到内存中,可以多次读取。如果你的类路径上没有BufferingClientHttpResponseWrapper,你可以在这里找到简单的实现:

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

设置RestTemplate:

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

奇怪的是,这些解决方案都不能工作,因为RestTemplate似乎不会在某些客户机和服务器上返回500x错误的响应。在这种情况下,您还必须通过实现ResponseErrorHandler来记录这些日志,如下所示。这是一个代码草案,但你明白了:

你可以设置与错误处理程序相同的拦截器:

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

拦截实现了两个接口:

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