LoginSignup
21
25

More than 5 years have passed since last update.

RestTemplateでリクエストやレスポンスの中身をログに出す

Last updated at Posted at 2018-05-09

目的

RestTemplateで外部API等を叩いたときのリクエストやレスポンスの情報をログに落とすやり方を調べてまとめた。

やり方は2通り

  1. RestTemplateのログレベルをDEBUGに設定する方法
  2. ClientHttpRequestInterceptorを使ってリクエストのタイミングで処理を差し込む方法

1のやり方は設定を足すだけなのでめちゃめちゃ簡単です。ただ、おそらくですが実装をさらっと見た感じではログの中身をカスタマイズできなそうなので、とりあえずログ見れればいいっすってときにはよさそうって感じ。

2のやり方は実装は必要でちょっと手間がかかりますが、任意の処理が実行可能なので好きな内容をログに出せます。
今回はログ用途で使いますが、ログに限らず任意の処理が差し込み可能です。

順番に試してみたいと思います。

環境

Java8
Spring 4.3.14.RELEASE
SpringBoot 1.5.14.RELEASE

1. RestTemplateのログレベルをDEBUGに設定する方法

設定ファイルに下記を追加する

src/main/resources/application.yml
logging:
  level:
    org.springframework.web.client.RestTemplate: DEBUG

動かしてみると下記のようなログが出ます

level:DEBUG Created GET request for "https://example.com"
level:DEBUG Setting request Accept header to [text/plain, text/plain, application/json, application/json, application/*+json, application/*+json, */*, */*]
level:DEBUG GET request for "https://example.com" resulted in 200 (OK)
level:DEBUG Reading [java.lang.String] as "text/html" using [org.springframework.http.converter.StringHttpMessageConverter@6a714237]

ざっと見た感じ出てる情報は以下

  • リクエスト先URL
  • リクエストヘッダ
  • ステータスコード
  • レスポンスのContent-Type
  • レスポンスボディに適用されるConverterの型とか

2. ClientHttpRequestInterceptorを使ってリクエストのタイミングで処理を差し込む方法

必要な作業をざっくりと

  1. org.springframework.http.client.ClientHttpRequestInterceptorインタフェースをimplementsしたクラスを作り、interceptメソッドにログ出力など任意の処理を実装する.
  2. RestTempalteに1で作ったInterceptorをaddしてあげて、呼ばれるようにする

以上です

やってみる

まずはClientHttpRequestInterceptorインタフェースを実装したクラスを作る

ClientHttpRequestInterceptorインタフェースを実装したクラスを下記の要領で作る。

@Slf4j
public class RestTemplateLoggingInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        // リクエストの中身をログに落とす
        log.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();
        }

        // レスポンスの中身をログに落とす
        log.info("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();
        }

    }
}

interceptメソッドの中身はよしなに変えれば、ログの中身をカスタマイズできます。
ネストクラスでBufferingClientHttpResponseWrapperを実装しているのは、レスポンスが1度しか読み込めないらしいのでラップするクラスを作っています。Springのソースを追っていたらorg.springframework.http.client.BufferingClientHttpRequestWrapperクラスがパッケージプライベートで存在していたので、コピペで使っちゃいました。

上で作ったInterceptorが呼ばれるようにする

RestTemplateBuilderを使ってRestTemplateを作るときに下記の要領で、自分で作ったRestTemplateLoggingInterceptorをaddすればOKです

RestTemplate restTemplate = restTemplateBuilder
    .additionalInterceptors(new RestTemplateLoggingInterceptor()); // ここ
    .build();

↑のやり方でも全然良いですが、「RestTemplateを使っている箇所全部に足すのはちょっと、、」って感じる場合は、下記のようにBean登録してあげると、使う側が何も気にしなくてよくなるので、こうやって使うのが良いんじゃないかと思います。
使う側はRestTemplateBuilderのインスタンスをDIコンテナから取得すれば、今回作ったRestTemplateLoggingInterceptorがaddされた状態で取得できるようになります。

@Slf4j
@Configuration
public class RestTemplateLoggingInterceptor implements ClientHttpRequestInterceptor {

    @Bean
    public RestTemplateBuilder restTemplateBuilder() {
        return new RestTemplateBuilder()
                .additionalInterceptors(new RestTemplateLoggingInterceptor());
    }

    //.....下は割愛

使ってみる

使う側のコードイメージです。普通にRestTemplateを使うだけです。

@Component
public class Sample {

    private final RestTemplate restTemplate;

    public Sample(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder
                .setConnectTimeout(1000)
                .setReadTimeout(1000)
                .build();
    }

    public String get() {
        return restTemplate.getForObject("https://example.com", String.class);
    }
}

動かしてみると下記の感じでログが出ます!

level:INFO  RestTemplate Request: URI=https://example.com, Headers={Accept=[text/plain, application/json, application/*+json, */*], Content-Length=[0]}, Body=
level:INFO  RestTemplate Response: StatusCode=200 OK, Headers={Accept-Ranges=[bytes], Cache-Control=[max-age=604800], Content-Type=[text/html], Date=[Wed, 09 May 2018 08:23:02 GMT], Expires=[Wed, 16 May 2018 08:23:02 GMT], Last-Modified=[Fri, 09 Aug 2013 23:54:35 GMT], Body=<!doctype html>\n<html>\n</html>\n

参考

RestTepmlateのログが出したい | エンジニアっぽいことを書くブログ
java - Spring RestTemplate - how to enable full debugging/logging of requests/responses? - Stack Overflow

21
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
25