Spring RestTemplate - 如何启用请求/响应的完整调试/记录?

Pau*_*bou 198 java debugging logging resttemplate

我一直在使用Spring RestTemplate一段时间,当我试图调试它的请求和响应时,我一直碰壁.我基本上希望看到当我使用curl并打开"详细"选项时看到的相同内容.例如 :

curl -v http://twitter.com/statuses/public_timeline.rss
Run Code Online (Sandbox Code Playgroud)

将显示发送的数据和接收的数据(包括标题,cookie等).

我检查了一些相关的帖子,如: 我如何在Spring RestTemplate中记录响应? 但我没有设法解决这个问题.

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

我的目标是能够使用以下代码执行此操作:

restTemplate.put("http://someurl", objectToPut, urlPathValues);
Run Code Online (Sandbox Code Playgroud)

然后在日志文件或控制台中获取相同类型的调试信息(我使用curl).我相信这对使用Spring RestTemplate且有问题的任何人都非常有用.使用curl调试RestTemplate问题不起作用(在某些情况下).

sof*_*udi 186

只是为了完成ClientHttpRequestInterceptor跟踪请求和响应的完整实现的示例:

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.info("===========================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.info("==========================request end================================================");
    }

    private void traceResponse(ClientHttpResponse response) throws IOException {
        StringBuilder inputStringBuilder = new StringBuilder();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), "UTF-8"));
        String line = bufferedReader.readLine();
        while (line != null) {
            inputStringBuilder.append(line);
            inputStringBuilder.append('\n');
            line = bufferedReader.readLine();
        }
        log.info("============================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.info("=======================response end=================================================");
    }

}
Run Code Online (Sandbox Code Playgroud)

然后RestTemplate使用a BufferingClientHttpRequestFactoryLoggingRequestInterceptor:实例化:

RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(new LoggingRequestInterceptor());
restTemplate.setInterceptors(interceptors);
Run Code Online (Sandbox Code Playgroud)

BufferingClientHttpRequestFactory是必需的,因为我们想要在拦截器和初始调用代码中使用响应体.默认实现只允许读取响应主体一次.

  • 我们给RestTemplate一个BufferingClientHttpRequestFactory,所以我们可以读两次响应. (21认同)
  • 这是错的.如果您读取流,则应用程序代码将无法读取响应. (19认同)
  • 我们现在已经使用这种技术大约3个月了.它只适用于配置了`BuffingClientHttpResponseWrapper`的RestTemplate,如@sofienezaghdoudi暗示的那样.但是,当在使用spring的mockServer框架的测试中使用它时它不起作用,因为`MockRestServiceServer.createServer(restTemplate)`将RequestFactory覆盖为`InterceptingClientHttpRequestFactory`. (14认同)
  • 技术很好,实施是错误的.404 case,response.getBody()抛出IOException - >你永远不会得到日志,甚至最糟糕的是,它会在你的进一步代码中变成ResourceAccessException,而不是RestClientResponseException (6认同)
  • 谢谢回复.但这是一个不好的做法,有多个"log.debug",因为它可以分布在很多其他日志上.最好使用一个log.debug指令,这样你就可以确定所有内容都在同一个地方 (4认同)
  • 新的RestTemplate(新的BufferingClientHttpRequestFactory(新的HttpComponentsClientHttpRequestFactory()))对我来说效果更好,只要记录非成功响应也是如此。注意,您需要将org.apache.httpcomponents:httpclient添加到依赖项中 (3认同)
  • 看到这里的所有建议和评论有点有趣,有人似乎复制了上述密码锁,股票和桶,并在没有署名的情况下将其传递出去。哦,等等,他们将 C 风格的日志记录从“信息”更改为“调试” - 称其为好!#sad https://objectpartners.com/2018/03/01/log-your-resttemplate-request-and-response-without-destroying-the-body/ (2认同)
  • 对于在构建后使用 `RestTemplateBuilder` 的人来说,更好地使用 `restTemplate.getInterceptors().add(new LoggingRequestInterceptor());` 只是为了避免丢失其他拦截器,例如 auth,...。 (2认同)

xen*_*ide 113

在Spring Boot中,你可以通过在属性中设置它(或其他12因子方法)来获得完整的请求/响应

logging.level.org.apache.http=DEBUG
Run Code Online (Sandbox Code Playgroud)

这个输出

-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":"xenoterracide@gmail.com","new":true}"
Run Code Online (Sandbox Code Playgroud)

和回应

-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":"xenoterracide@gmail.com","new":true}"
Run Code Online (Sandbox Code Playgroud)

或者只是logging.level.org.apache.http.wire=DEBUG似乎包含所有相关信息

  • 根据[RestTemplate]的javadoc(http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html):`默认情况下RestTemplate依赖在标准JDK工具上建立HTTP连接.您可以切换到使用不同的HTTP库,例如Apache HttpComponents` (19认同)
  • 正如@OrtomalaLokni所指出的,RestTemplate默认不使用这些Apache类,因此除了在使用它们时如何打印调试之外,还应该包括*how*如何使用它们. (15认同)
  • 这没有按预期工作! (6认同)
  • 这是做我想要的最简单的事情.我非常鼓励在接受的答案中加入这一点. (2认同)
  • @ParthaSarathiGhosh 内容可能是 gzip 编码的,这就是您看不到原始文本的原因。 (2认同)
  • 如果您的应用程序配置为使用 Apache,这个简单的解决方案就有效 (2认同)

mjj*_*409 77

使用一些代码扩展@hstoerr答案:


创建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
    }
}
Run Code Online (Sandbox Code Playgroud)

设置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());
Run Code Online (Sandbox Code Playgroud)

  • @PavelNiedoba BufferClientHttpRequestFactory允许多次读取响应. (11认同)
  • 它没有回答"记录响应"的问题,而是留下//做记录注释. (2认同)
  • 如果您需要在数据库中存储有关请求/响应的信息以进行调试并且常规日志记录不适合您的需要,则此方法很有效. (2认同)

Jam*_*ins 29

这些答案都没有真正解决100%的问题.mjj1409获得了大部分内容,但是方便地避免了记录响应的问题,这需要更多的工作.保罗萨布提供了一个看似现实的解决方案,但没有提供足够的细节来实际实施(而且它对我来说根本不起作用).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;
    }

}
Run Code Online (Sandbox Code Playgroud)

这不会消耗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);
Run Code Online (Sandbox Code Playgroud)

  • 但它是包私有的.你把LoggingRequestInterceptor放在包'org.springframework.http.client'中了吗? (4认同)
  • 为什么?错误消息也可以具有主体 (2认同)
  • 那么`asyncRestTemplate`呢?当你拦截它时,它需要返回一个`ListenableFuture`,这在回调中不能用`BufferingClientHttpResponseWrapper`改变. (2认同)

Ort*_*kni 27

xenoterracide给出的解决方案

logging.level.org.apache.http=DEBUG
Run Code Online (Sandbox Code Playgroud)

很好,但问题是默认情况下没有使用Apache HttpComponents.

要使用Apache HttpComponents,请添加到您的pom.xml

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpasyncclient</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)

并配置RestTemplate:

RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new HttpComponentsAsyncClientHttpRequestFactory());
Run Code Online (Sandbox Code Playgroud)


gam*_*ela 25

最好的办法是添加logging.level.org.springframework.web.client.RestTemplate=DEBUGapplication.properties文件中.

像设置这样的其他解决方案log4j.logger.httpclient.wire并不总是有效,因为他们假设你使用log4j和Apache HttpClient,这并不总是正确的.

但请注意,此语法仅适用于最新版本的Spring Boot.

  • 这不记录请求和响应正文,只记录url和请求类型(spring-web-4.2.6) (5认同)

Pau*_*bou 24

我终于找到了以正确的方式做到这一点的方法.大多数解决方案来自 如何配置Spring和SLF4J以便我可以获取日志记录?

似乎有两件事需要做:

  1. 在log4j.properties中添加以下行: log4j.logger.httpclient.wire=DEBUG
  2. 确保spring不会忽略您的日志配置

第二个问题主要发生在使用slf4j的弹簧环境中(就像我的情况一样).因此,当使用slf4j时,请确保发生以下两件事:

  1. 您的类路径中没有公共日志记录库:这可以通过在您的pom中添加排除描述符来完成:

            <exclusions><exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
    
    Run Code Online (Sandbox Code Playgroud)
  2. log4j.properties文件存储在类路径中的某个位置,spring可以在其中找到/查看它.如果您遇到此问题,最后的解决方案是将log4j.properties文件放在默认包中(这不是一个好习惯,只是为了看到事情按预期工作)

  • `httpclient.wire`实际上来自Apache HttpComponents HttpClient库(参见https://hc.apache.org/httpcomponents-client-ga/logging.html).只有将`RestTemplate`配置为使用`HttpComponentsClientHttpRequestFactory`时,此技术才有效. (10认同)
  • 这对我不起作用,我做了两件事.我不明白为什么在我的项目中没有使用它时需要放入log4j.properties(由mvn依赖:tree检查) (7认同)

Mar*_*son 22

您可以使用spring-rest-template-logger记录RestTemplateHTTP流量.

为Maven项目添加依赖项:

<dependency>
    <groupId>org.hobsoft.spring</groupId>
    <artifactId>spring-rest-template-logger</artifactId>
    <version>2.0.0</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

然后RestTemplate按如下方式自定义:

RestTemplate restTemplate = new RestTemplateBuilder()
    .customizers(new LoggingCustomizer())
    .build()
Run Code Online (Sandbox Code Playgroud)

现在,所有RestTemplate HTTP流量都将记录到org.hobsoft.spring.resttemplatelogger.LoggingCustomizer调试级别.

免责声明:我写了这个库.

  • 很高兴它帮助@RaffaelBecharaRameh.它最初被downvoted,因为我没有嵌入链接项目的指令.如果您发现它有用,请随意提升! (3认同)

小智 19

记录RestTemplate

选项1.打开调试日志记录.

配置RestTemplate

  • 默认情况下,RestTemplate依赖于标准JDK工具来建立HTTP连接.您可以切换到使用不同的HTTP库,例如Apache HttpComponents

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

配置日志记录

  • application.yml

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

选项2.使用拦截器

包装响应

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

实现拦截器

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

}
Run Code Online (Sandbox Code Playgroud)

配置RestTemplate

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

配置日志记录

  • 检查LoggingRestTemplate的包,例如application.yml:

    logging:level:com.example.logging:DEBUG

选项3.使用httpcomponent

导入httpcomponent依赖项

<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpasyncclient</artifactId>
Run Code Online (Sandbox Code Playgroud)

配置RestTemplate

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
    RestTemplate restTemplate = builder.build();
    restTemplate.setRequestFactory(new HttpComponentsAsyncClientHttpRequestFactory());
    return restTemplate;
}
Run Code Online (Sandbox Code Playgroud)

配置日志记录

  • 检查LoggingRestTemplate的包,例如application.yml:

    logging:level:org.apache.http:DEBUG


Han*_*örr 11

除了在另一个答案中描述的HttpClient日志记录之外,您还可以引入ClientHttpRequestInterceptor,它读取请求的主体和响应并记录它.如果其他内容也使用HttpClient,或者您需要自定义日志记录格式,则可能需要执行此操作.警告:您需要为RestTemplate提供一个BufferingClientHttpRequestFactory,以便您可以两次读取响应.


Pet*_*ser 9

如其他响应中所述,响应主体需要特殊处理,因此可以重复读取(默认情况下,其内容在第一次读取时就被消耗掉)。

BufferingClientHttpRequestFactory拦截器本身可以包装响应并确保内容被保留并可以重复读取(由记录程序以及响应的使用者使用),而不是使用设置请求时的:

我的拦截器

  • 使用包装器缓冲响应主体
  • 更紧凑的方式登录
  • 记录状态代码标识符(例如201已创建)
  • 包括一个请求序列号,可轻松区分多个线程中的并发日志条目

码:

public class LoggingInterceptor implements ClientHttpRequestInterceptor {

    private final Logger log = LoggerFactory.getLogger(getClass());
    private AtomicInteger requestNumberSequence = new AtomicInteger(0);

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        int requestNumber = requestNumberSequence.incrementAndGet();
        logRequest(requestNumber, request, body);
        ClientHttpResponse response = execution.execute(request, body);
        response = new BufferedClientHttpResponse(response);
        logResponse(requestNumber, response);
        return response;
    }

    private void logRequest(int requestNumber, HttpRequest request, byte[] body) {
        if (log.isDebugEnabled()) {
            String prefix = requestNumber + " > ";
            log.debug("{} Request: {} {}", prefix, request.getMethod(), request.getURI());
            log.debug("{} Headers: {}", prefix, request.getHeaders());
            if (body.length > 0) {
                log.debug("{} Body: \n{}", prefix, new String(body, StandardCharsets.UTF_8));
            }
        }
    }

    private void logResponse(int requestNumber, ClientHttpResponse response) throws IOException {
        if (log.isDebugEnabled()) {
            String prefix = requestNumber + " < ";
            log.debug("{} Response: {} {} {}", prefix, response.getStatusCode(), response.getStatusCode().name(), response.getStatusText());
            log.debug("{} Headers: {}", prefix, response.getHeaders());
            String body = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8);
            if (body.length() > 0) {
                log.debug("{} Body: \n{}", prefix, body);
            }
        }
    }

    /**
     * Wrapper around ClientHttpResponse, buffers the body so it can be read repeatedly (for logging & consuming the result).
     */
    private static class BufferedClientHttpResponse implements ClientHttpResponse {

        private final ClientHttpResponse response;
        private byte[] body;

        public BufferedClientHttpResponse(ClientHttpResponse response) {
            this.response = response;
        }

        @Override
        public HttpStatus getStatusCode() throws IOException {
            return response.getStatusCode();
        }

        @Override
        public int getRawStatusCode() throws IOException {
            return response.getRawStatusCode();
        }

        @Override
        public String getStatusText() throws IOException {
            return response.getStatusText();
        }

        @Override
        public void close() {
            response.close();
        }

        @Override
        public InputStream getBody() throws IOException {
            if (body == null) {
                body = StreamUtils.copyToByteArray(response.getBody());
            }
            return new ByteArrayInputStream(body);
        }

        @Override
        public HttpHeaders getHeaders() {
            return response.getHeaders();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

组态:

 @Bean
    public RestTemplateBuilder restTemplateBuilder() {
        return new RestTemplateBuilder()
                .additionalInterceptors(Collections.singletonList(new LoggingInterceptor()));
    }
Run Code Online (Sandbox Code Playgroud)

日志输出示例:

2018-10-08 10:58:53 [main] DEBUG x.y.z.LoggingInterceptor - 2 >  Request: POST http://localhost:53969/payment/v4/private/payment-lists/10022/templates
2018-10-08 10:58:53 [main] DEBUG x.y.z.LoggingInterceptor - 2 >  Headers: {Accept=[application/json, application/*+json], Content-Type=[application/json;charset=UTF-8], Content-Length=[986]}
2018-10-08 10:58:53 [main] DEBUG x.y.z.LoggingInterceptor - 2 >  Body: 
{"idKey":null, ...}
2018-10-08 10:58:53 [main] DEBUG x.y.z.LoggingInterceptor - 2 <  Response: 200 OK 
2018-10-08 10:58:53 [main] DEBUG x.y.z.LoggingInterceptor - 2 <  Headers: {Content-Type=[application/json;charset=UTF-8], Transfer-Encoding=[chunked], Date=[Mon, 08 Oct 2018 08:58:53 GMT]}
2018-10-08 10:58:53 [main] DEBUG x.y.z.LoggingInterceptor - 2 <  Body: 
{ "idKey" : "10022", ...  }
Run Code Online (Sandbox Code Playgroud)


Elt*_*dré 8

application.properties

logging.level.org.springframework.web.client=DEBUG
Run Code Online (Sandbox Code Playgroud)

application.yml

logging:
  level:  
    root: WARN
    org.springframework.web.client: DEBUG
Run Code Online (Sandbox Code Playgroud)


Chr*_*ris 7

---- 2019年7月----

(使用Spring Boot)

令我惊讶的是,Spring Boot拥有零配置魔术,它没有提供一种使用RestTemplate检查或记录简单JSON响应正文的简便方法。我浏览了此处提供的各种答案和评论,并分享了自己的(仍)有效的简化版本,并且在当前的情况下,对于我来说,这似乎是一个合理的解决方案(我使用的是Spring Boot 2.1.6和Gradle 4.4。 )

1.使用Fiddler作为http代理

实际上,这是一个非常优雅的解决方案,因为它绕开了创建自己的拦截器或将基础的HTTP客户端更改为apache的所有繁琐工作(请参见下文)。

安装并运行Fiddler

然后

添加-DproxySet=true -Dhttp.proxyHost=localhost -Dhttp.proxyPort=8888到您的VM选项

2.使用Apache HttpClient

将Apache HttpClient添加到您的Maven或Gradle依赖项。

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.9</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

使用HttpComponentsClientHttpRequestFactory作为RequestFactory的RestTemplate。最简单的方法是:

RestTemplate restTemplate = new RestTemplate();

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

application.properties文件中启用DEBUG (如果您使用的是Spring Boot)

logging.level.org.apache.http=DEBUG

如果您使用的是Spring Boot,则需要确保已建立日志记录框架,例如,通过使用包含包括在内的spring-boot-starter依赖项spring-boot-starter-logging

3.使用拦截器

我将让您通读其他答案和评论中的建议,反建议和陷阱,并自行决定是否要沿这条路走。

4.日志URL和响应状态(无正文)

尽管这不满足记录主体的规定要求,但这是开始记录REST调用的快速简便的方法。它显示完整的URL和响应状态。

只需将以下行添加到application.properties文件中(假设您使用的是Spring Boot,并假设您使用的Spring Boot Starter依赖项包括spring-boot-starter-logging

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

输出将如下所示:

2019-07-29 11:53:50.265 DEBUG o.s.web.client.RestTemplate : HTTP GET http://www.myrestservice.com/Endpoint?myQueryParam=myValue
2019-07-29 11:53:50.276 DEBUG o.s.web.client.RestTemplate : Accept=[application/json]
2019-07-29 11:53:50.584 DEBUG o.s.web.client.RestTemplate : Response 200 OK
2019-07-29 11:53:50.585 DEBUG o.s.web.client.RestTemplate : Reading to [org.mynamespace.MyJsonModelClass]
Run Code Online (Sandbox Code Playgroud)

  • 4号是最简单的调试方法。 (2认同)
  • 2号对我有用。它记录请求正文。谢谢你! (2认同)

Eme*_*gia 6

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

此处提供了HttpClient 3.x的日志记录配置.


Vig*_*ggi 5

这可能不是正确的方法,但是我认为这是打印请求和响应而不填太多日志的最简单方法。

通过在以下两行中添加application.properties,可在第一行记录所有请求和响应,以便记录请求,在第二行记录响应。

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


小智 5

这里的许多响应都需要更改代码和自定义类,但这确实没有必要。Gte 一个调试代理,例如 fiddler,并设置您的 java 环境以在命令行上使用代理(-Dhttp.proxyHost 和 -Dhttp.proxyPort),然后运行 ​​fiddler,您可以看到完整的请求和响应。它还具有许多辅助优势,例如能够在提交修改服务器之前和之后对结果和响应进行修改以运行实验。

最后可能出现的问题是,如果您必须使用 HTTPS,则需要从 fiddler 导出 SSL 证书并将其导入到 java 密钥库 (cacerts) 提示:默认的 java 密钥库密码通常是“changeit”。


hol*_*s83 5

在Apache HttpClient 的帮助下登录到Logback

您需要在类路径中使用 Apache HttpClient:

<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpclient</artifactId>
  <version>4.5.10</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

配置您RestTemplate使用 HttpClient:

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
Run Code Online (Sandbox Code Playgroud)

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

<logger name="org.apache.http.wire" level="DEBUG"/>
Run Code Online (Sandbox Code Playgroud)

或者记录更多:

<logger name="org.apache.http" level="DEBUG"/>
Run Code Online (Sandbox Code Playgroud)