Spring RestTemplate - isteklerin / yanıtların tam hata ayıklama / günlük kaydını nasıl etkinleştirebilirim?


220

Spring RestTemplate'i bir süredir kullanıyorum ve isteklerini ve yanıtlarını hata ayıklamaya çalışırken sürekli bir duvara çarptım. Temelde curl "verbose" seçeneği açıkken kullandığım aynı şeyleri görmek için arıyorum. Örneğin :

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

Hem gönderilen verileri hem de alınan verileri görüntüler (başlıklar, çerezler vb. Dahil).

Gibi bazı ilgili mesajları kontrol ettim: Spring RestTemplate yanıt nasıl kaydederim? ancak bu sorunu çözmeyi başaramadım.

Bunu yapmanın bir yolu aslında RestTemplate kaynak kodunu değiştirmek ve orada bazı ekstra günlük ifadeleri eklemek olacaktır, ama bu yaklaşım gerçekten son çare bir şey bulurdum. Spring Web Client / RestTemplate'e her şeyi çok daha dostça bir şekilde kaydetmesini söylemenin bir yolu olmalı.

Amacım bunu aşağıdaki gibi kodla yapabilmek olacaktır:

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

ve daha sonra günlük dosyasında veya konsolda aynı tür hata ayıklama bilgilerini (curl ile aldığım gibi) almak için. Bunun Spring RestTemplate kullanan ve sorunları olan herkes için son derece yararlı olacağına inanıyorum. RestTemplate sorunlarınızda hata ayıklamak için curl kullanmak işe yaramaz (bazı durumlarda).


31
2018'de okuyan herkese uyarı: Bunun basit bir cevabı yok!
davidfrancis

3
En kolay yolu AbstractHttpMessageConverter sınıfının write (...) yönteminde bir kesme noktası kullanmaktır, verileri görebileceğiniz bir outputMessage nesnesi vardır. PS Değeri kopyalayabilir ve daha sonra çevrimiçi biçimlendirici ile biçimlendirebilirsiniz.
Sergey Chepurnov

1
bunun Baharda yapılması kolay gibi görünüyor, ama buradaki cevaplardan yola çıkarak - durum değil. Başka bir çözüm, Spring'i tamamen atlamak ve isteği / yanıtı yakalamak için Fiddler gibi bir araç kullanmak olacaktır.
michaelok


Temmuz 2019: Bu sorunun basit bir çözümü hala var, ben diğer 24 cevaplar (şimdiye kadar) ve bunların yorumlar ve tartışmalar bir özetini vermeye çalıştım altında kendi cevap . Umarım yardımcı olur.
Chris

Yanıtlar:


207

ClientHttpRequestInterceptorTalebi ve yanıtı izlemek için tam bir uygulama ile örneği tamamlamak için :

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

}

Sonra RestTemplatea BufferingClientHttpRequestFactoryve the tuşlarını kullanarak başlatın LoggingRequestInterceptor:

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

BufferingClientHttpRequestFactoryBiz yakalayıcısı ve ilk arama kodu için hem tepki gövdesini kullanmak istediğiniz gibi gereklidir. Varsayılan uygulama, yanıt gövdesinin yalnızca bir kez okunmasına izin verir.


28
Bu yanlış. Akışı okursanız, uygulama kodu yanıtı okuyamaz.
James Watkins

28
yanıtı iki kez okuyabilmemiz için RestTemplate'e bir BufferingClientHttpRequestFactory verdik.
sofiene zaghdoudi

16
Bu tekniği yaklaşık 3 aydır kullanıyoruz. Yalnızca @ BufferingClientHttpResponseWrappersofienezaghdoudi'nin ima ettiği gibi yapılandırılmış RestTemplate ile çalışır . Ancak, Spring'in mockServer çerçevesini kullanan testlerde kullanıldığında MockRestServiceServer.createServer(restTemplate), RequestFactory öğesine yazdığı için çalışmaz InterceptingClientHttpRequestFactory.
RubesMN

8
Teknik iyi, uygulama yanlış. 404 case, response.getBody () IOException'ı atar -> günlüğü asla dışarı çıkarmazsınız ve daha da kötüsü, daha sonraki kodunuzda RestClientResponseException
MilacH 26:17 '

6
Cevap için teşekkürler. Ancak bu, diğer günlüklerin çoğuna yayılabileceği için birden çok "log.debug" a sahip olmak kötü bir uygulamadır. Tek bir log.debug talimatı kullanmak daha iyidir, böylece her şeyin aynı yerde olduğundan emin olursunuz
user2447161

130

Spring Boot'da, özellikleri (veya başka bir 12 faktör yöntemi) ayarlayarak tam isteği / yanıtı alabilirsiniz.

logging.level.org.apache.http=DEBUG

bu çıktılar

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

ve cevap

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

ya da sadece logging.level.org.apache.http.wire=DEBUGilgili tüm bilgileri içeriyor gibi görünüyor


4
Bu istediğimi yapan en basit şeydi. Bunu kabul edilen cevaba eklemeyi şiddetle tavsiye ediyorum.
michaelavila

22
Bir javadoc göre RestTemplate :by default the RestTemplate relies on standard JDK facilities to establish HTTP connections. You can switch to use a different HTTP library such as Apache HttpComponents
Ortomala Lokni

22
RestTemplate, @OrtomalaLokni tarafından işaret edildiği gibi bu Apache sınıflarını varsayılan olarak kullanmaz; bu nedenle , kullanıldıklarında hata ayıklamanın nasıl yazdırılacağına ek olarak bunları nasıl kullanacağınızı da eklemeniz gerekir .
Kaptan Adam

Böyle alıyorum:http-outgoing-0 << "[0x1f][0x8b][0x8][0x0][0x0][0x0][0x0][0x0]
Partha Sarathi Ghosh

2
@ParthaSarathiGhosh İçerik muhtemelen gzip kodludur, bu yüzden ham metni görmezsiniz.
Matthew Buckett

79

@Hstoerr cevabını bazı kodlarla genişletme:


İstek yanıtlarını günlüğe kaydetmek için LoggingRequestInterceptor oluşturun

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 Kurulumu

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

Bu, ilkbahar-3.1 versiyonuna kadar mevcut değildir.
Gyan

3
'günlük kaydı yanıtı' sorusuna cevap vermez, bunun yerine bir // do günlük yorumu bırakır.
Jiang YD

1
günlüğü yapmak kolaydı, ancak bu sadece istekler için çalışıyor, yanıt gövdelerini görmüyorum, yanıt nesnesine sahip olduğumu varsayalım, ancak akışını okumak iyi bir fikir değil.
Pavel Niedoba

11
@PavelNiedoba BufferClientHttpRequestFactory, yanıtın birden fazla kez okunmasına izin verir.
mjj1409

2
Hata ayıklama ve düzenli günlük kaydı gereksinimlerinize uygun olmayan bir veritabanında istek / yanıt hakkında bilgi depolamanız gerekiyorsa bu iyi çalışır.
GameSalutes

32

Yapabileceğiniz en iyi şey eklemektir logging.level.org.springframework.web.client.RestTemplate=DEBUGiçin application.propertiesdosyanın.

Ayarlama gibi diğer çözümler log4j.logger.httpclient.wireher zaman işe yaramaz çünkü kullandığınız log4jve HttpClienther zaman doğru olmayan Apache'yi varsayıyorlar .

Ancak, bu sözdiziminin yalnızca Spring Boot'un en son sürümlerinde çalışacağını unutmayın.


5
Bu, istek ve yanıt gövdesini, yalnızca URL'yi ve istek türünü (spring-web-4.2.6) günlüğe kaydetmez
dve

1
Haklısın, bu bir wirekayıt değil , sadece url, resepone kodu, POST parametreleri vb.Gibi temel bilgileri içerir
gamliela


Bu iyi, ancak tepki organı görülemedi!
sunleo

Parlak. Yanıt gövdesini yazdırmasa da, yine de çok kullanışlıdır. Teşekkür ederim.
Chris

30

Bu cevapların hiçbiri sorunun% 100'ünü çözmüyor. mjj1409 çoğunu alır, ancak yanıtın günlüğe kaydedilmesi sorununu önler, bu da biraz daha fazla çalışma gerektirir. Paul Sabou gerçekçi görünen bir çözüm sunar, ancak gerçekten uygulamak için yeterli ayrıntı sağlamaz (ve benim için hiç işe yaramadı). Sofiene günlük kaydını aldı ama kritik bir problemle: giriş akışı zaten tüketildiğinden yanıt artık okunamıyor!

Yanıt gövdesinin birden çok kez okunmasına izin vermek için yanıt nesnesini sarmak için bir BufferingClientHttpResponseWrapper kullanmanızı öneririz:

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

}

Yanıt gövdesi belleğe yüklendiğinden ve birden çok kez okunabildiğinden, bu, InputStream öğesini tüketmez. Sınıf yolunuzda BufferingClientHttpResponseWrapper yoksa, basit uygulamayı burada bulabilirsiniz:

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

RestTemplate'i ayarlamak için:

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

aynı, responseCopy.getBody () 404 durumunda IOexception atar, böylece yanıtı başka kodunuza geri göndermezsiniz ve normal olarak RestClientResponseException ResourceAccessException olur
MilacH

1
Daha status==200önce kontrol etmelisinizresponseCopy.getBody()
Anand Rockzz

4
Ama pakete özel. LoggingRequestInterceptor'unuzu 'org.springframework.http.client' paketine koydunuz mu?
zbstof

2
Ne hakkında asyncRestTemplate? Bir geri ListenableFuturearamada değiştirmek mümkün olmayan bir kesişme noktası döndürmeniz gerekir BufferingClientHttpResponseWrapper.
Ömer Faruk Almalı

@ ÖmerFarukAlmalı Bu durumda kullandığınız Guava sürümüne bağlı olarak zincir veya dönüşüm kullanmanız gerekecektir. Bakınız: stackoverflow.com/questions/8191891/…
James Watkins

30

HTTP trafiğini günlüğe kaydetmek için spring-rest-template-logger'ı kullanabilirsiniz RestTemplate.

Maven projenize bir bağımlılık ekleyin:

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

Ardından RestTemplateaşağıdaki gibi özelleştirin :

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

Hata ayıklama günlüğünün etkinleştirildiğinden emin olun application.properties:

logging.level.org.hobsoft.spring.resttemplatelogger.LoggingCustomizer = DEBUG

Artık tüm RestTemplate HTTP trafiği org.hobsoft.spring.resttemplatelogger.LoggingCustomizerhata ayıklama düzeyinde kaydedilecektir .

YASAL UYARI: Bu kütüphaneyi yazdım.


Bu cevap neden reddedildi? Bana yardımcı oldu. Teşekkürler, @Mark Hobson.
Raffael Bechara Rameh

3
@RaffaelBecharaRameh yardımcı oldu sevindim. Başlangıçta indirilmişti çünkü bağlantılı projeden talimatlar yerleştirmedim. Yararlı bulduysanız, oy kullanmaktan çekinmeyin!
Mark Hobson

Gradle aracılığıyla destekliyor musunuz?
BlackHatSamurai

1
@BlackHatSamurai bahar-dinlenme-şablon-logger düzenli bir Maven eseridir, bu yüzden Gradle ile iyi çalışmalıdır.
Mark Hobson

1
Merhaba @erhanasikoglu, rica ederim! Doğru, burada kullanımda görebilirsiniz: github.com/markhobson/spring-rest-template-logger/blob/master/…
Mark Hobson

29

Xenoterracide tarafından kullanılan çözelti

logging.level.org.apache.http=DEBUG

iyi ama sorun varsayılan olarak Apache HttpComponents kullanılmamasıdır.

Apache HttpComponents kullanmak için pom.xml dosyasına ekleyin

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

ve şunlarla yapılandırın RestTemplate:

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

En kolay yol, sadece istekFactory üzerine yazdığı için sadece MockRestServiceServer ile çalışmadığını ekleyeceğim.
17'de zbstof

İyi çalışıyor ve sorun yok daha az yapılandırma!
sunleo

26

Sonunda bunu doğru şekilde yapmanın bir yolunu buldum. Çözümün çoğu, Günlük alabilmem için Spring ve SLF4J'yi nasıl yapılandırabilirim?

Yapılması gereken iki şey var gibi görünüyor:

  1. Log4j.properties dosyasına aşağıdaki satırı ekleyin: log4j.logger.httpclient.wire=DEBUG
  2. İlkbaharın günlük yapılandırmanızı göz ardı etmediğinden emin olun

İkinci sorun çoğunlukla slf4j'nin kullanıldığı bahar ortamlarında olur (benim durumumda olduğu gibi). Bu nedenle, slf4j kullanıldığında, aşağıdaki iki şeyin gerçekleştiğinden emin olun:

  1. Sınıf yolunuzda ortak günlük kaydı kitaplığı yoktur: bu, pom'nıza hariç tutma tanımlayıcıları ekleyerek yapılabilir:

            <exclusions><exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
  2. Log4j.properties dosyası, sınıf yolunda, yayın bulabileceği / görebileceği bir yerde saklanır. Bununla ilgili sorunlarınız varsa, son çare çözümü log4j.properties dosyasını varsayılan pakete koymak olacaktır (iyi bir uygulama değil, sadece beklediğiniz gibi çalıştığını görmek için)


7
Bu benim için işe yaramıyor, her iki şeyi de yaptım. Projemde zaten kullanılmadığında neden log4j.properties koymam gerekiyor anlamıyorum (mvn bağımlılığı tarafından kontrol edildi: ağaç)
Pavel Niedoba

Bu benim için de işe yaramıyor. Hatta kök kaydediciyi Hata Ayıklama moduna ayarlamaya çalıştım ve hala hiçbir şey yapmadım.
James Watkins

"httpclient.wire.content" ve "httpclient.wire.header", Axis2 çerçevesindeki günlükçü adlarıdır. Onlar Bahar projesinde örneğin SOAP isteklerini oturum için kullanılabilir eğer bu Axis2 kullanarak yapılır.
17th lathspell

11
httpclient.wireaslında Apache HttpComponents HttpClient kütüphanesindendir ( hc.apache.org/httpcomponents-client-ga/logging.html adresine bakın ). Bu teknik yalnızcaRestTemplateHttpComponentsClientHttpRequestFactory
Scott Frederick

22

Günlük Kaydı RestTemplate

Seçenek 1. Hata ayıklama günlüğünü açın.

RestTemplate'i yapılandırın

  • Varsayılan olarak RestTemplate , HTTP bağlantıları oluşturmak için standart JDK özelliklerine güvenir. Apache HttpComponents gibi farklı bir HTTP kitaplığı kullanmaya geçebilirsiniz

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

Günlüğe kaydetmeyi yapılandır

  • application.yml

    günlüğe kaydetme: düzey: org.springframework.web.client.RestTemplate: DEBUG

Seçenek 2. Durdurucu Kullanma

Sarıcı Yanıtı

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

Durdurucu Uygula

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'i yapılandırın

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

Günlüğe kaydetmeyi yapılandır

  • LoggingRestTemplate paketini kontrol edin, örneğin application.yml:

    günlüğe kaydetme: düzey: com.example.logging: DEBUG

Seçenek 3. httpcomponent kullanımı

Http bileşen bağımlılığını içe aktarma

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

RestTemplate'i yapılandırın

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

Günlüğe kaydetmeyi yapılandır

  • LoggingRestTemplate paketini kontrol edin, örneğin application.yml:

    günlüğe kaydetme: düzey: org.apache.http: DEBUG


Not: yapılandırmak istiyorsanız TestRestTemplate, yapılandırın RestTemplateBuilder: @Bean public RestTemplateBuilder restTemplateBuilder () {return new RestTemplateBuilder (). AdditionalInterceptors (Collections.singletonList (new LoggingRestTemplate ())); }
kingoleg

Ayrıca yeni InputStreamReader (responseWrapper.getBody (), StandardCharsets.UTF_8); "diğer uç" bir hata döndürürse hata atabilir. Bir try bloğuna yerleştirmek isteyebilirsiniz.
PeterS

16

---- Temmuz 2019 ----

(Spring Boot kullanarak)

Spring Boot'un, tüm Sıfır Yapılandırma sihriyle birlikte, RestTemplate ile basit bir JSON yanıt gövdesini incelemek veya günlüğe kaydetmek için kolay bir yol sağlamadığına şaşırdım. Burada verilen çeşitli cevapları ve yorumları inceledim ve mevcut seçenekler göz önüne alındığında (hala) işe yarayan ve bana makul bir çözüm gibi görünen kendi damıtılmış versiyonumu paylaşıyorum (Gradle 4.4 ile Spring Boot 2.1.6 kullanıyorum) )

1. Fiddler'ı http proxy olarak kullanma

Bu aslında oldukça zarif bir çözümdür, çünkü kendi önleyicinizi oluşturmak veya temel http istemcisini apache olarak değiştirmek için hantal çabaları atlar (aşağıya bakın).

Fiddler'ı yükleyin ve çalıştırın

ve sonra

-DproxySet=true -Dhttp.proxyHost=localhost -Dhttp.proxyPort=8888VM Seçeneklerinize ekleyin

2. Apache HttpClient Kullanımı

Maven veya Gradle bağımlılıklarınıza Apache HttpClient ekleyin.

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

HttpComponentsClientHttpRequestFactoryRestTemplate için RequestFactory olarak kullanın . Bunu yapmanın en basit yolu:

RestTemplate restTemplate = new RestTemplate();

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

application.propertiesDosyanızda DEBUG'ı etkinleştirin (Spring Boot kullanıyorsanız)

logging.level.org.apache.http=DEBUG

Spring Boot kullanıyorsanız, örneğin bir yay önyükleme başlatıcı bağımlılığı kullanarak bir günlük kaydı çerçevesinin ayarlandığından emin olmanız gerekir spring-boot-starter-logging.

3. Bir Durdurucu Kullanın

Diğer cevaplar ve yorumlardaki teklifleri, karşı teklifleri ve gotcha'ları okumanıza izin vereceğim ve bu yola gitmek isteyip istemediğinize kendiniz karar vereceğim.

4. Günlüğü Olmayan URL ve Yanıt Durumu

Bu, gövdeyi kaydetmenin belirtilen gereksinimlerini karşılamasa da, REST çağrılarınızı günlüğe kaydetmenin hızlı ve basit bir yoludur. Tam URL'yi ve yanıt durumunu görüntüler.

application.propertiesDosyanıza aşağıdaki satırı ekleyin (Spring Boot kullandığınızı varsayarak ve dahil bir spring boot starter bağımlılığı kullandığınızı varsayarak spring-boot-starter-logging)

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

Çıktı şöyle görünecektir:

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]

2
4 numaralı hata ayıklamanın en kolay yolu.
Yubaraj

1
2 numara benim için çalıştı. İstek gövdesini günlüğe kaydeder. Teşekkür ederim!
caglar

1
Bu sayıya geldiğimde 3 numarayı yapmanın kolay bir yolunu buldum.
Bill Naylor

12

Diğer yanıtta açıklanan HttpClient günlüğünün yanı sıra, isteğin gövdesini ve yanıtı okuyan ve günlüğe kaydeden bir ClientHttpRequestInterceptor da tanıtabilirsiniz. Başka şeyler de HttpClient kullanıyorsa veya özel bir günlük biçimi istiyorsanız bunu yapmak isteyebilirsiniz. Dikkat: RestTemplate'e bir BufferingClientHttpRequestFactory vermek isteyeceksiniz, böylece yanıtı iki kez okuyabilirsiniz.


12

Diğer yanıtlarda belirtildiği gibi, yanıt veren kurumun tekrar tekrar okunabilmesi için özel bir tedaviye ihtiyacı vardır (varsayılan olarak, içeriği ilk okumada tüketilir).

BufferingClientHttpRequestFactoryİsteği ayarlarken kullanmak yerine , önleme aracının kendisi yanıtı sarabilir ve içeriğin saklandığından ve tekrar tekrar okunabildiğinden emin olabilir (kaydedicinin yanı sıra yanıtın tüketicisi tarafından):

Benim önleme, hangi

  • bir sarıcı kullanarak yanıt gövdesini arabelleğe alır
  • bir günlük daha kompakt bir şekilde
  • günlüğe kaydeder durum kodu tanımlayıcısını da (örn. 201 Oluşturuldu)
  • içerir eşzamanlı günlük girişlerini birden çok iş parçacığından kolayca ayırt etmeyi sağlayan istek sıra numarası

Kod:

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

Yapılandırma:

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

Örnek günlük çıktısı:

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

1
Bu, 2019 Bahar versiyonuyla çalışır ve vücudu sağlam tutar.
Udo

1
Bahar 2.1.10 üzerinde çalışıyor :) Teşekkürler
Moler

8

application.properties

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

application.yml

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

8

Bu doğru bir yol olmayabilir, ancak günlükleri çok fazla doldurmadan istekleri ve yanıtları yazdırmak için bu en basit yaklaşım olduğunu düşünüyorum.

Application.properties, 2 satırın altına eklenerek, istekleri kaydetmek için 1. satırı ve yanıtları kaydetmek için 2. satırı kaydeder.

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

Günlük yanıtları benim için çalışmıyor. Sadece durum kodunu kaydeder. Yükü kaydetmeli mi?
badera

HttpEntityMethodProcessor (v5.1.8) sınıfı hiçbir şey kaydetmiyor.
Chris


4

Buradaki birçok yanıt kodlama değişiklikleri ve özelleştirilmiş sınıflar gerektiriyor ve gerçekten gerekli değil. Fiddler gibi bir hata ayıklama proxy'sini alın ve java ortamınızı proxy'yi komut satırında (-Dhttp.proxyHost ve -Dhttp.proxyPort) kullanacak şekilde ayarlayın ve ardından istekleri ve yanıtları bütünüyle görebilirsiniz. Ayrıca, sunucunun değiştirilmesine başlamadan önce deneyleri çalıştırmak için gönderilmeden önce ve sonra sonuçları ve yanıtları tamir etme yeteneği gibi birçok yardımcı avantaj ile birlikte gelir.

Gelebilecek bir sorunun son biti HTTPS kullanmanız gerekiyorsa, SSL sertifikasını kemancıdan dışa aktarmanız ve java anahtar deposuna (cacerts) ipucu almanız gerekir: varsayılan java anahtar deposu şifresi genellikle "changeit" tir.


1
Bu benim için intellij ve düzenli keman kurulumu kullanarak işe yaradı. Yapılandırmayı Çalıştır'ı düzenledim ve VM seçeneklerini ayarladım -DproxySet=true -Dhttp.proxyHost=localhost -Dhttp.proxyPort=8888.
JD

Teşekkürler! Bu, kendi Interceptor'unuzu yazmaya kıyasla oldukça zarif bir çözümdür.
Chris

3

Apache HttpClient'in yardımıyla Logback'e giriş yapmak için :

Sınıf yolunda Apache HttpClient'e ihtiyacınız var:

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

RestTemplateHttpClient kullanacak şekilde yapılandırın :

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

İstekleri ve yanıtları günlüğe kaydetmek için, Logback yapılandırma dosyasına ekleyin:

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

Veya daha da fazla oturum açmak için:

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

Hangi geri dönüş yapılandırma dosyası?
G_V

1
@G_V logback.xml veya testler için logback-test.xml.
holmis83

Aynı zamanda çalışır org.apache.http.wire=DEBUGGözlerinde farklı application.propertiesşimdi
G_V

@G_V, Spring-Boot kullanıyorsanız. Cevabım Boot olmadan çalışıyor.
holmis83

2

Şu Verilerinizi yapılandırma hile RestTemplatebir ile BufferingClientHttpRequestFactoryherhangi kullanıyorsanız çalışmaz ClientHttpRequestInterceptorEğer önleyicilerin aracılığıyla oturum açmaya çalışıyoruz eğer ki. Bunun nedeni InterceptingHttpAccessor(hangi RestTemplatealt sınıfların) çalışma şeklidir .

Uzun öykü kısa ... sadece bu sınıfı yerine kullanın RestTemplate(bunun SLF4J günlük API'sını kullandığını, gerektiği gibi düzenlediğini unutmayın):

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

Katılıyorum, sadece bunu yapmak için bu kadar iş gerektiriyor.


2

Yukarıdaki tartışmaya ek olarak, bu sadece Mutlu senaryoları temsil eder. büyük olasılıkla bir Hata gelirse yanıtı günlüğe kaydedemezsiniz.

Bu durumda artı yukarıdaki tüm durumlar DefaultResponseErrorHandler'i geçersiz kılmalı ve aşağıdaki gibi ayarlamalısınız

restTemplate.setErrorHandler(new DefaultResponseErrorHandlerImpl());

2

Garip bir şekilde, RestTemplate bazı istemci ve sunucu 500x hatalarında yanıt döndürmüyor gibi bu çözümlerin hiçbiri işe yaramaz. Bu durumda, ResponseErrorHandler uygulamasını aşağıdaki gibi uygulayarak bunları da günlüğe kaydedersiniz. İşte bir taslak kod, ama nokta olsun:

Hata işleyiciyle aynı yakalayıcıyı ayarlayabilirsiniz:

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

Ve kesme noktası her iki arayüzü de uygular:

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

Gövde çok parçalı / form verisi ise, ikili verileri (dosya içeriği) günlükten filtrelemenin kolay bir yolu var mı?
Luke

1

@MilacH'nin işaret ettiği gibi, uygulamada bir hata var. Bir statusCode> 400 döndürülürse, errorHandler çağrılmadığı için yakalayıcılardan bir IOException oluşturulur. İstisna yok sayılabilir ve daha sonra işleyici yönteminde tekrar yakalanır.

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

}

0

Şimdi en iyi çözüm, sadece bağımlılık ekleyin:

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

RestTemplate'inize bu şekilde ekleyebileceğiniz bir LoggingRequestInterceptor sınıfı içerir:

bu yardımcı programı, bir yay RestTemplate'e önleme olarak ekleyerek aşağıdaki şekilde entegre edin:

restTemplate.setRequestFactory(LoggingRequestFactoryFactory.build());

ve çerçevenize log4j gibi bir slf4j uygulaması ekleyin.

veya doğrudan "Zg2proRestTemplate" kullanın . @PaulSabou tarafından "en iyi cevap" öyle görünüyor, çünkü httpclient ve tüm apache.http kütüphaneleri bir yay RestTemplate kullanılırken yüklenmez.


yayınlanan sürüm nedir?
popalka

yayınlanan sürüm şimdi 0.2
Moses Meyer

1
kullanım kolaylığı harika, ama üstbilgileri yok
WrRaThY

ek olarak: LoggingRequestInterceptor'daki tüm yararlı yöntemler özeldir, bu uzantı söz konusu olduğunda (korunabilir) bir
sorundur

ne yazık ki, 5 dakika sonra yorumları düzenleyemiyorum. Eğer başlıklarını günlüğe yapmanız gereken şudur: log("Headers: {}", request.headers)yer LoggingRequestInterceptor:traceRequestve log("Headers: {}", response.headers)içinde LoggingRequestInterceptor:logResponse. Günlük başlıklarını ve gövdesini eklemek için bazı bayraklar eklemeyi düşünebilirsiniz. Ayrıca, günlük içeriği için gövde içeriği türünü denetlemek isteyebilirsiniz (örneğin, yalnızca günlük uygulama / json *). Bu da yapılandırılabilir olmalıdır. Sonuçta, bu küçük tweaks ile yaymak için güzel bir kütüphane olacak. iyi iş :)
WrRaThY

0

Bu benim uygulama eklemek de istedim. Tüm eksik noktalı virgüller için özür dilerim, bu Groovy dilinde yazılmış.

Kabul edilen cevaptan daha yapılandırılabilir bir şeye ihtiyacım vardı. İşte çok çevik olan ve OP'nin aradığı gibi her şeyi kaydeden bir dinlenme şablonu fasulyesi.

Özel Günlük Durdurma Sınıfı:

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

Dinlenme Şablonu Fasulye Tanımı:

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

Uygulama:

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


0

org.apache.http.wire vermek çok okunmaz günlükleri, kullandığım bu yüzden seyir defteri günlüğe uygulama Servlet ve RestTemplate req / solunum günlüğüne

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

}
}

-1

ClientHttpInterceptor kullanan yanıtla ilgili olarak, tüm yanıtı Tamponlama fabrikaları olmadan tutmanın bir yolunu buldum. Yanıt gövdesi giriş akışını, o diziyi gövdeden kopyalayacak bazı utils yöntemini kullanarak bayt dizisi içinde saklayın, ancak önemli, yanıt boşsa (Kaynak Erişimi Özel Durumunun nedeni budur) kırılacağı için bu yöntemi try catch ile çevreleyin ve catch sadece boş bayt dizisi oluşturmak ve sadece o dizi ve orijinal yanıttan diğer parametreleri kullanarak ClientHttpResponse anonim iç sınıf oluşturmak. Daha sonra bu yeni ClientHttpResponse nesnesini geri kalan şablon yürütme zincirine döndürebilir ve daha önce depolanan body byte dizisini kullanarak yanıtı günlüğe kaydedebilirsiniz. Bu şekilde, gerçek yanıtta InputStream'i tüketmekten kaçınacak ve Şablon Şablonu yanıtını olduğu gibi kullanabilirsiniz. Not,


-2

Logger yapılandırmam xml kullandı

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

o zaman aşağıdaki gibi bir şey alacaksınız:

DEBUG org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:92) : Reading [com.test.java.MyClass] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@604525f1]

HttpMessageConverterExtractor.java:92, hata ayıklamaya devam etmeniz gerekiyor ve benim durumumda, bunu aldım:

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

ve bu:

outputMessage.getBody().flush();

outputMessage.getBody (), http (posta türü) gönderdiği mesajı içerir


izleme günlüğü çok ayrıntılı olabilir ... ya saniyede binlerce istek varsa ??
Gervasio Amy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.