简介

Spring RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程Http服务的方法,使用RestTemplate能够快捷简便的调用其他HTTP服务。

在日常使用RestTemplate的过程中,常需要跟踪RestTemplate发出的请求日志,通过分析发出的请求头、请求方法、请求体等信息进行程序调试,本文将介绍如何自定义RestTemplate日志,以便使用时能够更好的通过日志分析进行程序调试。

日志配置类

先创建自定义日志配置类,实现Spring的http客户端拦截器接口:

@Slf4j
public class RestTemplateLoggingRequestInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        traceRequest(request, body);
        ClientHttpResponse response = execution.execute(request, body);
        traceResponse(response);
        return response;
    }

    private void traceRequest(HttpRequest request, byte[] body) throws IOException {
        log.info("-------------请求开始-------------");
        log.info("URI         : {}", request.getURI());
        log.info("Method      : {}", request.getMethod());
        log.info("Headers     : {}", request.getHeaders());
        log.info("Request body: {}", new String(body, "UTF-8"));
        log.info("-------------请求结束-------------");
    }

    private void traceResponse(ClientHttpResponse response) throws IOException {
        StringBuilder inputStringBuilder = new StringBuilder();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), "UTF-8"));
        String line = bufferedReader.readLine();
        while (line != null) {
            inputStringBuilder.append(line);
            line = bufferedReader.readLine();
        }
        log.info("-------------应答开始-------------");
        log.info("Status code  : {}", response.getStatusCode());
        log.info("Status text  : {}", response.getStatusText());
        log.info("Headers      : {}", response.getHeaders());
        log.info("Response body: {}", inputStringBuilder.toString());
        log.info("-------------应答结束-------------");
    }
}

配置RestTemplate

编写完自定义日志配置类,还需要将日志配置类设置进RestTemplate中才能使其生效,下面介绍两种配置方式:

使用RestTemplateBuilder:

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate getRestTemplate(RestTemplateBuilder restTemplateBuilder) {
        return restTemplateBuilder
                .requestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()))
                .additionalInterceptors(new RestTemplateLoggingRequestInterceptor())
                .build();
    }
}

直接new一个RestTemplate客户端:

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(){
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
        restTemplate.setInterceptors(Arrays.asList(new RestTemplateLoggingRequestInterceptor()));
        return restTemplate;
    }
}

当然,本文是将RestTemplate注册成了Bean,在需要用到的地方注入依赖即可,如果不需要注册成Bean,在使用到的地方new一个RestTemplate实例出来,根据自己需要配置相关的东西就行了,RestTemplate还有超时时间等其他配置,这里就不过多介绍。

需要注意的是,上面的代码中可以看到,除了配置ClientHttpRequestInterceptor之外,还配置了RestTemplate的ClientHttpRequestFactory,这是因为RestTemplate默认使用的是SimpleClientHttpRequestFactory,它的Body流只能读取一次,如果我们记录日志的时候,把Body读取打印出来,那么接下去在去读Body流的时候读不到就会报错,所以如果需要在日志中记录应答的Body,就可以使用BufferingClientHttpRequestFactory,它会通过用缓冲流存储请求体的方式,让我们可以多次重复读取Body流。