拦截Spring的RestTemplate打印日志

简单使用

复制@Component
@Slf4j
public class RestTemplateBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof RestTemplate) {
            RestTemplate restTemplate = (RestTemplate) bean;
            List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(restTemplate.getInterceptors());
            restTemplate.setInterceptors(Collections.emptyList());
            //可以多次调用ClientHttpResponse的getBody()方法
            BufferingClientHttpRequestFactory requestFactory = new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory());
            restTemplate.setRequestFactory(requestFactory);
            //打印日志
            interceptors.add(new WebLogInterceptor());
            restTemplate.setInterceptors(interceptors);
        }
        return bean;
    }

    static class WebLogInterceptor implements ClientHttpRequestInterceptor {

        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
            long start = System.currentTimeMillis();
            try {
                ClientHttpResponse response = execution.execute(request, body);
                RequestInfo requestInfo = createRequestInfo(request, body, response, start);
                log.info("Request Info      : {}", JSON.toJSONString(requestInfo));
                return response;
            } catch (Throwable e) {
                RequestErrorInfo requestErrorInfo = createRequestErrorInfo(request, body, e);
                log.error("Error Request Info      : {}", JSON.toJSONString(requestErrorInfo), e);
                throw e;
            }
        }
    }

    private static RequestInfo createRequestInfo(HttpRequest request, byte[] body, ClientHttpResponse response, long start)
            throws IOException {
        long cost = System.currentTimeMillis() - start;
        String result = IOUtils.toString(response.getBody(), StandardCharsets.UTF_8);
        RequestInfo requestInfo = new RequestInfo();
        requestInfo.setUrl(request.getURI().toString());
        requestInfo.setHttpMethod(request.getMethodValue());
        try {
            MediaType requestContentType = request.getHeaders().getContentType();
            if (Objects.nonNull(requestContentType) &&
                    requestContentType.toString().startsWith("application/json")) {
                requestInfo.setRequestParams(JSON.parseObject(new String(body)));
            } else {
                requestInfo.setRequestParams(new String(body));
            }
            MediaType responseContentType = response.getHeaders().getContentType();
            if (Objects.nonNull(responseContentType) &&
                    responseContentType.toString().startsWith("application/json")) {
                requestInfo.setResult(JSON.parseObject(result));
            } else {
                requestInfo.setResult(result);
            }
        } catch (Exception e) {
            log.error("创建RequestInfo错误,", e);
            requestInfo.setRequestParams(new String(body));
            requestInfo.setResult(result);
        }
        requestInfo.setTimeCost(cost);
        return requestInfo;
    }

    private static RequestErrorInfo createRequestErrorInfo(HttpRequest request, byte[] body, Throwable throwable) {
        RequestErrorInfo requestErrorInfo = new RequestErrorInfo();
        requestErrorInfo.setUrl(request.getURI().toString());
        requestErrorInfo.setHttpMethod(request.getMethodValue());
        requestErrorInfo.setExceptionMessage(throwable.getMessage());
        try {
            MediaType requestContentType = request.getHeaders().getContentType();
            if (Objects.nonNull(requestContentType) &&
                    requestContentType.toString().startsWith("application/json")) {
                requestErrorInfo.setRequestParams(JSON.parseObject(new String(body)));
            } else {
                requestErrorInfo.setRequestParams(new String(body));
            }
        } catch (Exception e) {
            log.error("创建RequestErrorInfo错误,", e);
            requestErrorInfo.setRequestParams(new String(body));
        }
        return requestErrorInfo;
    }

    @Data
    public static class RequestInfo {
        private String url;
        private String httpMethod;
        private Object requestParams;
        private Object result;
        private Long timeCost;
    }

    @Data
    public static class RequestErrorInfo {
        private String url;
        private String httpMethod;
        private Object requestParams;
        private String exceptionMessage;
    }
}

通过后置处理器(BeanPostProcessor)拦截 RestTemplate 的初始化过程,给 RestTemplate 添加拦截器。

遇到的问题

问题1

复制Caused by: java.io.IOException: Attempted read from closed stream.

主要是因为 response.getBody() 只能获取一次,在后续 RestTemplate 获取输出转化时会再次调用 response.getBody(),所以我们需要包装一下。

复制//可以多次调用ClientHttpResponse的getBody()方法
BufferingClientHttpRequestFactory requestFactory = new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory());

问题2

我们在调用 restTemplate.getRequestFactory() 前必须先调用 restTemplate.setInterceptors(Collections.emptyList()),不然如果之前已经有拦截器了,那么 interceptingRequestFactory 会被包装两次,所有拦截器都会执行两次。

复制RestTemplate restTemplate = (RestTemplate) bean;
restTemplate.getInterceptors().add(new TestInterceptor()); // 模拟在其他地方设置了拦截器
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(restTemplate.getInterceptors());
//        restTemplate.setInterceptors(Collections.emptyList()); // 如果不置空就会有问题
//可以多次调用ClientHttpResponse的getBody()方法
BufferingClientHttpRequestFactory requestFactory = new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory());
restTemplate.setRequestFactory(requestFactory);
//打印日志
interceptors.add(new WebLogInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;

参考

RestTemplate 打印日志的正确姿势

posted @   strongmore  阅读(65)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
历史上的今天:
2021-03-03 [Leetcode]10. 正则表达式匹配
点击右上角即可分享
微信分享提示