다른 응답에서 언급했듯이 응답 본문은 특별한 처리가 필요하므로 반복해서 읽을 수 있습니다 (기본적으로 내용은 첫 번째 읽기에서 소비 됨).
BufferingClientHttpRequestFactory
요청을 설정할 때 를 사용하는 대신 인터셉터 자체가 응답을 랩핑하고 컨텐츠를 보유하고 반복적으로 읽을 수 있습니다 (로거 및 응답 소비자가).
내 요격기
- 랩퍼를 사용하여 응답 본문 을 버퍼링합니다.
- A의 로그를 보다 컴팩트 한 방법
- 상태 코드 식별자 도 기록합니다 (예 : 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", ... }