Spring RestTemplate - วิธีเปิดใช้งานการดีบัก / การบันทึกคำขอ / การตอบกลับอย่างสมบูรณ์หรือไม่


220

ฉันใช้ Spring RestTemplate อยู่พักหนึ่งแล้วฉันก็ชนกำแพงอย่างต่อเนื่องเมื่อฉันพยายามที่จะดีบั๊กมันเป็นคำขอและคำตอบ โดยทั่วไปฉันกำลังมองหาสิ่งเดียวกันกับที่ฉันเห็นเมื่อฉันใช้ขดตัวเลือก "verbose" ที่เปิดอยู่ ตัวอย่างเช่น :

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

จะแสดงทั้งข้อมูลที่ส่งและข้อมูลที่ได้รับ (รวมถึงส่วนหัวคุกกี้ ฯลฯ )

ฉันได้ตรวจสอบโพสต์ที่เกี่ยวข้องเช่น: ฉันจะบันทึกการตอบสนองใน Spring RestTemplate ได้อย่างไร แต่ฉันไม่สามารถแก้ไขปัญหานี้ได้

วิธีหนึ่งในการทำเช่นนี้คือการเปลี่ยนรหัสต้นฉบับของ RestTemplate และเพิ่มคำสั่งการบันทึกพิเศษที่นั่น แต่ฉันจะพบว่าวิธีนี้เป็นวิธีสุดท้าย ควรมีวิธีบอก Spring Web Client / RestTemplate เพื่อบันทึกทุกอย่างด้วยวิธีที่เป็นมิตรกว่านี้

เป้าหมายของฉันจะสามารถทำเช่นนี้ด้วยรหัสเช่น:

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

และจากนั้นรับข้อมูล debug ชนิดเดียวกัน (ตามที่ได้รับด้วย curl) ในล็อกไฟล์หรือในคอนโซล ฉันเชื่อว่านี่จะเป็นประโยชน์อย่างยิ่งสำหรับทุกคนที่ใช้ Spring RestTemplate และมีปัญหา การใช้ curl เพื่อดีบักปัญหา RestTemplate ของคุณก็ใช้ไม่ได้ (ในบางกรณี)


31
คำเตือนสำหรับทุกคนที่อ่านในปี 2561: ไม่มีคำตอบง่ายๆสำหรับเรื่องนี้!
davidfrancis

3
วิธีที่ง่ายที่สุดคือการใช้เบรกพอยต์ในวิธีเขียน (... ) ของคลาส AbstractHttpMessageConverter มีวัตถุ outputMessage ที่คุณสามารถดูข้อมูลได้ PS คุณสามารถคัดลอกค่าแล้วจัดรูปแบบด้วยตัวจัดรูปแบบออนไลน์
Sergey Chepurnov

1
ดูเหมือนว่าเรื่องนี้ควรจะง่ายในฤดูใบไม้ผลิ แต่ตัดสินจากคำตอบที่นี่ไม่ใช่กรณี ดังนั้นวิธีแก้ปัญหาอื่น ๆ ก็คือการข้าม Spring ทั้งหมดและใช้เครื่องมือเช่น Fiddler เพื่อจับการร้องขอ / การตอบสนอง
michaelok

อ่านคำตอบสำหรับคำถามนี้ได้จากลิงค์ต่อไปนี้: spring-resttemplate- วิธีการเปิดใช้งานเต็มการดีบัก - บันทึกการร้องขอ - ตอบสนอง
Solanki Vaibhav

กรกฎาคม 2019: ในฐานะที่ยังคงไม่มีวิธีแก้ง่าย ๆ กับคำถามนี้ผมพยายามที่จะให้ข้อมูลสรุปของอื่น ๆ 24 คำตอบ (ไกล) และแสดงความคิดเห็นของพวกเขาและการอภิปรายในคำตอบของตัวเองด้านล่าง หวังว่ามันจะช่วย
Chris

คำตอบ:


207

เพียงเพื่อทำตัวอย่างให้สมบูรณ์โดยใช้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โดยใช้ a BufferingClientHttpRequestFactoryและLoggingRequestInterceptor:

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

BufferingClientHttpRequestFactoryจะต้องเป็นเราต้องการใช้ร่างกายตอบสนองทั้งใน interceptor และสำหรับรหัสโทรเริ่มต้น การใช้งานเริ่มต้นช่วยให้การอ่านเนื้อหาการตอบสนองเพียงครั้งเดียว


27
นี่เป็นสิ่งที่ผิด หากคุณอ่านสตรีมรหัสแอปพลิเคชันจะไม่สามารถอ่านการตอบสนองได้
James Watkins

28
เราได้ให้ RestTemplate BufferingClientHttpRequestFactory เพื่อให้เราสามารถอ่านการตอบสนองได้สองครั้ง
sofiene zaghdoudi

16
เราใช้เทคนิคนี้มาประมาณ 3 เดือนแล้ว ใช้งานได้กับ RestTemplate ที่กำหนดค่าด้วยBufferingClientHttpResponseWrapper@sofienezaghdoudi แต่ก็ไม่ทำงานเมื่อใช้ในการทดสอบโดยใช้กรอบ mockServer ฤดูใบไม้ผลิตั้งแต่MockRestServiceServer.createServer(restTemplate)เขียนทับ RequestFactory InterceptingClientHttpRequestFactoryไป
RubesMN

8
เทคนิคดีใช้งานผิด 404 case, response.getBody () โยน IOException -> คุณไม่เคยได้รับ log out และแย่ที่สุดมันจะกลายเป็น ResourceAccessException ในโค้ดของคุณแทนที่จะเป็น RestClientResponseException
MilacH

6
ขอบคุณสำหรับการตอบกลับ. แต่นี่เป็นวิธีปฏิบัติที่ไม่ดีที่จะมี "log.debug" หลายรายการเนื่องจากสามารถแพร่กระจายได้มากกว่าบันทึกอื่น ๆ จำนวนมาก จะดีกว่าถ้าใช้คำสั่ง log.debug เพียงครั้งเดียวดังนั้นคุณมั่นใจได้ว่าทุกอย่างอยู่ในที่เดียวกัน
2447161

129

ใน 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ดูเหมือนว่าจะมีข้อมูลที่เกี่ยวข้องทั้งหมด


4
นี่เป็นสิ่งที่ง่ายที่สุดที่ฉันต้องการ ฉันขอแนะนำให้รวมถึงสิ่งนี้ในคำตอบที่ได้รับการยอมรับ
michaelavila

22
ตาม Javadoc ของ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 ไม่ได้ใช้คลาส Apache เหล่านี้เป็นค่าเริ่มต้นตามที่ระบุไว้โดย @OrtomalaLokni ดังนั้นคุณควรรวมถึงวิธีการใช้นอกเหนือไปจากวิธีการพิมพ์การดีบักเมื่อมีการใช้งาน
Captain Man

ฉันได้รับเช่นนี้http-outgoing-0 << "[0x1f][0x8b][0x8][0x0][0x0][0x0][0x0][0x0]
Partha Sarathi กอช

2
@ParthaSarathiGhosh เนื้อหาอาจมีการเข้ารหัส gzip ซึ่งเป็นสาเหตุที่คุณไม่เห็นข้อความดิบ
Matthew Buckett

79

การขยาย @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());

สิ่งนี้ไม่สามารถใช้ได้จนกว่าจะถึงเวอร์ชั่นฤดูใบไม้ผลิ 3.1
Gyan

3
ไม่ตอบคำถามของ 'การตอบกลับการบันทึก' แต่ให้ // ความคิดเห็นการบันทึกแทน
Jiang YD

1
การบันทึกเป็นเรื่องง่าย แต่วิธีนี้ใช้ได้เฉพาะกับคำขอเท่านั้นฉันไม่เห็นหน่วยตอบรับสมมติว่าฉันมีออบเจ็กต์การตอบสนอง แต่การอ่านสตรีมนั้นไม่ใช่ความคิดที่ดี
Pavel Niedoba

11
@PavelNiedoba The BufferClientHttpRequestFactory อนุญาตให้ตอบกลับอ่านมากกว่าหนึ่งครั้ง
mjj1409

2
วิธีนี้ใช้งานได้ดีหากคุณต้องการเก็บข้อมูลเกี่ยวกับคำขอ / ตอบกลับในฐานข้อมูลสำหรับการดีบักและการบันทึกปกติไม่เหมาะกับความต้องการของคุณ
GameSalutes

32

ทางออกที่ดีที่สุดของคุณคือการเพิ่มลงlogging.level.org.springframework.web.client.RestTemplate=DEBUGในapplication.propertiesไฟล์

โซลูชันอื่น ๆ เช่นการตั้งค่าlog4j.logger.httpclient.wireจะไม่ทำงานเสมอเพราะจะถือว่าคุณใช้log4jและ Apache HttpClientซึ่งไม่จริงเสมอ

อย่างไรก็ตามโปรดทราบว่าไวยากรณ์นี้จะใช้ได้เฉพาะกับ Spring Boot เวอร์ชันล่าสุดเท่านั้น


5
นี่ไม่ได้ทำการบันทึกคำขอและเนื้อหาการตอบสนองเพียงแค่ url และประเภทคำขอ (spring-web-4.2.6)
dve

1
คุณถูกต้องไม่ใช่การwireเข้าสู่ระบบ แต่จะรวมเฉพาะข้อมูลที่จำเป็นเช่น url, โค้ด resepone, พารามิเตอร์ POST เป็นต้น
gamliela

1
สิ่งที่คุณต้องการจริงๆคือstackoverflow.com/a/39109538/206466
xenoterracide

นี่เป็นเรื่องปกติ แต่ไม่สามารถมองเห็นเนื้อหาการตอบสนองได้!
sunleo

สุกใส แม้ว่ามันจะไม่พิมพ์เนื้อหาการตอบสนอง แต่ก็ยังมีประโยชน์มาก ขอบคุณ.
Chris

30

ไม่มีคำตอบเหล่านี้แก้ปัญหาได้ 100% mjj1409 ได้รับประโยชน์ส่วนใหญ่ แต่หลีกเลี่ยงปัญหาในการบันทึกการตอบสนองซึ่งจะใช้งานได้อีกเล็กน้อย Paul Sabou เสนอวิธีแก้ปัญหาที่ดูเหมือนจริง แต่ไม่ได้ให้รายละเอียดเพียงพอที่จะใช้งานจริง (และมันก็ไม่ได้ผลเลยสำหรับฉัน) Sofiene ได้รับการบันทึก แต่มีปัญหาที่สำคัญ: การตอบสนองไม่สามารถอ่านได้อีกต่อไปเนื่องจากอินพุตสตรีมถูกใช้ไปแล้ว!

ฉันขอแนะนำให้ใช้ BufferingClientHttpResponseWrapper เพื่อห่อวัตถุตอบกลับเพื่อให้สามารถอ่านเนื้อหาตอบกลับได้หลายครั้ง:

public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor {

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

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

        response = log(request, body, response);

        return response;
    }

    private ClientHttpResponse log(final HttpRequest request, final byte[] body, final ClientHttpResponse response) {
        final ClientHttpResponse responseCopy = new BufferingClientHttpResponseWrapper(response);
        logger.debug("Method: ", request.getMethod().toString());
        logger.debug("URI: ", , request.getURI().toString());
        logger.debug("Request Body: " + new String(body));
        logger.debug("Response body: " + IOUtils.toString(responseCopy.getBody()));
        return responseCopy;
    }

}

สิ่งนี้จะไม่ใช้ InputStream เนื่องจากเนื้อหาการตอบสนองถูกโหลดลงในหน่วยความจำและสามารถอ่านได้หลายครั้ง หากคุณไม่มี BufferingClientHttpResponseWrapper ใน classpath ของคุณคุณสามารถค้นหาการใช้งานอย่างง่ายได้ที่นี่:

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

เดียวกัน responseCopy.getBody () โยน IOexception ในกรณีของ 404 ดังนั้นคุณจะไม่ส่งรหัสตอบกลับไปให้คุณอีกต่อไปและ RestClientResponseException ปกติจะกลายเป็น ResourceAccessException
MilacH

1
คุณควรตรวจสอบstatus==200ก่อนresponseCopy.getBody()
Anand Rockzz

4
แต่มันเป็นแพคเกจส่วนตัว คุณใส่ LoggingRequestInterceptor ของคุณในแพ็คเกจ 'org.springframework.http.client' หรือไม่
zbstof

2
แล้วasyncRestTemplateไงล่ะ มันจะต้องกลับมาListenableFutureเมื่อคุณตัดมันซึ่งเป็นไปไม่ได้ที่จะเปลี่ยนแปลงด้วยBufferingClientHttpResponseWrapperในการโทรกลับ
Ömer Faruk Almalı

@ ÖmerFarukAlmalıในกรณีนี้คุณจะต้องใช้เชนหรือแปลงขึ้นอยู่กับเวอร์ชั่นของ Guava ที่คุณใช้ ดู: stackoverflow.com/questions/8191891/…
James Watkins

30

คุณสามารถใช้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()

ตรวจสอบให้แน่ใจว่าเปิดใช้งานการบันทึกการดีบักในapplication.properties:

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

ตอนนี้ทราฟฟิก HTTP ของ RestTemplate ทั้งหมดจะถูกบันทึกไว้org.hobsoft.spring.resttemplatelogger.LoggingCustomizerที่ระดับการดีบัก

หมายเหตุ: ฉันเขียนห้องสมุดนี้


ทำไมคำตอบนี้จึงถูกลดระดับลง? มันช่วยฉัน ขอบคุณ @ Mark Hobson
Raffael Bechara Rameh

3
ดีใจที่ได้ช่วย @RaffaelBecharaRameh ตอนแรกมันถูกลดระดับลงเพราะฉันไม่ได้ฝังคำแนะนำจากโครงการที่เชื่อมโยง รู้สึกฟรีเพื่อโหวตถ้าคุณพบว่ามีประโยชน์!
Mark Hobson

คุณสนับสนุนผ่าน Gradle หรือไม่
BlackHatSamurai

1
@BlackHatSamurai spring-rest-template-logger เป็นสิ่งประดิษฐ์ Maven ปกติดังนั้นจึงควรทำงานได้ดีกับ Gradle
Mark Hobson

1
สวัสดี @erhanasikoglu ยินดีต้อนรับ! ถูกต้องคุณสามารถดูได้ที่นี่: github.com/markhobson/spring-rest-template-logger/blob/master/ ......
Mark Hobson

29

วิธีแก้ปัญหาที่กำหนดโดย xenoterracide ให้ใช้

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

วิธีที่ง่ายที่สุดฉันจะเพิ่มว่ามันไม่ทำงานกับ MockRestServiceServer เพราะมันเขียนทับคำขอของโรงงาน
zbstof

ทำงานได้ดีและไม่มีปัญหาการตั้งค่าน้อย!
sunleo

26

ในที่สุดฉันก็พบวิธีที่จะทำสิ่งนี้ในวิธีที่ถูกต้อง โซลูชันส่วนใหญ่มาจาก ฉันจะกำหนดค่า Spring และ SLF4J เพื่อให้สามารถบันทึกได้อย่างไร

ดูเหมือนว่ามีสองสิ่งที่ต้องทำ:

  1. เพิ่มบรรทัดต่อไปนี้ใน log4j.properties: log4j.logger.httpclient.wire=DEBUG
  2. ตรวจสอบให้แน่ใจว่าสปริงไม่เพิกเฉยต่อการกำหนดค่าการบันทึกของคุณ

ปัญหาที่สองเกิดขึ้นส่วนใหญ่กับสภาพแวดล้อมในฤดูใบไม้ผลิที่ใช้ slf4j (เหมือนกรณีของฉัน) เช่นนี้เมื่อใช้ slf4j ตรวจสอบให้แน่ใจว่ามีสองสิ่งต่อไปนี้เกิดขึ้น:

  1. ไม่มีไลบรารีการบันทึกแบบคอมมอนใน classpath ของคุณ: สามารถทำได้โดยการเพิ่ม descriptor การยกเว้นใน pom ของคุณ:

            <exclusions><exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
  2. ไฟล์ log4j.properties ถูกเก็บไว้ที่ไหนสักแห่งใน classpath ที่ spring สามารถค้นหา / ดูได้ หากคุณมีปัญหากับสิ่งนี้ทางออกสุดท้ายคือการใส่ไฟล์ log4j.properties ในแพ็คเกจเริ่มต้น (ไม่ใช่วิธีปฏิบัติที่ดี แต่เพียงเพื่อดูว่าสิ่งต่าง ๆ ทำงานตามที่คุณคาดหวัง


7
สิ่งนี้ไม่ได้ผลสำหรับฉันฉันทำทั้งสองอย่าง ฉันไม่เข้าใจว่าทำไมฉันต้องใส่ log4j.properties เมื่อมันไม่ได้ใช้ในโครงการของฉัน (ตรวจสอบโดยการพึ่งพา mvn: ต้นไม้)
Pavel Niedoba

มันไม่ได้ผลสำหรับฉันเช่นกัน ฉันได้ลองตั้งค่า root logger เป็นโหมด Debug และยังคงไม่มีอะไร
James Watkins

"httpclient.wire.content" และ "httpclient.wire.header" เป็นชื่อตัวบันทึกจากเฟรมเวิร์ก Axis2 สามารถใช้บันทึกได้เช่นคำขอ SOAP ในโครงการ Spring หากดำเนินการโดยใช้ Axis2
lathspell

11
httpclient.wireมาจากไลบรารี Apache HttpComponents HttpClient (ดูที่hc.apache.org/httpcomponents-client-ga/logging.html ) เทคนิคนี้จะใช้ได้เฉพาะเมื่อคุณRestTemplateกำหนดค่าให้ใช้งานHttpComponentsClientHttpRequestFactory
Scott Frederick

22

การบันทึก RestTemplate

ตัวเลือก 1. เปิดการบันทึกการดีบัก

กำหนดค่า RestTemplate

  • โดยค่าเริ่มต้นRestTemplateอาศัยสิ่งอำนวยความสะดวก JDK มาตรฐานเพื่อสร้างการเชื่อมต่อ HTTP คุณสามารถสลับไปใช้ไลบรารี HTTP อื่นเช่น Apache HttpComponents

    @Bean พับลิก RestTemplate restTemplate (RestTemplateBuilder builder) {RestTemplate restTemplate = builder.build (); ส่วนที่เหลือกลับ }

กำหนดค่าการบันทึก

  • application.yml

    การบันทึก: ระดับ: org.springframework.web.client.RestTemplate: DEBUG

ตัวเลือก 2 การใช้ Interceptor

การตอบสนองของ Wrapper

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StreamUtils;

public final class BufferingClientHttpResponseWrapper implements ClientHttpResponse {

    private final ClientHttpResponse response;

    private byte[] body;


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

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

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

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

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

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

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

ใช้ Interceptor

package com.example.logging;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;

public class LoggingRestTemplate implements ClientHttpRequestInterceptor {

    private final static Logger LOGGER = LoggerFactory.getLogger(LoggingRestTemplate.class);

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

    private void traceRequest(HttpRequest request, byte[] body) throws IOException {
        if (!LOGGER.isDebugEnabled()) {
            return;
        }
        LOGGER.debug(
                "==========================request begin==============================================");
        LOGGER.debug("URI                 : {}", request.getURI());
        LOGGER.debug("Method            : {}", request.getMethod());
        LOGGER.debug("Headers         : {}", request.getHeaders());
        LOGGER.debug("Request body: {}", new String(body, "UTF-8"));
        LOGGER.debug(
                "==========================request end================================================");
    }

    private ClientHttpResponse traceResponse(ClientHttpResponse response) throws IOException {
        if (!LOGGER.isDebugEnabled()) {
            return response;
        }
        final ClientHttpResponse responseWrapper = new BufferingClientHttpResponseWrapper(response);
        StringBuilder inputStringBuilder = new StringBuilder();
        BufferedReader bufferedReader = new BufferedReader(
                new InputStreamReader(responseWrapper.getBody(), "UTF-8"));
        String line = bufferedReader.readLine();
        while (line != null) {
            inputStringBuilder.append(line);
            inputStringBuilder.append('\n');
            line = bufferedReader.readLine();
        }
        LOGGER.debug(
                "==========================response begin=============================================");
        LOGGER.debug("Status code    : {}", responseWrapper.getStatusCode());
        LOGGER.debug("Status text    : {}", responseWrapper.getStatusText());
        LOGGER.debug("Headers            : {}", responseWrapper.getHeaders());
        LOGGER.debug("Response body: {}", inputStringBuilder.toString());
        LOGGER.debug(
                "==========================response end===============================================");
        return responseWrapper;
    }

}

กำหนดค่า RestTemplate

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

กำหนดค่าการบันทึก

  • ตรวจสอบแพ็คเกจของ LoggingRestTemplate ตัวอย่างเช่นในapplication.yml:

    การบันทึก: ระดับ: 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


เพียงแค่ทราบ: หากคุณต้องการกำหนดค่ากำหนดTestRestTemplateค่าRestTemplateBuilder: @Bean public RestTemplateBuilder restTemplateBuilder () {return RestTemplateBuilder ใหม่ (). เพิ่มเติมInterceptors (Collections.singletonList (LoggingRestTemplate ใหม่) () }
kingoleg

โปรดทราบว่า InputStreamReader ใหม่ (responseWrapper.getBody (), StandardCharsets.UTF_8)) สามารถโยนข้อผิดพลาดหาก "ปลายอื่น ๆ " กลับข้อผิดพลาด คุณอาจต้องการวางลงในบล็อกลอง
PeterS

16

---- กรกฎาคม 2019 ----

(ใช้ Spring Boot)

ฉันรู้สึกประหลาดใจที่ Spring Boot ซึ่งทั้งหมดนี้เป็นเวทย์มนตร์ Zero Configuration ไม่ได้ให้วิธีง่ายๆในการตรวจสอบหรือบันทึกเนื้อหาการตอบสนอง JSON แบบง่ายๆด้วย RestTemplate ฉันดูคำตอบและข้อคิดเห็นต่าง ๆ ที่มีให้ที่นี่และฉันกำลังแชร์เวอร์ชันกลั่นของฉันเองว่าอะไร (ยัง) ทำงานได้และดูเหมือนว่าฉันจะเป็นทางออกที่สมเหตุสมผลให้ตัวเลือกปัจจุบัน (ฉันใช้ Spring Boot 2.1.6 กับ Gradle 4.4 )

1. ใช้ Fiddler เป็น http proxy

นี่เป็นวิธีการแก้ปัญหาที่สง่างามเพราะมันข้ามความพยายามทั้งหมดที่ยุ่งยากในการสร้าง interceptor ของคุณเองหรือเปลี่ยนไคลเอนต์ http พื้นฐานเป็น apache (ดูด้านล่าง)

ติดตั้งและเรียกใช้Fiddler

แล้ว

เพิ่ม-DproxySet=true -Dhttp.proxyHost=localhost -Dhttp.proxyPort=8888ไปยังตัวเลือก VM ของคุณ

2. การใช้ Apache HttpClient

เพิ่ม Apache HttpClient ให้กับการอ้างอิง Maven หรือ Gradle ของคุณ

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

ใช้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-loggingคุณจะต้องให้แน่ใจว่าคุณมีกรอบการเข้าสู่ระบบการตั้งค่าเช่นโดยใช้การพึ่งพาฤดูใบไม้ผลิบูตเริ่มต้นที่มี

3. ใช้ Interceptor

ฉันจะให้คุณอ่านข้อเสนอตอบโต้ข้อเสนอและ gotchas ในคำตอบและความคิดเห็นอื่น ๆ และตัดสินใจด้วยตัวเองหากคุณต้องการลงไปที่เส้นทางนั้น

4. บันทึก URL และสถานะการตอบกลับโดยไม่มีเนื้อหา

แม้ว่าจะไม่เป็นไปตามข้อกำหนดที่ระบุไว้ในการบันทึกเนื้อความ แต่เป็นวิธีที่ง่ายและรวดเร็วในการเริ่มบันทึกการโทร REST ของคุณ จะแสดง URL แบบเต็มและสถานะการตอบสนอง

เพียงเพิ่มบรรทัดต่อไปนี้ลงในapplication.propertiesไฟล์ของคุณ(สมมติว่าคุณใช้ Spring Boot และสมมติว่าคุณใช้การเริ่มต้นใช้งานสปริงบูทที่มี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]

2
หมายเลข 4 เป็นวิธีที่ง่ายที่สุดในการดีบัก
Yubaraj

1
หมายเลข 2 ทำงานให้ฉัน มันบันทึกเนื้อหาของคำขอ ขอบคุณ!
caglar

1
ฉันพบหมายเลข 3 เป็นวิธีที่ง่ายในการทำเช่นนี้เมื่อฉันมาถึงปัญหานี้
Bill Naylor

12

นอกจากการบันทึก HttpClient ที่อธิบายไว้ในคำตอบอื่น ๆคุณยังสามารถแนะนำ ClientHttpRequestInterceptor ที่อ่านเนื้อหาของคำขอและการตอบสนองและบันทึก คุณอาจต้องการทำสิ่งนี้หากสิ่งอื่น ๆ ใช้ HttpClient หรือถ้าคุณต้องการรูปแบบการบันทึกที่กำหนดเอง ข้อควรระวัง: คุณจะต้องให้ RestTemplate BufferingClientHttpRequestFactory เพื่อให้คุณสามารถอ่านการตอบสนองได้สองครั้ง


12

ตามที่ระบุไว้ในคำตอบอื่น ๆ ร่างกายตอบสนองต้องการการดูแลเป็นพิเศษเพื่อให้สามารถอ่านซ้ำ ๆ (โดยค่าเริ่มต้นเนื้อหาที่ได้รับการบริโภคในการอ่านครั้งแรก)

แทนที่จะใช้การBufferingClientHttpRequestFactoryตั้งค่าเมื่อร้องขอ Interceptor เองสามารถห่อคำตอบและตรวจสอบให้แน่ใจว่าเนื้อหานั้นถูกเก็บไว้และสามารถอ่านซ้ำได้ (โดยคนตัดไม้และผู้บริโภคที่ตอบรับ):

เครื่องดักฟังของฉันซึ่ง

  • บัฟเฟอร์ร่างกายการตอบสนองโดยใช้เสื้อคลุม
  • บันทึกในวิธีกะทัดรัดมากขึ้น
  • บันทึกตัวระบุรหัสสถานะด้วย (เช่นสร้างขึ้น 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", ...  }

1
อันนี้ใช้ได้กับเวอร์ชั่น 2019 Spring ทำให้ร่างกายไม่เสียหาย
Udo Held

1
ใช้งานได้กับ Spring 2.1.10 :) ขอบคุณ
Moler


8

นี่อาจไม่ใช่วิธีที่ถูกต้อง แต่ฉันคิดว่านี่เป็นวิธีที่ง่ายที่สุดในการพิมพ์คำขอและการตอบสนองโดยไม่ต้องกรอกข้อมูลลงในบันทึกมากเกินไป

โดยเพิ่มแอปพลิเคชั่นด้านล่าง 2 บรรทัดบันทึกการร้องขอและการตอบกลับทั้งหมดบรรทัดที่ 1 เพื่อบันทึกคำขอและบรรทัดที่ 2 เพื่อบันทึกการตอบกลับ

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

การตอบกลับการบันทึกไม่ทำงานสำหรับฉัน มันเป็นเพียงบันทึกรหัสสถานะ มันควรจะบันทึกน้ำหนักบรรทุก?
badera

คลาส HttpEntityMethodProcessor (v5.1.8) ไม่บันทึกสิ่งใด
Chris

6

สมมติว่าRestTemplateมีการกำหนดค่าให้ใช้ HttpClient 4.x คุณสามารถอ่านเอกสารการบันทึกของ HttpClient ได้ที่นี่ที่นี่คนตัดไม้นั้นแตกต่างจากที่ระบุไว้ในคำตอบอื่น ๆ

การกำหนดค่าการบันทึกสำหรับ HttpClient 3.x สามารถใช้ได้ที่นี่


4

คำตอบมากมายที่นี่จำเป็นต้องมีการเปลี่ยนแปลงการเข้ารหัสและคลาสที่กำหนดเองและไม่จำเป็นจริงๆ รับพร็อกซีการดีบักเช่น fiddler และตั้งค่าสภาพแวดล้อม java ของคุณเพื่อใช้พร็อกซีบนบรรทัดคำสั่ง (-Dhttp.proxyHost และ -Dhttp.proxyPort) จากนั้นเรียกใช้พู้ทำเล่นและคุณสามารถเห็นคำขอและการตอบสนองทั้งหมด ยังมาพร้อมกับข้อได้เปรียบมากมายเช่นความสามารถในการคนจรจัดกับผลลัพธ์และการตอบสนองก่อนและหลังพวกเขาจะถูกส่งไปทำงานทดลองก่อนที่จะกระทำการปรับเปลี่ยนเซิร์ฟเวอร์

บิตสุดท้ายของปัญหาที่อาจเกิดขึ้นคือถ้าคุณต้องใช้ HTTPS คุณจะต้องส่งออกใบรับรอง SSL จากพู้ทำเล่นและนำเข้าลงในคำแนะนำ java keystore (cacerts): รหัสผ่านเริ่มต้น java keoreore มักจะเป็น "changeit"


1
สิ่งนี้ใช้ได้สำหรับฉันโดยใช้ intellij และการติดตั้งซอธรรมดา ฉันแก้ไขการเรียกใช้การกำหนดค่าและชุด VM -DproxySet=true -Dhttp.proxyHost=localhost -Dhttp.proxyPort=8888ตัวเลือกในการ
JD

ขอบคุณ! นี่เป็นโซลูชันที่สง่างามเมื่อเทียบกับการเขียน Interceptor ของคุณเอง
Chris

3

สำหรับการเข้าสู่Logbackด้วยความช่วยเหลือจาก Apache HttpClient:

คุณต้อง Apache HttpClient ใน classpath:

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

ไฟล์การกำหนดค่า logback ใด
G_V

1
@G_V logback.xml หรือ logback-test.xml สำหรับการทดสอบ
holmis83

นอกจากนี้ยังใช้งานได้org.apache.http.wire=DEBUGในapplication.propertiesตอนนี้ของคุณ
G_V

@G_V หากคุณใช้ Spring-Boot คำตอบของฉันทำงานได้โดยไม่ต้องบูต
holmis83

2

เคล็ดลับในการกำหนดค่าของคุณRestTemplateด้วย a 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();
    }
}

ฉันยอมรับว่ามันงี่เง่าที่จะต้องใช้เวลามากในการทำสิ่งนี้


2

การเพิ่มในการสนทนาข้างต้นนี้แสดงถึงสถานการณ์แฮปปี้เท่านั้น อาจเป็นไปได้ว่าคุณจะไม่สามารถบันทึกการตอบสนองหากเกิดข้อผิดพลาดเกิดขึ้น

ในกรณีนี้รวมถึงทุกกรณีข้างต้นคุณจะต้องแทนที่DefaultResponseErrorHandlerและตั้งค่าไว้ด้านล่าง

restTemplate.setErrorHandler(new DefaultResponseErrorHandlerImpl());

2

น่าแปลกที่ไม่มีวิธีแก้ปัญหาเหล่านี้ในขณะที่ RestTemplate ดูเหมือนจะไม่ตอบกลับข้อผิดพลาดไคลเอ็นต์และเซิร์ฟเวอร์ 500x ในกรณีนี้คุณจะต้องบันทึกสิ่งเหล่านั้นด้วยการใช้ ResponseErrorHandler ดังนี้ นี่คือรหัสร่าง แต่คุณจะได้รับคะแนน:

คุณสามารถตั้งค่าตัวดักจับเดียวกับตัวจัดการข้อผิดพลาด:

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

และการสกัดกั้นนำทั้งสองอินเตอร์เฟส:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus.Series;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.ResponseErrorHandler;

public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor, ResponseErrorHandler {
    static final Logger log = LoggerFactory.getLogger(LoggingRequestInterceptor.class);
    static final DefaultResponseErrorHandler defaultResponseErrorHandler = new DefaultResponseErrorHandler();
    final Set<Series> loggableStatuses = new HashSet();

    public LoggingRequestInterceptor() {
    }

    public LoggingRequestInterceptor(Set<Series> loggableStatuses) {
        loggableStatuses.addAll(loggableStatuses);
    }

    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        this.traceRequest(request, body);
        ClientHttpResponse response = execution.execute(request, body);
        if(response != null) {
            this.traceResponse(response);
        }

        return response;
    }

    private void traceRequest(HttpRequest request, byte[] body) throws IOException {
        log.debug("===========================request begin================================================");
        log.debug("URI         : {}", request.getURI());
        log.debug("Method      : {}", request.getMethod());
        log.debug("Headers     : {}", request.getHeaders());
        log.debug("Request body: {}", new String(body, "UTF-8"));
        log.debug("==========================request end================================================");
    }

    private void traceResponse(ClientHttpResponse response) throws IOException {
        if(this.loggableStatuses.isEmpty() || this.loggableStatuses.contains(response.getStatusCode().series())) {
            StringBuilder inputStringBuilder = new StringBuilder();

            try {
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), "UTF-8"));

                for(String line = bufferedReader.readLine(); line != null; line = bufferedReader.readLine()) {
                    inputStringBuilder.append(line);
                    inputStringBuilder.append('\n');
                }
            } catch (Throwable var5) {
                log.error("cannot read response due to error", var5);
            }

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

    }

    public boolean hasError(ClientHttpResponse response) throws IOException {
        return defaultResponseErrorHandler.hasError(response);
    }

    public void handleError(ClientHttpResponse response) throws IOException {
        this.traceResponse(response);
        defaultResponseErrorHandler.handleError(response);
    }
}

จะเป็นอย่างไรถ้าร่างกายเป็นแบบหลายส่วน / แบบฟอร์มข้อมูลจะมีวิธีง่าย ๆ ในการกรองข้อมูลไบนารี (เนื้อหาไฟล์) จากบันทึกหรือไม่
ลุค

1

@MilacH ชี้ให้เห็นว่ามีข้อผิดพลาดในการใช้งาน หากส่งคืน statusCode> 400 IOException จะถูกส่งออกไปเนื่องจาก errorHandler ไม่ได้ถูกเรียกใช้จาก interceptors ข้อยกเว้นสามารถถูกละเว้นและถูกดักจับอีกครั้งในวิธีการจัดการ

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

ทางออกที่ดีที่สุดในตอนนี้เพียงเพิ่มการพึ่งพา:

<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 และ lib apache.http ทั้งหมดไม่จำเป็นต้องโหลดเมื่อใช้ RestTemplate แบบสปริง


รุ่นที่วางจำหน่ายคืออะไร?
popalka

รุ่นที่วางจำหน่ายในขณะนี้คือ 0.2
Moses Meyer

1
ความสะดวกในการใช้งานดีมาก แต่ไม่มีส่วนหัว
WrRaThY

นอกจากนี้: วิธีการที่มีประโยชน์ทั้งหมดใน LoggingRequestInterceptor เป็นส่วนตัวซึ่งเป็นปัญหาเมื่อมันมาถึงการขยาย (สามารถป้องกันได้)
WrRaThY

น่าเศร้าที่ฉันไม่สามารถแก้ไขความคิดเห็นหลังจาก 5 นาที ทั้งหมดที่คุณต้องทำเพื่อให้เข้าสู่ระบบหัวคือ: log("Headers: {}", request.headers)ในLoggingRequestInterceptor:traceRequestและในlog("Headers: {}", response.headers) LoggingRequestInterceptor:logResponseคุณอาจต้องการคิดถึงการเพิ่มค่าสถานะบางอย่างสำหรับการบันทึกส่วนหัวและเนื้อหา นอกจากนี้ - คุณอาจต้องการตรวจสอบประเภทเนื้อหาของร่างกายเพื่อบันทึก (ตัวอย่างเช่นแอปพลิเคชันบันทึกเท่านั้น / json *) สิ่งนี้ควรกำหนดค่าได้เช่นกัน สรุปด้วยการปรับแต่งเล็กน้อยคุณจะมีห้องสมุดที่ดีสำหรับการแพร่กระจาย ผลงานดี :)
WrRaThY

0

ต้องการที่จะเพิ่มการใช้งานของฉันนี้เช่นกัน ฉันขอโทษสำหรับเซมิโคลอนที่หายไปทั้งหมดนี่เขียนไว้ใน Groovy

ฉันต้องการบางสิ่งที่สามารถกำหนดค่าได้มากกว่าคำตอบที่ได้รับ นี่คือเทมเพลตถั่วที่เหลือที่ว่องไวมากและจะบันทึกทุกอย่างที่ OP มองหา

คลาส Interceptor การบันทึกที่กำหนดเอง:

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

เทมเพลตถั่วนิยามที่เหลือ:

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

0

อ้างถึง Q / A สำหรับบันทึกการร้องขอและการตอบสนองสำหรับเทมเพลตที่เหลือโดยเปิดใช้งานการอ่านหลายรายการบน HttpInputStream

ทำไม ClientHttpRequestInterceptor ที่กำหนดเองของฉันพร้อมการตอบกลับที่ว่างเปล่า


0

org.apache.http.wireให้บันทึกที่อ่านไม่ได้ดังนั้นฉันจึงใช้สมุดบันทึกเพื่อบันทึกแอปพลิเคชัน Servlet และ RestTemplate ขอ / 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;
    }

}
}

-1

ที่เกี่ยวข้องกับการตอบสนองโดยใช้ ClientHttpInterceptor ฉันพบวิธีที่จะทำให้การตอบสนองทั้งหมดโดยไม่ต้องบัฟเฟอร์โรงงาน เพียงเก็บสตรีมอินพุตการตอบสนองไว้ในอาร์เรย์ไบต์โดยใช้วิธี utils ที่จะคัดลอกอาร์เรย์นั้นจากเนื้อหา แต่สำคัญล้อมรอบวิธีนี้ด้วยลอง catch เนื่องจากจะหยุดถ้าการตอบสนองว่างเปล่า (นั่นคือสาเหตุของการยกเว้นการเข้าถึงทรัพยากร) และ เพียงแค่สร้างอาร์เรย์ไบต์ที่ว่างเปล่าและไม่ใช่แค่สร้างคลาสภายในแบบไม่ระบุชื่อของ ClientHttpResponse โดยใช้อาร์เรย์นั้นและพารามิเตอร์อื่น ๆ จากการตอบกลับดั้งเดิม กว่าที่คุณจะสามารถส่งคืนออบเจกต์ ClientHttpResponse ใหม่ไปยังเชนการดำเนินการเทมเพลตส่วนที่เหลือและคุณสามารถบันทึกการตอบสนองโดยใช้อาร์เรย์ไบต์เนื้อหาที่เก็บไว้ก่อนหน้านี้ ด้วยวิธีนี้คุณจะหลีกเลี่ยงการใช้ InputStream ในการตอบสนองที่แท้จริงและคุณสามารถใช้การตอบสนองส่วนที่เหลือเทมเพลตได้ บันทึก,


-2

คนตัดไม้ของฉันใช้ config xml

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

จากนั้นคุณจะได้รับสิ่งต่าง ๆ ดังนี้:

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 คุณต้องทำการดีบักต่อไปและในกรณีของฉันฉันได้รับสิ่งนี้:

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

และนี่:

outputMessage.getBody().flush();

outputMessage.getBody () มีข้อความ http (ประเภทโพสต์) ส่ง


การบันทึกการติดตามอาจเป็น verbose มากเกินไป ... จะเกิดอะไรขึ้นถ้ามีคำขอนับพันต่อวินาที
Gervasio Amy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.