Spring RestTemplate - как включить полную отладку / запись запросов / ответов?

Я уже некоторое время использую Spring RestTemplate и постоянно бьюсь о стену, когда пытаюсь отлаживать ее запросы и ответы. Я в основном ищу то же самое, что и при использовании curl с включенной опцией "verbose". Например:

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

Отображает как отправленные данные, так и полученные данные (включая заголовки, куки и т. Д.).

Я проверил некоторые похожие сообщения, такие как: Как мне регистрировать ответы в Spring RestTemplate? но мне не удалось решить эту проблему.

Один из способов сделать это состоит в том, чтобы фактически изменить исходный код RestTemplate и добавить туда несколько дополнительных операторов регистрации, но я бы нашел этот подход действительно последним средством. Должен быть какой-то способ сказать Spring Web Client/RestTemplate, чтобы все регистрировалось более дружественным способом.

Моя цель - сделать это с помощью кода вроде:

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

и затем получить тот же тип отладочной информации (как я получаю с помощью curl) в файле журнала или в консоли. Я считаю, что это было бы чрезвычайно полезно для любого, кто использует Spring RestTemplate и имеет проблемы. Использование curl для отладки ваших проблем RestTemplate просто не работает (в некоторых случаях).

31 ответ

Решение

Я наконец нашел способ сделать это правильно. Большая часть решения исходит от того, как настроить Spring и SLF4J, чтобы я мог получать логи?

Кажется, есть две вещи, которые необходимо сделать:

  1. Добавьте следующую строку в log4j.properties: log4j.logger.httpclient.wire=DEBUG
  2. Убедитесь, что Spring не игнорирует вашу конфигурацию регистрации

Вторая проблема возникает в основном в среде Spring, где используется slf4j (как это было в моем случае). Таким образом, при использовании slf4j убедитесь, что происходят следующие две вещи:

  1. В вашем classpath нет библиотеки регистрации общего ресурса: это можно сделать, добавив дескрипторы исключения в ваш pom:

            <exclusions><exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
    
  2. Файл log4j.properties хранится где-то в пути к классам, где Spring может найти / увидеть его. Если у вас есть проблемы с этим, последнее решение было бы поместить файл log4j.properties в пакет по умолчанию (не очень хорошая практика, но просто чтобы убедиться, что все работает так, как вы ожидаете)

Просто чтобы завершить пример с полной реализацией 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=================================================");
    }

}

Затем создать экземпляр RestTemplate используя BufferingClientHttpRequestFactory и LoggingRequestInterceptor:

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

BufferingClientHttpRequestFactory требуется, так как мы хотим использовать тело ответа как в перехватчике, так и для исходного кода вызова. Реализация по умолчанию позволяет прочитать тело ответа только один раз.

В Spring Boot вы можете получить полный запрос / ответ, установив его в свойствах (или другой метод 12 факторов)

logging.level.org.apache.http=DEBUG

это выводы

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

и ответ

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

или просто logging.level.org.apache.http.wire=DEBUG который, кажется, содержит всю соответствующую информацию

Расширяем ответ @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
    }
}

Настройка 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());

Лучше всего добавить logging.level.org.springframework.web.client.RestTemplate=DEBUG к application.properties файл.

Другие решения, такие как настройка log4j.logger.httpclient.wire не всегда будет работать, потому что они предполагают, что вы используете log4j и апач HttpClient, что не всегда верно.

Однако обратите внимание, что этот синтаксис будет работать только в последних версиях Spring Boot.

Вы можете использовать https://github.com/markhobson/spring-rest-template-logger для входа RestTemplate HTTP трафик.

Добавьте зависимость в ваш проект Maven:

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

Затем настройте свой RestTemplate следующее:

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

Теперь весь HTTP-трафик RestTemplate будет регистрироваться в org.hobsoft.spring.resttemplatelogger.LoggingCustomizer на уровне отладки.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я написал эту библиотеку.

Решение, данное ксенотеррацидом для использования

logging.level.org.apache.http=DEBUG

это хорошо, но проблема в том, что по умолчанию Apache HttpComponents не используется.

Чтобы использовать Apache HttpComponents, добавьте в свой pom.xml

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

и настроить RestTemplate с:

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

Ни один из этих ответов на самом деле не решает 100% проблемы. mjj1409 получает большую часть этого, но удобно избегает проблемы регистрации ответа, которая требует немного больше работы. Поль Сабу предлагает решение, которое кажется реалистичным, но не предоставляет достаточно подробностей для его реализации (и оно не сработало для меня). Софиен получила протоколирование, но с критической проблемой: ответ больше не читается, потому что входной поток уже использован!

Я рекомендую использовать 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

Вариант 1. Откройте журнал отладки.

Конфигурировать RestTemplate

  • По умолчанию RestTemplate использует стандартные средства JDK для установления HTTP-соединений. Вы можете переключиться на использование другой библиотеки HTTP, такой как Apache HttpComponents

    @Bean public RestTemplate restTemplate (RestTemplateBuilder builder) {RestTemplate restTemplate = builder.build (); вернуть restTemplate; }

Настроить ведение журнала

  • application.yml

    ведение журнала: уровень: 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();
    }
}

Внедрить перехватчик

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

}

Конфигурировать RestTemplate

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

Настроить ведение журнала

  • Проверьте пакет LoggingRestTemplate, например, в application.yml:

    logging: level: com.example.logging: DEBUG

Вариант 3. Использование httpcomponent

Импорт зависимости httpcomponent

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

Конфигурировать RestTemplate

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

Настроить ведение журнала

  • Проверьте пакет LoggingRestTemplate, например, в application.yml:

    ведение журнала: уровень: org.apache.http: DEBUG

---- июль 2019 ----

(с использованием Spring Boot)

Я был удивлен, что Spring Boot со всей магией нулевой конфигурации не предоставляет простой способ проверки или регистрации простого тела ответа JSON с помощью RestTemplate. Я просмотрел различные ответы и комментарии, представленные здесь, и поделюсь своей собственной дистиллированной версией того, что (все еще) работает и кажется мне разумным решением, учитывая текущие параметры (я использую Spring Boot 2.1.6 с Gradle 4.4)

1. Использование Fiddler в качестве HTTP-прокси

На самом деле это довольно элегантное решение, так как оно позволяет обойти все громоздкие усилия по созданию собственного перехватчика или замене базового http-клиента на apache (см. Ниже).

Установите и запустите Fiddler

а потом

добавлять -DproxySet=true -Dhttp.proxyHost=localhost -Dhttp.proxyPort=8888 к параметрам вашей виртуальной машины

2. Использование Apache HttpClient

Добавьте Apache HttpClient в свои зависимости Maven или Gradle.

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

Использовать HttpComponentsClientHttpRequestFactoryкак RequestFactory для RestTemplate. Самый простой способ сделать это:

RestTemplate restTemplate = new RestTemplate();

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

Включите DEBUG в вашем application.properties файл (если вы используете 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, которая включает 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]

Как указано в других ответах, тело ответа требует особой обработки, чтобы его можно было читать повторно (по умолчанию его содержимое расходуется при первом чтении).

Вместо использования 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();
        }
    }
}

Конфигурация:

 @Bean
    public RestTemplateBuilder restTemplateBuilder() {
        return new RestTemplateBuilder()
                .additionalInterceptors(Collections.singletonList(new LoggingInterceptor()));
    }

Пример вывода журнала:

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", ...  }

application.properties

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

application.yml

logging:
  level:  
    root: WARN
    org.springframework.web.client: DEBUG

Помимо ведения журнала HttpClient, описанного в другом ответе, вы также можете ввести ClientHttpRequestInterceptor, который читает тело запроса и ответ и регистрирует его. Возможно, вы захотите сделать это, если другие вещи также используют HttpClient, или если вы хотите пользовательский формат ведения журнала. Внимание: вы захотите дать RestTemplate объект BufferingClientHttpRequestFactory, чтобы вы могли прочитать ответ дважды.

Это может быть неправильный способ сделать это, но я думаю, что это самый простой подход к печати запросов и ответов без чрезмерного заполнения журналов.

Добавив ниже 2 строки, application.properties регистрирует все запросы и ответы в 1-й строке, чтобы регистрировать запросы, а во 2-й строке - в ответах.

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

Если предположить, RestTemplate настроен для использования HttpClient 4.x, вы можете ознакомиться с документацией по ведению журнала HttpClient здесь. Регистраторы отличаются от тех, которые указаны в других ответах.

Конфигурация регистрации для HttpClient 3.x доступна здесь.

Так много ответов здесь требуют изменений кодирования и настраиваемых классов, и это действительно не нужно. Получите прокси-сервер для отладки, такой как fiddler, и настройте свою среду java на использование прокси в командной строке (-Dhttp.proxyHost и -Dhttp.proxyPort), затем запустите fiddler, и вы сможете увидеть запросы и ответы полностью. Также имеется множество дополнительных преимуществ, таких как возможность возиться с результатами и ответами до и после их отправки для проведения экспериментов, прежде чем приступить к модификации сервера.

Последняя проблема, которая может возникнуть, заключается в том, что если вы должны использовать HTTPS, вам нужно будет экспортировать сертификат SSL из fiddler и импортировать его в подсказку хранилища ключей java (cacerts): пароль хранилища ключей java по умолчанию обычно "changeit".

Для входа в Logback с помощью Apache HttpClient:

Вам нужен 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"/>

Большая часть приведенного выше решения не работает должным образом, когда используется RestTemplate и есть4xxили5xxтип ответа какClientHttpResponseтело пусто. Вот решение, которое я использовал для регистрации всего HTTP-запроса/ответа в RestTemplate без потери информации тела ответа во всех случаях. Версия загрузки Spring<version>2.7.5</version>

1.СоздатьLoggingInterceptorсорт

      import lombok.extern.slf4j.Slf4j;
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.stereotype.Component;
import org.springframework.util.StreamUtils;

import java.io.*;

@Component
@Slf4j
public class LoggingInterceptor implements ClientHttpRequestInterceptor {

@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
    traceRequest(request, body);
    ClientHttpResponse response = execution.execute(request, body);
    response = traceResponse(response);
    return response;
}

private void traceRequest(HttpRequest request, byte[] body) throws IOException {
    if (!log.isDebugEnabled()) {
        return;
    }
    log.debug("=========================== Request Begin ===========================");
    log.debug("URI          : " + request.getURI());
    log.debug("Method       : " + request.getMethod());
    log.debug("Headers      : " + request.getHeaders());
    log.debug("Body : " + new String(body, "utf-8"));
    log.debug("============================ Request End ============================");

}

private ClientHttpResponse traceResponse(ClientHttpResponse response) throws IOException {
    if (!log.isDebugEnabled()) {
        return response;
    }
    ClientHttpResponse newCopiedResponse = new BufferingClientHttpResponseWrapper(response);
    StringBuilder inputStringBuilder = new StringBuilder();
    // ClientHttpResponse there is no body in response in case of 4xx or 5xx code, so we skip the body part
    if (isSuccessStatus(response.getRawStatusCode())) {
        inputStringBuilder = new StringBuilder();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(newCopiedResponse.getBody(), "UTF-8"));
        String line = bufferedReader.readLine();
        while (line != null) {
            inputStringBuilder.append(line);
            line = bufferedReader.readLine();
        }
    }

    log.debug("=========================== Response Begin ===========================");
    log.debug("Status code   : {}", response.getStatusCode());
    log.debug("Status text   : {}", response.getStatusText());
    log.debug("Headers       : {}", response.getHeaders());
    if (isSuccessStatus(response.getRawStatusCode())) {
        log.debug("Response Body : {}", inputStringBuilder.toString());
        log.debug("============================ Response End ============================");
    }

    return newCopiedResponse;

}

private static boolean isSuccessStatus(int statusCode) {
    return (statusCode / 100) == 2;
}


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

    private final ClientHttpResponse response;
    private byte[] body;

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

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

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

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

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

    @Override
    public HttpHeaders getHeaders() {
        return this.response.getHeaders();
    }

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

}

2. Прикрепите его кRestTemplateфасоль

      @Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate createRestTemplate(LoggingInterceptor loggingInterceptor) {

        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setInterceptors(Collections.singletonList(loggingInterceptor));

        return restTemplate;
    }
}

3. Применить соответствующий уровень в приложении

      logging:
  level:
    com:
      test: DEBUG

Как ни странно, ни одно из этих решений не работает, так как RestTemplate, похоже, не возвращает ответ на некоторые ошибки клиента и сервера в 500 раз. В этом случае вы также будете регистрировать их, реализовав 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);
    }
}

Хитрость настройки вашего RestTemplate с BufferingClientHttpRequestFactory не работает, если вы используете какой-либо ClientHttpRequestInterceptor, что вы будете, если вы пытаетесь войти через перехватчики. Это связано с тем, что InterceptingHttpAccessor (который RestTemplate подклассы) работает.

Короче говоря... просто используйте этот класс вместо RestTemplate (обратите внимание, что для этого используется API ведения логов SLF4J, при необходимости измените его):

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

Я согласен, что глупо, что для этого требуется такая большая работа.

В добавление к вышеприведенному обсуждению это только представляет счастливые сценарии. вероятно, вы не сможете записать ответ, если придет ошибка.

В этом случае плюс все вышеперечисленные случаи вы должны переопределить DefaultResponseErrorHandler и установить его, как показано ниже

restTemplate.setErrorHandler(new DefaultResponseErrorHandlerImpl());

Простой способ решить проблему:

  1. Создайте Bean-компонент RestTemplate с помощью RestTemplateBuilder: это даст вам больше контроля над временем соединения и временем чтения.
      @Configuration
public class RestTemplateConfig {
  @Bean
  public RestTemplate restTemplate(RestTemplateBuilder builder) {
    return builder
      .setConnectTimeout(Duration.ofMillis(60000))
      .setReadTimeout(Duration.ofMillis(60000))
      .build();
  }

}
  1. Добавьте эту строку в resources/application.propertiesфайл : logging.level.org.springframework.web.client.RestTemplate=DEBUG
    Надеюсь проблема решится!

Как указал @MilacH, в реализации есть ошибка. Если statusCode > 400 возвращается, IOException генерируется, так как errorHandler не вызывается из перехватчиков. Исключение можно игнорировать, а затем снова перехватить в методе обработчика.

package net.sprd.fulfillment.common;

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;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import static java.nio.charset.StandardCharsets.UTF_8;

public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor {

    final static Logger log = LoggerFactory.getLogger(LoggingRequestInterceptor.class);

    @SuppressWarnings("HardcodedLineSeparator")
    public static final char LINE_BREAK = '\n';

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        try {
            traceRequest(request, body);
        } catch (Exception e) {
            log.warn("Exception in LoggingRequestInterceptor while tracing request", e);
        }

        ClientHttpResponse response = execution.execute(request, body);

        try {
            traceResponse(response);
        } catch (IOException e) {
            // ignore the exception here, as it will be handled by the error handler of the restTemplate
            log.warn("Exception in LoggingRequestInterceptor", e);
        }
        return response;
    }

    private void traceRequest(HttpRequest request, byte[] body) {
        log.info("===========================request begin================================================");
        log.info("URI         : {}", request.getURI());
        log.info("Method      : {}", request.getMethod());
        log.info("Headers     : {}", request.getHeaders());
        log.info("Request body: {}", new String(body, UTF_8));
        log.info("==========================request end================================================");
    }

    private void traceResponse(ClientHttpResponse response) throws IOException {
        StringBuilder inputStringBuilder = new StringBuilder();
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), UTF_8))) {
            String line = bufferedReader.readLine();
            while (line != null) {
                inputStringBuilder.append(line);
                inputStringBuilder.append(LINE_BREAK);
                line = bufferedReader.readLine();
            }
        }

        log.info("============================response begin==========================================");
        log.info("Status code  : {}", response.getStatusCode());
        log.info("Status text  : {}", response.getStatusText());
        log.info("Headers      : {}", response.getHeaders());
        log.info("Response body: {}", inputStringBuilder);
        log.info("=======================response end=================================================");
    }

}

org.apache.http.wire дает слишком нечитаемые журналы, поэтому я использую журнал для регистрации сервлета приложений и RestTemplate req/resp для журнала

build.gradle

compile group: 'org.zalando', name: 'logbook-spring-boot-starter', version: '1.13.0'

application.properties

logging.level.org.zalando.logbook:TRACE

RestTemplate

@Configuration
public class RestTemplateConfig {

@Autowired
private LogbookHttpRequestInterceptor logbookHttpRequestInterceptor;

@Autowired
private LogbookHttpResponseInterceptor logbookHttpResponseInterceptor;

@Bean
public RestTemplate restTemplate() {
    return new RestTemplateBuilder()
        .requestFactory(new MyRequestFactorySupplier())
        .build();
}

class MyRequestFactorySupplier implements Supplier<ClientHttpRequestFactory> {

    @Override
    public ClientHttpRequestFactory get() {
        // Using Apache HTTP client.
        CloseableHttpClient client = HttpClientBuilder.create()
            .addInterceptorFirst(logbookHttpRequestInterceptor)
            .addInterceptorFirst(logbookHttpResponseInterceptor)
            .build();
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(client);
        return clientHttpRequestFactory;
    }

}
}

Хотел добавить и мою реализацию этого тоже. Я прошу прощения за все недостающие точки с запятой, это написано на Groovy.

Мне нужно что-то более настраиваемое, чем принятый ответ. Вот бин шаблона отдыха, который очень гибок и регистрирует все, как ищет OP.

Пользовательский класс перехватчика ведения журнала:

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:

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

Реализация:

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

Я просмотрел все ответы. Если вам нужно установить тип аутентификации или время ожидания подключения, вы можете сделать следующее:

      SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(Integer.valueOf(YOUR_VALUE));
factory.setReadTimeout(Integer.valueOf(YOUR_VALUE));

RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(factory));
restTemplate.getInterceptors().add(new LoggingRequestInterceptor());
restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor(USER_NAME,PASSWORD,StandardCharsets.UTF_8));
return restTemplate;

Отspring boot 3, зависимость Apache появилась позже и выглядит следующим образом:<artifactId>httpclient5</artifactId>который используется

Затем вы должны использовать следующее свойство:org.apache.hc.client5.http:DEBUG.

Лучшее решение сейчас, просто добавьте зависимость:

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

Он содержит класс LoggingRequestInterceptor, который вы можете добавить таким образом в RestTemplate:

интегрировать эту утилиту, добавив ее в качестве перехватчика к пружинному RestTemplate, следующим образом:

restTemplate.setRequestFactory(LoggingRequestFactoryFactory.build());

и добавьте реализацию slf4j в ваш фреймворк, например, log4j.

или напрямую используйте "Zg2proRestTemplate". "Лучший ответ" @PaulSabou выглядит так, поскольку httpclient и все библиотеки apache.http не обязательно загружаются при использовании пружинного RestTemplate.

Обратитесь к разделу "Вопросы и ответы" для регистрации запросов и ответов для шаблона остальных, включив множественные чтения в HttpInputStream

Почему мой пользовательский ClientHttpRequestInterceptor с пустым ответом

Что касается ответа с использованием ClientHttpInterceptor, я нашел способ сохранить весь ответ без буферизации фабрик. Просто сохраните входной поток тела ответа внутри байтового массива с помощью некоторого метода utils, который скопирует этот массив из тела, но важно, окружите этот метод try catch, потому что он сломается, если ответ пуст (что является причиной исключения доступа к ресурсам), и в catch просто создайте пустой байтовый массив, а затем просто создайте анонимный внутренний класс ClientHttpResponse, используя этот массив и другие параметры из исходного ответа. Затем вы можете вернуть этот новый объект ClientHttpResponse в цепочку выполнения остальных шаблонов и зарегистрировать ответ, используя байтовый массив тела, который был ранее сохранен. Таким образом, вы избежите потребления InputStream в реальном ответе и сможете использовать ответ Rest Template как есть. Обратите внимание, это может быть опасно, если ваш ответ слишком велик

Другие вопросы по тегам