Ccmmutty logo
Commutty IT
5 min read

(Java) SpringのRestTemplateでのログ出力

https://cdn.magicode.io/media/notebox/a52d44f3-e7a3-4fed-9cdb-6555086a7d79.jpeg

初めに

Springでのサーバ内のHTTP通信の際にRestTemplateを使用するケースの際に
フォーマットを統一するためにHTTP通信毎に毎回同じログ出力のコードをコピペして貼っている現場があったので
少しでも同じ悲しみを背負わなくてもいいようにオンラインに実装例を残しておきます。

実装

自作インターセプターRestTemplate DIに設定
@Configuration
public class RestClientConfig {
	/**
	 *  RestTemplate 
	 */
	@Bean
	public RestTemplate restTemplate() {
		return new RestTemplateBuilder()
				// ここでインターセプター追加 (RestTemplateLoggingInterceptorは自作) 
				.additionalInterceptors(new RestTemplateLoggingInterceptor())
				.build();
	}
}
org.springframework.http.client.ClientHttpRequestInterceptorの実装クラス RestTemplateLoggingInterceptor を作成します
この際、ClientHttpRequestExecution#executeの内容が1度のみしか取得ができないため
BufferingClientHttpResponseWrapperのような形式で一度ラップします。

public class RestTemplateLoggingInterceptor implements ClientHttpRequestInterceptor {
	public static final Logger logger = LoggerFactory.getLogger(RestTemplateLoggingInterceptor.class);

	@Override
	public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
			throws IOException {
		/*
		 * リクエスト処理
		 */
		logger.info("RestTemplate Request: URI={}, Headers={}, Body={}",
				request.getURI(), request.getHeaders(), new String(body));

		/*
		 * レスポンス バッファリング
		 */
		ClientHttpResponse response = new BufferingClientHttpResponseWrapper(execution.execute(request, body));
		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();
		}

		/*
		 * レスポンス処理
		 */
		if (HttpStatus.OK.equals(response.getStatusCode())) {
			logger.info("RestTemplate Response: StatusCode={} {}, Headers={}, Body={}",
					response.getStatusCode(),
					response.getStatusText(),
					response.getHeaders(),
					inputStringBuilder.toString());
		} else {
			logger.warn("RestTemplate Response: StatusCode={} {}, Headers={}, Body={}",
					response.getStatusCode(),
					response.getStatusText(),
					response.getHeaders(),
					inputStringBuilder.toString());
		}

		return response;
	}

	private static class BufferingClientHttpResponseWrapper implements ClientHttpResponse {
		private final ClientHttpResponse response;
		@Nullable
		private byte[] body;
		BufferingClientHttpResponseWrapper(ClientHttpResponse response) {
			this.response = response;
		}
		@Override
		public HttpStatus getStatusCode() throws IOException {
			return this.response.getStatusCode();
		}
		@Override
		public int getRawStatusCode() throws IOException {
			return this.response.getRawStatusCode();
		}
		@Override
		public String getStatusText() throws IOException {
			return this.response.getStatusText();
		}
		@Override
		public HttpHeaders getHeaders() {
			return this.response.getHeaders();
		}
		@Override
		public InputStream getBody() throws IOException {
			if (this.body == null) {
				this.body = StreamUtils.copyToByteArray(this.response.getBody());
			}
			return new ByteArrayInputStream(this.body);
		}
		@Override
		public void close() {
			this.response.close();
		}
	}
}
これで@Autowiredで取得してきたRestTemplateは通信毎にログを吐いてくれます。
一応Githubにサンプルを置いておきます。
スニペットとしておいてあるコードなので多少不要なコードが混じっているのでご了承ください。

Discussion

コメントにはログインが必要です。