概要
RestTemplateを使用してHTTPリクエストを送信する際に、ログ出力を行う方法についてまとめた。
動作確認を行うための事前準備については、以下に記載している。
概要 RESTfulなAPIを呼ぶクライアントライブラリである、RestTemplateの概要についてまとめた。 RestTemplateを使用するための準備と、どんなメソッドがあるのかを紹介している。 前提 今[…]
基本的なログ出力を行う方法
RestTemplateを使用してHTTPリクエストを行った際に、リクエストとレスポンスの内容をログ出力させる。
ログ出力を行う資材については、以下に記載。
概要 これから数回にかけてREST APIについて学んだことを載せていく。 今回はREST APIの仕組みとプロジェクト作成方法について紹介する。 尚、RESTとは何かということについては取り扱わない。 仕組み[…]
ClientHttpRequestInterceptor
ClientHttpRequestInterceptorインターフェースを使用して、HTTPリクエスト・レスポンスの内容をログに出すインターセプターを実装する。
このインターフェースは、HTTPリクエストの前後に処理を挟むことができる。
実装
ClientHttpRequestInterceptorを実装してログ出力を行う。
RestClientInterceptor.java
package com.example.client_prototype.biz;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
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 RestClientInterceptor implements ClientHttpRequestInterceptor {
/** ロガー */
private static final Logger logger = LoggerFactory.getLogger(RestClientInterceptor.class);
@Override
public ClientHttpResponse intercept(HttpRequest req, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
// リクエスト内容をログ出力
logRequest(req, body);
// 実際にリクエスト送信
ClientHttpResponse res = execution.execute(req, body);
// レスポンス内容をログ出力
logResponse(res);
return res;
}
private void logRequest(HttpRequest req, byte[] body) {
logger.info("----------★★リクエスト★★----------\n" +
"★Request URI: {}\n" +
"★Request Method: {}\n" +
"★Request Headers: {}\n" +
"★Request Body: {}",
req.getURI(),
req.getMethod(),
req.getHeaders(),
new String(body, StandardCharsets.UTF_8));
}
private void logResponse(ClientHttpResponse res) throws IOException {
byte[] bodyBytes = res.getBody().readAllBytes();
String responseBody = new String(bodyBytes, StandardCharsets.UTF_8);
logger.info("----------★★レスポンス★★----------\n" +
"★Response Status Code: {}\n" +
"★Response Headers: {}\n" +
"★Response Body: {}",
res.getStatusCode(),
res.getHeaders(),
responseBody);
}
}
“★Request URI: {}\n” +
“★Request Method: {}\n” +
“★Request Headers: {}\n” +
“★Request Body: {}”,
req.getURI(),
req.getMethod(),
req.getHeaders(),
new String(body, StandardCharsets.UTF_8));
RestTemplateのリクエストについて、ログを出力している。
URI、HTTPメソッド、ヘッダー、リクエストボディを出力する。
“★Response Status Code: {}\n” +
“★Response Headers: {}\n” +
“★Response Body: {}”,
res.getStatusCode(),
res.getHeaders(),
responseBody);
RestTemplateで疎通後のレスポンスについて、ログを出力している。
HTTPステータスコード、ヘッダー、レスポンスボディを出力する。
本来は「ClientHttpResponse.getBody()」を一度使用すると、二度目は読み込めなくなる。
そのため、ログ出力に上記を使用すると、実際のアプリのレスポンスを取得できなくなる。
そこで、レスポンスの読み取りを複数回使用するためにBufferingClientHttpRequestFactoryをBean定義に含める。
ログ出力の流れ
実際にログ出力を行う場合、以下のような流れとなる。
②インターセプターが呼ばれる
③リクエスト内容をログ出力する
④実際にリクエスト送信を行う
⑤レスポンスを取得する
⑥レスポンス内容をログ出力する
⑦呼び出し元に戻り、アプリ側でRestTemplateメソッドのレスポンスを受け取る
RestTemplateのBean定義
作成したインターセプターをRestTemplateに適用させるためにBean定義を行う。
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- コンポーネントスキャン -->
<context:component-scan base-package="com.example.client_prototype.biz" />
<!-- BufferingClientHttpRequestFactory(レスポンスを複数回読み取れるようにする設定) -->
<bean id="simpleRequestFactory" class="org.springframework.http.client.SimpleClientHttpRequestFactory" />
<bean id="bufferingRequestFactory" class="org.springframework.http.client.BufferingClientHttpRequestFactory">
<constructor-arg ref="simpleRequestFactory"/>
</bean>
<!-- LoggingInterceptor の Bean 定義 -->
<bean id="restClientInterceptor" class="com.example.client_prototype.biz.RestClientInterceptor" />
<!-- RestTemplate の Bean 定義 -->
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<constructor-arg ref="bufferingRequestFactory"/>
<property name="interceptors">
<list>
<ref bean="restClientInterceptor" />
</list>
</property>
</bean>
</beans>
上記をRestTemplateのコンストラクタに設定することで、通常は1回しか読み取ることができないレスポンスボディが、ログ用とアプリ用で複数回読み取れるようになる。
作成したインターセプターをBean定義して、RestTemplateのinterceptorsプロパティに設定する。
動作確認
実際にRestTemplateのメソッドを呼び出した際の挙動を確認する。
動作確認用クラス
// クエリパラメータ
Map<String, Object> queryParams = Map.of("name", "ご");
// API疎通
ResponseEntity<List<Resource>> res = restClient.exchange(
"rest05/",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<Resource>>() {},
queryParams,
null);
// 動作確認
System.out.println("-------動作確認-------");
System.out.println(res.getStatusCode());
System.out.println(res.getHeaders());
System.out.println(res.getBody());
コンソールログ
----------★★リクエスト★★----------
★Request URI: http://localhost:8080/rest_prototype/rest05/?name=%E3%81%94
★Request Method: GET
★Request Headers: [Accept:"application/json, application/*+json", Content-Type:"application/json", Content-Length:"0"]
★Request Body:
----------★★レスポンス★★----------
★Response Status Code: 200 OK
★Response Headers: [Content-Type:"application/json;charset=UTF-8", Transfer-Encoding:"chunked", Date:"Sat, 05 Apr 2025 07:13:42 GMT", Keep-Alive:"timeout=20", Connection:"keep-alive"]
★Response Body: [{"id":"1","name":"りんご","hogeDate":"2025-02-01"},{"id":"2","name":"ごりら","hogeDate":"2024-06-05"},{"id":"4","name":"ごま","hogeDate":"2099-05-01"}]
-------動作確認-------
200 OK
[Content-Type:"application/json;charset=UTF-8", Transfer-Encoding:"chunked", Date:"Sat, 05 Apr 2025 07:13:42 GMT", Keep-Alive:"timeout=20", Connection:"keep-alive"]
[Resource(id=1, name=りんご, hogeDate=2025-02-01), Resource(id=2, name=ごりら, hogeDate=2024-06-05), Resource(id=4, name=ごま, hogeDate=2099-05-01)]
まとめ
☑ ClientHttpRequestInterceptorを実装することで、RestTemplateのHTTPリクエスト前後にログ出力処理を入れることができる
☑ RestTemplateに組み込むだけで、HTTPリクエストの全通信に適用される
☑ レスポンスをログに出力したい場合、BufferingClientHttpRequestFactoryを使用する必要がある