Spring Rest Template - 요청/응답의 완전한 디버깅/로그를 활성화하려면 어떻게 해야 합니까?
Spring Rest Template를 사용한 지 얼마 되지 않아 요청과 응답을 디버깅하려고 할 때 항상 벽에 부딪힙니다.기본적으로 "상세" 옵션이 켜져 있는 상태에서 컬을 사용할 때 볼 수 있는 것과 동일한 기능을 찾고 있습니다.예를 들어 다음과 같습니다.
curl -v http://twitter.com/statuses/public_timeline.rss
송신된 데이터와 수신된 데이터(헤더, 쿠키 등)를 모두 표시합니다.
Spring Rest Template에서 응답을 기록하는 방법 등의 관련 게시물을 확인했습니다.아직 이 문제를 해결하지 못했어요.
이를 위한 한 가지 방법은 실제로 RestTemplate 소스 코드를 변경하고 여기에 로깅 문을 추가하는 것입니다. 하지만 이 방법은 정말 마지막 수단입니다.Spring Web Client/RestTemplate에 모든 것을 보다 알기 쉽게 기록하도록 지시할 수 있는 방법이 있습니다.
제 목표는 다음과 같은 코드로 이것을 할 수 있는 것입니다.
restTemplate.put("http://someurl", objectToPut, urlPathValues);
로그 파일 또는 콘솔에서 동일한 유형의 디버깅 정보를 가져옵니다.이것은 Spring Rest Template를 사용하고 문제가 있는 사람에게 매우 도움이 될 것이라고 생각합니다.컬을 사용하여 RestTemplate 문제를 디버깅하면 (경우에 따라) 작동하지 않습니다.
, 「」를 .ClientHttpRequestInterceptor
및 " " to to to to to to 。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor {
final static Logger log = LoggerFactory.getLogger(LoggingRequestInterceptor.class);
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
traceRequest(request, body);
ClientHttpResponse response = execution.execute(request, body);
traceResponse(response);
return response;
}
private void traceRequest(HttpRequest request, byte[] body) throws IOException {
log.info("===========================request begin================================================");
log.debug("URI : {}", request.getURI());
log.debug("Method : {}", request.getMethod());
log.debug("Headers : {}", request.getHeaders() );
log.debug("Request body: {}", new String(body, "UTF-8"));
log.info("==========================request end================================================");
}
private void traceResponse(ClientHttpResponse response) throws IOException {
StringBuilder inputStringBuilder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), "UTF-8"));
String line = bufferedReader.readLine();
while (line != null) {
inputStringBuilder.append(line);
inputStringBuilder.append('\n');
line = bufferedReader.readLine();
}
log.info("============================response begin==========================================");
log.debug("Status code : {}", response.getStatusCode());
log.debug("Status text : {}", response.getStatusText());
log.debug("Headers : {}", response.getHeaders());
log.debug("Response body: {}", inputStringBuilder.toString());
log.info("=======================response end=================================================");
}
}
다음 ""를 합니다.RestTemplate
, 「」의BufferingClientHttpRequestFactory
및LoggingRequestInterceptor
:
RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(new LoggingRequestInterceptor());
restTemplate.setInterceptors(interceptors);
BufferingClientHttpRequestFactory
는, 인터셉터와 최초의 발신 코드 양쪽에서 응답 본문을 사용하는 경우에 필요합니다.디폴트 실장에서는 응답 본문을 1회만 읽을 수 있습니다.
Spring Boot 에서는, 이것을 속성(또는 그 외의 12 팩터 방식)으로 설정하면, 완전한 요구/응답이 가능하게 됩니다.
logging.level.org.apache.http=DEBUG
이 출력물
-DEBUG .i.c.DefaultHttpClientConnectionOperator : Connecting to localhost/127.0.0.1:41827
-DEBUG .i.c.DefaultHttpClientConnectionOperator : Connection established 127.0.0.1:39546<->127.0.0.1:41827
-DEBUG o.a.http.impl.execchain.MainClientExec : Executing request POST /v0/users HTTP/1.1
-DEBUG o.a.http.impl.execchain.MainClientExec : Target auth state: UNCHALLENGED
-DEBUG o.a.http.impl.execchain.MainClientExec : Proxy auth state: UNCHALLENGED
-DEBUG org.apache.http.headers : http-outgoing-0 >> POST /v0/users HTTP/1.1
-DEBUG org.apache.http.headers : http-outgoing-0 >> Content-Type: application/json;charset=UTF-8
-DEBUG org.apache.http.headers : http-outgoing-0 >> Content-Length: 56
-DEBUG org.apache.http.headers : http-outgoing-0 >> Host: localhost:41827
-DEBUG org.apache.http.headers : http-outgoing-0 >> Connection: Keep-Alive
-DEBUG org.apache.http.headers : http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.2 (Java/1.8.0_102)
-DEBUG org.apache.http.headers : http-outgoing-0 >> Accept-Encoding: gzip,deflate
-DEBUG org.apache.http.wire : http-outgoing-0 >> "POST /v0/users HTTP/1.1[\r][\n]"
-DEBUG org.apache.http.wire : http-outgoing-0 >> "Content-Type: application/json;charset=UTF-8[\r][\n]"
-DEBUG org.apache.http.wire : http-outgoing-0 >> "Content-Length: 56[\r][\n]"
-DEBUG org.apache.http.wire : http-outgoing-0 >> "Host: localhost:41827[\r][\n]"
-DEBUG org.apache.http.wire : http-outgoing-0 >> "Connection: Keep-Alive[\r][\n]"
-DEBUG org.apache.http.wire : http-outgoing-0 >> "User-Agent: Apache-HttpClient/4.5.2 (Java/1.8.0_102)[\r][\n]"
-DEBUG org.apache.http.wire : http-outgoing-0 >> "Accept-Encoding: gzip,deflate[\r][\n]"
-DEBUG org.apache.http.wire : http-outgoing-0 >> "[\r][\n]"
-DEBUG org.apache.http.wire : http-outgoing-0 >> "{"id":null,"email":"xenoterracide@gmail.com","new":true}"
및 응답
-DEBUG .i.c.DefaultHttpClientConnectionOperator : Connecting to localhost/127.0.0.1:41827
-DEBUG .i.c.DefaultHttpClientConnectionOperator : Connection established 127.0.0.1:39546<->127.0.0.1:41827
-DEBUG o.a.http.impl.execchain.MainClientExec : Executing request POST /v0/users HTTP/1.1
-DEBUG o.a.http.impl.execchain.MainClientExec : Target auth state: UNCHALLENGED
-DEBUG o.a.http.impl.execchain.MainClientExec : Proxy auth state: UNCHALLENGED
-DEBUG org.apache.http.headers : http-outgoing-0 >> POST /v0/users HTTP/1.1
-DEBUG org.apache.http.headers : http-outgoing-0 >> Content-Type: application/json;charset=UTF-8
-DEBUG org.apache.http.headers : http-outgoing-0 >> Content-Length: 56
-DEBUG org.apache.http.headers : http-outgoing-0 >> Host: localhost:41827
-DEBUG org.apache.http.headers : http-outgoing-0 >> Connection: Keep-Alive
-DEBUG org.apache.http.headers : http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.2 (Java/1.8.0_102)
-DEBUG org.apache.http.headers : http-outgoing-0 >> Accept-Encoding: gzip,deflate
-DEBUG org.apache.http.wire : http-outgoing-0 >> "POST /v0/users HTTP/1.1[\r][\n]"
-DEBUG org.apache.http.wire : http-outgoing-0 >> "Content-Type: application/json;charset=UTF-8[\r][\n]"
-DEBUG org.apache.http.wire : http-outgoing-0 >> "Content-Length: 56[\r][\n]"
-DEBUG org.apache.http.wire : http-outgoing-0 >> "Host: localhost:41827[\r][\n]"
-DEBUG org.apache.http.wire : http-outgoing-0 >> "Connection: Keep-Alive[\r][\n]"
-DEBUG org.apache.http.wire : http-outgoing-0 >> "User-Agent: Apache-HttpClient/4.5.2 (Java/1.8.0_102)[\r][\n]"
-DEBUG org.apache.http.wire : http-outgoing-0 >> "Accept-Encoding: gzip,deflate[\r][\n]"
-DEBUG org.apache.http.wire : http-outgoing-0 >> "[\r][\n]"
-DEBUG org.apache.http.wire : http-outgoing-0 >> "{"id":null,"email":"xenoterracide@gmail.com","new":true}"
그냥 '그냥'일 수도 있어요.logging.level.org.apache.http.wire=DEBUG
되어 있는 것 .
일부 코드를 사용하여 @hstoerr 응답을 확장합니다.
Logging Request 작성요청 응답을 기록할 인터셉터
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
}
}
Rest Template 설정
RestTemplate rt = new RestTemplate();
//set interceptors/requestFactory
ClientHttpRequestInterceptor ri = new LoggingRequestInterceptor();
List<ClientHttpRequestInterceptor> ris = new ArrayList<ClientHttpRequestInterceptor>();
ris.add(ri);
rt.setInterceptors(ris);
rt.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory());
입니다.logging.level.org.springframework.web.client.RestTemplate=DEBUG
application.properties
filename을 클릭합니다.
등 log4j.logger.httpclient.wire
.왜냐하면 그들은 당신이log4j
Apache » ApacheHttpClient
항상 맞는 것은 아닙니다.
단, 이 구문은 Spring Boot의 최신 버전에서만 작동합니다.
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()
이 "Debug Logging" 이 되어 있는지 합니다.application.properties
:
logging.level.org.hobsoft.spring.resttemplatelogger.LoggingCustomizer = DEBUG
이 RestTemplate HTTP 에 됩니다.org.hobsoft.spring.resttemplatelogger.LoggingCustomizer
버버레벨벨하하하하
면책사항: 이 도서관은 내가 쓴 것이다.
Xenoteracide가 제공하는 솔루션
logging.level.org.apache.http=DEBUG
문제는 Apache Http Components가 기본적으로 사용되지 않는다는 것입니다.
Apache Http Components를 사용하려면 pom.xml에 추가합니다.
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpasyncclient</artifactId>
</dependency>
및 으로 설정합니다.
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new HttpComponentsAsyncClientHttpRequestFactory());
이들 답변 중 어느 것도 문제의 100%를 해결할 수 없습니다.mjjj1409는 대부분을 취득하지만, 응답 기록 문제를 쉽게 회피할 수 있어 작업이 좀 더 필요합니다.Paul Sabou는 현실적인 솔루션을 제공하지만 실제로 구현하기에 충분한 세부 정보를 제공하지 않았습니다(그리고 전혀 효과가 없었습니다).Sofiene가 로깅을 받았으나 입력 스트림이 이미 사용되었기 때문에 응답을 읽을 수 없습니다.
Buffering ClientHttpResponseWrapper를 사용하여 응답 객체를 랩하여 응답 본문을 여러 번 읽을 수 있도록 하는 것이 좋습니다.
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은 소비되지 않습니다.클래스 패스에 Buffering ClientHttpResponseWrapper가 없는 경우 다음 사이트에서 간단한 구현을 찾을 수 있습니다.
RestTemplate 설정 시:
LoggingRequestInterceptor loggingInterceptor = new LoggingRequestInterceptor();
restTemplate.getInterceptors().add(loggingInterceptor);
나는 마침내 이것을 올바른 방법으로 하는 방법을 찾았다.대부분의 솔루션은 "로그를 취득할 수 있도록 Spring 및 SLF4J를 구성하는 방법"을 참조하십시오.
다음 두 가지 작업을 수행해야 할 것 같습니다.
- 에 log4j.properties 행을 합니다.
log4j.logger.httpclient.wire=DEBUG
- 스프링이 로깅 구성을 무시하지 않도록 하십시오.
두 번째 문제는 주로 slf4j가 사용되는 스프링 환경에서 발생합니다(저와 마찬가지로).이와 같이 slf4j를 사용하는 경우 다음 두 가지가 발생하는지 확인합니다.
classpath에는 commons-logging 라이브러리가 없습니다.이것은 제외 기술자를 pom에 추가하는 것으로 실행할 수 있습니다.
<exclusions><exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions>
log4j.properties 파일은 spring이 찾을 수 있는 클래스 경로 어딘가에 저장됩니다.이것에 문제가 있는 경우는, log4j.properties 파일을 디폴트 패키지에 격납하는 것이 마지막 방법입니다(좋은 프랙티스는 아니지만, 기대대로 동작하는 것을 확인합니다).
RestTemplate 로깅
옵션 1디버깅 로깅을 엽니다.
RestTemplate 구성
기본적으로는 RestTemplate는 표준 JDK 기능을 사용하여 HTTP 연결을 확립합니다.Apache Http Components와 같은 다른 HTTP 라이브러리를 사용하도록 전환할 수 있습니다.
@Bean 공용 RestTemplate restTemplate(RestTemplateBuilder) {RestTemplate restTemplate = builder.build(), restTemplate 반환, }
로깅 구성
application.yml
logging: level: org.spring frameworkweb.client.RestTemplate: DEBUG
옵션 2인터셉터 사용
래퍼 응답
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StreamUtils;
public final class BufferingClientHttpResponseWrapper implements ClientHttpResponse {
private final ClientHttpResponse response;
private byte[] body;
BufferingClientHttpResponseWrapper(ClientHttpResponse response) {
this.response = response;
}
public HttpStatus getStatusCode() throws IOException {
return this.response.getStatusCode();
}
public int getRawStatusCode() throws IOException {
return this.response.getRawStatusCode();
}
public String getStatusText() throws IOException {
return this.response.getStatusText();
}
public HttpHeaders getHeaders() {
return this.response.getHeaders();
}
public InputStream getBody() throws IOException {
if (this.body == null) {
this.body = StreamUtils.copyToByteArray(this.response.getBody());
}
return new ByteArrayInputStream(this.body);
}
public void close() {
this.response.close();
}
}
인터셉터 구현
package com.example.logging;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
public class LoggingRestTemplate implements ClientHttpRequestInterceptor {
private final static Logger LOGGER = LoggerFactory.getLogger(LoggingRestTemplate.class);
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
traceRequest(request, body);
ClientHttpResponse response = execution.execute(request, body);
return traceResponse(response);
}
private void traceRequest(HttpRequest request, byte[] body) throws IOException {
if (!LOGGER.isDebugEnabled()) {
return;
}
LOGGER.debug(
"==========================request begin==============================================");
LOGGER.debug("URI : {}", request.getURI());
LOGGER.debug("Method : {}", request.getMethod());
LOGGER.debug("Headers : {}", request.getHeaders());
LOGGER.debug("Request body: {}", new String(body, "UTF-8"));
LOGGER.debug(
"==========================request end================================================");
}
private ClientHttpResponse traceResponse(ClientHttpResponse response) throws IOException {
if (!LOGGER.isDebugEnabled()) {
return response;
}
final ClientHttpResponse responseWrapper = new BufferingClientHttpResponseWrapper(response);
StringBuilder inputStringBuilder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(responseWrapper.getBody(), "UTF-8"));
String line = bufferedReader.readLine();
while (line != null) {
inputStringBuilder.append(line);
inputStringBuilder.append('\n');
line = bufferedReader.readLine();
}
LOGGER.debug(
"==========================response begin=============================================");
LOGGER.debug("Status code : {}", responseWrapper.getStatusCode());
LOGGER.debug("Status text : {}", responseWrapper.getStatusText());
LOGGER.debug("Headers : {}", responseWrapper.getHeaders());
LOGGER.debug("Response body: {}", inputStringBuilder.toString());
LOGGER.debug(
"==========================response end===============================================");
return responseWrapper;
}
}
RestTemplate 구성
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = builder.build();
restTemplate.setInterceptors(Collections.singletonList(new LoggingRestTemplate()));
return restTemplate;
}
로깅 구성
를 들어 LoggingRestTemplate는 다음과 같습니다.
application.yml
:로깅: 수준: com.example.logg: DEBUG
옵션 3http component 사용
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
:logging: level: org.syslog.http: DEBUG
----- 2019년 7월 -----
(스프링 부트 사용)
Spring Boot은 제로 구성 마법이지만 RestTemplate를 사용하여 간단한 JSON 응답 본문을 검사하거나 기록할 수 있는 쉬운 방법이 없다는 사실에 놀랐습니다.여기에 기재되어 있는 다양한 답변과 코멘트를 검토하고, 현재의 옵션을 생각하면, (아직도) 기능하는 것과 합리적인 솔루션이라고 생각되는 것에 대한 저만의 증류판을 공유하고 있습니다(Gradle 4.4에서 Spring Boot 2.1.6을 사용하고 있습니다).
1. Fiddler를 http 프록시로 사용
이것은, 독자적인 대행 수신기를 작성하거나 기반이 되는 http 클라이언트를 apache로 변경하는 번거로운 작업을 모두 생략하기 때문에, 실제로는 매우 우아한 솔루션입니다(아래 참조).
Fiddler 설치 및 실행
그리고 나서.
-DproxySet=true -Dhttp.proxyHost=localhost -Dhttp.proxyPort=8888
VM 션션 vm
2. Apache Http Client 사용
Maven 또는 Gradle 종속성에 Apache HttpClient를 추가합니다.
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.9</version>
</dependency>
HttpComponentsClientHttpRequestFactory
RestTemplate request RequestFactory rest rest 。가장 간단한 방법은 다음과 같습니다.
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
「DEBUG」로 를 유효하게 .application.properties
Boot을 )
logging.level.org.apache.http=DEBUG
Boot을 되어 있는지 를 들어 Spring Boot를 하는 spring-boot-starter 해야 합니다.봄 부츠spring-boot-starter-logging
.
3. 요격기의 사용
다른 답변이나 코멘트에 있는 제안서, 반대 제안서, 고치를 읽고 그 길을 갈지 스스로 결정하도록 하겠습니다.
4. 본문 없이 URL 및 응답 상태 로그
이것은 본문 로깅에 관한 규정된 요건을 충족하지 못하지만 REST 콜 로깅을 시작하는 빠르고 간단한 방법입니다.완전한 URL 및 응답 상태가 표시됩니다.
줄만 .application.properties
Boot을 Spring Boot (스프링 부트)를 )spring-boot-starter-logging
)
logging.level.org. springframework.web.client.RestTemplate=디버깅
출력은 다음과 같습니다.
2019-07-29 11:53:50.265 DEBUG o.s.web.client.RestTemplate : HTTP GET http://www.myrestservice.com/Endpoint?myQueryParam=myValue
2019-07-29 11:53:50.276 DEBUG o.s.web.client.RestTemplate : Accept=[application/json]
2019-07-29 11:53:50.584 DEBUG o.s.web.client.RestTemplate : Response 200 OK
2019-07-29 11:53:50.585 DEBUG o.s.web.client.RestTemplate : Reading to [org.mynamespace.MyJsonModelClass]
다른 응답과 같이 응답체는 반복적으로 읽을 수 있도록 특별한 처리가 필요하다(디폴트로는 첫 번째 읽기에서 내용이 소비된다).
「 」를하는 대신에, 「 」를 사용합니다.BufferingClientHttpRequestFactory
요구를 셋업 할 때, 대행 수신자 자신이 응답을 랩 해, 컨텐츠가 보관 유지되고 있는 것을 확인해, (로거와 응답의 소비자에 의해서) 반복해 읽어낼 수 있습니다.
내 요격기 말이야
- 래퍼를 사용하여 응답 본문을 버퍼링합니다.
- 보다 콤팩트한 방법으로 로그 기록
- 상태 코드 ID도 기록합니다(예: 201 Created).
- 에는 여러 스레드와 동시 로그 엔트리를 쉽게 구분할 수 있는 요구 시퀀스 번호가 포함되어 있습니다.
코드:
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", ... }
다른 응답에서 설명한HttpClient 로깅 외에 ClientHttpRequest를 도입할 수도 있습니다.요청 본문과 응답을 읽고 기록하는 인터셉터.다른 파일에서도 HttpClient를 사용하거나 사용자 정의 로깅 형식을 원하는 경우 이 작업을 수행할 수 있습니다.주의: 응답을 두 번 읽을 수 있도록 RestTemplate에 BufferingClientHttpRequestFactory를 지정합니다.
application.properties
logging.level.org.springframework.web.client=DEBUG
application.yml
logging:
level:
root: WARN
org.springframework.web.client: DEBUG
이것은 올바른 방법이 아닐 수도 있지만, 저는 이것이 너무 많은 로그를 작성하지 않고 인쇄 요청과 응답을 할 수 있는 가장 간단한 방법이라고 생각합니다.
아래에 application.properties를 2행 추가하면 모든 요청과 응답이 첫 번째 줄에 기록되고 두 번째 줄에 응답이 기록됩니다.
logging.level.org.springframework.web.client.RestTemplate=DEBUG
logging.level.org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor=DEBUG
정 RestTemplate
는 HttpClient 4.x 를 사용하도록 설정되어 있습니다.HttpClient 의 로그 메뉴얼을 참조해 주세요.로거는 다른 응답에 지정된 것과 다릅니다.
HttpClient 3.x 로깅 설정은 여기서 확인할 수 있습니다.
이 응답의 상당수는 코드 변경과 맞춤형 클래스가 필요하기 때문에 실제로는 필요하지 않습니다.fiddler 등의 디버깅프록시를 사용하여 명령줄(-Dhttp.proxyHost 및 -Dhttp.proxyPort)에서 프록시를 사용하도록 Java 환경을 설정한 후 fiddler를 실행하면 요청 및 응답 전체를 볼 수 있습니다.또, 서버의 변경을 실시하기 전에, 실험용으로 송신하기 전후에, 결과와 응답을 조작할 수 있는 등, 많은 부수적인 메리트가 있습니다.
마지막으로 HTTPS를 사용해야 하는 경우 SSL 증명서를 피들러에서 내보내고 Java keystore(캐서트) 힌트로 Import해야 합니다.기본 Java keystore 비밀번호는 보통 "변경"입니다.
Apache HttpClient의 도움말을 사용하여 Logback에 기록하는 경우:
클래스 경로에 Apache HttpClient가 필요합니다.
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.10</version>
</dependency>
★★★★★★★★★★★★★★★★★의 설정RestTemplate
HttpClient:
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
요청 및 응답을 기록하려면 로그백컨피규레이션파일에 추가합니다.
<logger name="org.apache.http.wire" level="DEBUG"/>
또는 더 많은 로그를 기록하려면:
<logger name="org.apache.http" level="DEBUG"/>
위의 설명에 덧붙여, 이것은 해피 시나리오에 지나지 않습니다.에러가 발생했을 경우는, 응답을 로그에 기록할 수 없습니다.
이 케이스와 위의 모든 케이스에서는 DefaultResponseErrorHandler를 덮어쓰고 다음과 같이 설정해야 합니다.
restTemplate.setErrorHandler(new DefaultResponseErrorHandlerImpl());
이상하게도 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);
}
}
「 」의 설정 ,RestTemplate
a BufferingClientHttpRequestFactory
하면 안 요, 안 돼요, 안 요, 안 돼요, 안 돼요.ClientHttpRequestInterceptor
인터셉터를 경유하여 로그를 작성하려고 할 때 사용합니다.은 ' 하다'의 방식 때문입니다.InterceptingHttpAccessor
인가)RestTemplate
서브클래스)가 동작합니다.
★★★★★★★★★★… 이 을 사용하세요.RestTemplate
에 따라 (SLF4J "API" "API" "SLF4J" "API" "SLF4J" "SLF" "SLF4J" "SLF" "S" "SLF4J" "S" "API")
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();
}
}
나는 단지 이것을 하기 위해 이 많은 작업이 필요하다는 것이 어리석다는 것에 동의한다.
@MilacH가 지적한 바와 같이 구현에 오류가 있습니다.statusCode > 400이 반환되면 errorHandler가 호출되지 않기 때문에 IOException이 대행 수신기에서 느려집니다.예외는 무시해도 되고 핸들러 방식에서 다시 검출됩니다.
package net.sprd.fulfillment.common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import static java.nio.charset.StandardCharsets.UTF_8;
public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor {
final static Logger log = LoggerFactory.getLogger(LoggingRequestInterceptor.class);
@SuppressWarnings("HardcodedLineSeparator")
public static final char LINE_BREAK = '\n';
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
try {
traceRequest(request, body);
} catch (Exception e) {
log.warn("Exception in LoggingRequestInterceptor while tracing request", e);
}
ClientHttpResponse response = execution.execute(request, body);
try {
traceResponse(response);
} catch (IOException e) {
// ignore the exception here, as it will be handled by the error handler of the restTemplate
log.warn("Exception in LoggingRequestInterceptor", e);
}
return response;
}
private void traceRequest(HttpRequest request, byte[] body) {
log.info("===========================request begin================================================");
log.info("URI : {}", request.getURI());
log.info("Method : {}", request.getMethod());
log.info("Headers : {}", request.getHeaders());
log.info("Request body: {}", new String(body, UTF_8));
log.info("==========================request end================================================");
}
private void traceResponse(ClientHttpResponse response) throws IOException {
StringBuilder inputStringBuilder = new StringBuilder();
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), UTF_8))) {
String line = bufferedReader.readLine();
while (line != null) {
inputStringBuilder.append(line);
inputStringBuilder.append(LINE_BREAK);
line = bufferedReader.readLine();
}
}
log.info("============================response begin==========================================");
log.info("Status code : {}", response.getStatusCode());
log.info("Status text : {}", response.getStatusText());
log.info("Headers : {}", response.getHeaders());
log.info("Response body: {}", inputStringBuilder);
log.info("=======================response end=================================================");
}
}
org.apache.http.wire
로그북을 사용하여 어플리케이션 Servlet 및 RestTemplate 요청 및 응답을 페이로드와 함께 기록합니다.
build.gradle
:
compile group: 'org.zalando', name: 'logbook-spring-boot-starter', version: '2.6.2'
또는 Maven 의존관계:
<dependency>
<groupId>org.zalando</groupId>
<artifactId>logbook-spring-boot-starter</artifactId>
<version>2.6.2</version>
</dependency>
application.properties
YAML (또는 트로프 YAML):
logging.level.org.zalando.logbook = TRACE
RestTemplate.java
:
import java.util.function.Supplier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import org.zalando.logbook.httpclient.LogbookHttpRequestInterceptor;
import org.zalando.logbook.httpclient.LogbookHttpResponseInterceptor;
@Configuration
public class RestTemplateConfiguration {
private final LogbookHttpRequestInterceptor logbookHttpRequestInterceptor;
private final LogbookHttpResponseInterceptor logbookHttpResponseInterceptor;
public RestTemplateConfiguration(LogbookHttpRequestInterceptor logbookHttpRequestInterceptor,
LogbookHttpResponseInterceptor logbookHttpResponseInterceptor) {
this.logbookHttpRequestInterceptor = logbookHttpRequestInterceptor;
this.logbookHttpResponseInterceptor = logbookHttpResponseInterceptor;
}
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return 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();
return new HttpComponentsClientHttpRequestFactory(client);
}
}
}
현시점에서는 의존관계를 추가하는 것이 최선입니다.
<dependency>
<groupId>com.github.zg2pro</groupId>
<artifactId>spring-rest-basis</artifactId>
<version>v.x</version>
</dependency>
LoggingRequest가 포함되어 있습니다.인터셉터 클래스는 RestTemplate에 추가할 수 있습니다.
다음 방법으로 이 유틸리티를 스프링 RestTemplate에 대행 수신기로 추가하여 통합합니다.
restTemplate.setRequestFactory(LoggingRequestFactoryFactory.build());
log4j와 같은 slf4j 구현을 프레임워크에 추가합니다.
또는 "Zg2proRestTemplate"를 직접 사용합니다.spring RestTemplate를 사용할 때 httpclient와 모든 apache.http libs가 반드시 로드되는 것은 아니기 때문에 @PaulSabou의 "best answer"는 그렇게 보입니다.
이 구현도 추가하고 싶었습니다.사라진 세미콜론에 대해 사과드립니다. 그루비어로 쓰여 있습니다.
제공된 답변보다 더 구성 가능한 답변이 필요했습니다.이 제품은 민첩성이 뛰어나고 OP가 원하는 모든 것을 기록할 수 있는 레스트 템플릿입니다.
사용자 지정 로깅 가로채기 클래스:
import org.springframework.http.HttpRequest
import org.springframework.http.client.ClientHttpRequestExecution
import org.springframework.http.client.ClientHttpRequestInterceptor
import org.springframework.http.client.ClientHttpResponse
import org.springframework.util.StreamUtils
import java.nio.charset.Charset
class HttpLoggingInterceptor implements ClientHttpRequestInterceptor {
private final static Logger log = LoggerFactory.getLogger(HttpLoggingInterceptor.class)
@Override
ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
logRequest(request, body)
ClientHttpResponse response = execution.execute(request, body)
logResponse(response)
return response
}
private void logRequest(HttpRequest request, byte[] body) throws IOException {
if (log.isDebugEnabled()) {
log.debug("===========================request begin================================================")
log.debug("URI : {}", request.getURI())
log.debug("Method : {}", request.getMethod())
log.debug("Headers : {}", request.getHeaders())
log.debug("Request body: {}", new String(body, "UTF-8"))
log.debug("==========================request end================================================")
}
}
private void logResponse(ClientHttpResponse response) throws IOException {
if (log.isDebugEnabled()) {
log.debug("============================response begin==========================================")
log.debug("Status code : {}", response.getStatusCode())
log.debug("Status text : {}", response.getStatusText())
log.debug("Headers : {}", response.getHeaders())
log.debug("Response body: {}", StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()))
log.debug("=======================response end=================================================")
}
}
}
Rest 템플릿 Bean 정의:
@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
}
}
HttpInputStream에서 다중 읽기를 활성화하여 나머지 템플릿에 대한 요청 및 응답 로깅에 대한 Q/A를 참조하십시오.
커스텀 ClientHttpRequest가 필요한 이유응답이 빈 인터셉터
간단한 문제 해결 방법:
- RestTemplateBuilder를 사용하여 Bean of RestTemplate를 만듭니다.접속시간과 판독시간을 보다 상세하게 제어할 수 있습니다.
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.setConnectTimeout(Duration.ofMillis(60000))
.setReadTimeout(Duration.ofMillis(60000))
.build();
}
}
- 파일에 다음 행을 추가합니다.
logging.level.org.springframework.web.client.RestTemplate=DEBUG
제!가!!!!
ClientHttp를 사용한 응답 관련요격기, 버퍼링 공장 없이 모든 반응을 유지할 수 있는 방법을 찾았어요응답 본문 입력 스트림을 바이트 배열에서 복사하는 utils 메서드를 사용하여 바이트 배열 내에 저장하기만 하면 됩니다.그러나 중요한 것은 응답이 비어 있으면(리소스 액세스 예외의 원인) 중단되기 때문에 이 메서드를 try catch로 둘러싸서 빈 바이트 배열을 만들고 익명의 내부 클래스를 만드는 것이 아닙니다.클라이언트HttpResponse의 경우 해당 배열 및 원래 응답의 기타 파라미터를 사용합니다.이 경우 새로운 ClientHttpResponse 개체를 나머지 템플릿 실행 체인으로 되돌리고 이전에 저장된 본문 바이트 배열을 사용하여 응답을 기록할 수 있습니다.이렇게 하면 실제 응답에서 InputStream을 사용하지 않고 그대로 Rest Template 응답을 사용할 수 있습니다.주의: 응답이 너무 크면 위험할 수 있습니다.
my logger 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(post type)가 송신하는 메시지가 포함되어 있습니다.
언급URL : https://stackoverflow.com/questions/7952154/spring-resttemplate-how-to-enable-full-debugging-logging-of-requests-responses
'source' 카테고리의 다른 글
'errno'가 무슨 뜻인지 어떻게 알죠? (0) | 2022.08.18 |
---|---|
Weak Hash Map이란 무엇이며 언제 사용해야 합니까? (0) | 2022.08.18 |
화면 크기를 확대하기 위해 Vuetify 그리드 열 너비를 줄이는 방법 (0) | 2022.08.18 |
슬롯이 있는 동적 컴포넌트 (0) | 2022.08.18 |
페이지 새로 고침 후 쿠키 저장소가 있는 Vuex 상태가 손실됨 (0) | 2022.08.18 |