【Spring MVC】RestTemplateを使用したAPI疎通時に基本的なログ出力を行う方法

概要

RestTemplateを使用してHTTPリクエストを送信する際に、ログ出力を行う方法についてまとめた。
動作確認を行うための事前準備については、以下に記載している。

あわせて読みたい

概要 RESTfulなAPIを呼ぶクライアントライブラリである、RestTemplateの概要についてまとめた。 RestTemplateを使用するための準備と、どんなメソッドがあるのかを紹介している。   前提 今[…]

【Spring MVC】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);
    }
}

 

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

RestTemplateのリクエストについて、ログを出力している。
URI、HTTPメソッド、ヘッダー、リクエストボディを出力する。

 

logger.info(“———-★★レスポンス★★———-\n” +
“★Response Status Code: {}\n” +
“★Response Headers: {}\n” +
“★Response Body: {}”,
res.getStatusCode(),
res.getHeaders(),
responseBody);

RestTemplateで疎通後のレスポンスについて、ログを出力している。
HTTPステータスコード、ヘッダー、レスポンスボディを出力する。

 

byte[] bodyBytes = res.getBody().readAllBytes();

本来は「ClientHttpResponse.getBody()」を一度使用すると、二度目は読み込めなくなる
そのため、ログ出力に上記を使用すると、実際のアプリのレスポンスを取得できなくなる。

そこで、レスポンスの読み取りを複数回使用するためにBufferingClientHttpRequestFactoryをBean定義に含める

 

ログ出力の流れ

実際にログ出力を行う場合、以下のような流れとなる。

①アプリ側でRestTemplateのメソッドを呼ぶ
②インターセプターが呼ばれる
③リクエスト内容をログ出力する
④実際にリクエスト送信を行う
⑤レスポンスを取得する
⑥レスポンス内容をログ出力する
⑦呼び出し元に戻り、アプリ側で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>

 

<bean id=”bufferingRequestFactory” class=”org.springframework.http.client.BufferingClientHttpRequestFactory”>

上記をRestTemplateのコンストラクタに設定することで、通常は1回しか読み取ることができないレスポンスボディが、ログ用とアプリ用で複数回読み取れるようになる。

 

<bean id=”restClientInterceptor” class=”com.example.client_prototype.biz.RestClientInterceptor” />

作成したインターセプターを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を使用する必要がある

 

スポンサーリンク