博客
关于我
【RestTemplate】统一记录RestTemplate的调用日志
阅读量:490 次
发布时间:2019-03-06

本文共 5355 字,大约阅读时间需要 17 分钟。

文章目录

重写ClientHttpRequestInterceptor统一记录RestTemplate的调用日志

现如今比较火的微服务架构,SpringCloud顺势成为了主流框架,当然SpringCloud并不是一个框架,而是一个框架的集合。不管是否为微服务,难免会有程序之间的调用,当然zipkin可以帮助收集时间数据,解决在微服务架构下的延迟问题,如何详细记录请求以及返回的信息变得比较重要。

RestTemplate 对rest复杂请求封装简单的调用方法,默认JDK facilities。当然你也可以 通过setRequestFactory属性切换到不同的HTTP源,比如Apache HttpComponents、Netty和OkHttp 具体可以看ClientHttpRequestFactory 实现。当然本质上做是封装的匣子,使用者方便很多。

以上均是本章节的废话,转入正题如何优雅的记录请求以及返回信息。当然考虑一个问题得有切入点,知道@LoadBalanced注解的当然很容易理解

Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient.

其实他也就是个Interceptor 默认LoadBalancerInterceptor详见LoadBalancerAutoConfiguration类,它可以做到地址的‘偷梁换柱’,记录请求信息也就没有那么困难。新加一个Interceptor就可以解决,当然这不怎么太完美,众所周知http输入输出流只能读取一次的问题,当然spring比较完美的是做了处理。详看 BufferingClientHttpRequestFactory#createRequest 方法BufferingClientHttpRequestWrapper#executeInternal,提供对输入/输出流的缓冲。ok前提了解工作已备齐,撸码开始。

LogClientHttpRequestInterceptor.java

@Slf4j(topic = "outgoing")public class LogClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {      @Override   public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {          StopWatch stopWatch = new StopWatch();       stopWatch.start();       ClientHttpResponse response = execution.execute(request, body);       stopWatch.stop();       StringBuilder resBody = new StringBuilder();       try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(),               Charset.forName("UTF-8")))) {              String line = bufferedReader.readLine();           while (line != null) {                  resBody.append(line);               line = bufferedReader.readLine();           }       }        //当然图片、文件一类的就可以省了,打出日志没啥用处,此处的业务逻辑随意撸了,比如header头信息类似于  Accept 、Accept-Encoding 、Accept-Language、Connection 等等       if (request.getHeaders().getContentType() != null && request.getHeaders().getContentType().includes(MediaType.MULTIPART_FORM_DATA)) {              body = new byte[]{   };       }       log.info(JSON.toJSONString(RestLog.builder().costTime(stopWatch.getLastTaskTimeMillis()).headers(request.getHeaders()).method(request.getMethodValue())               .reqUrl(request.getURI().toString()).reqBody(new String(body, Charset.forName("UTF-8"))).resBody(resBody.toString()).resStatus(response.getRawStatusCode()).build()));       return response;   }   @Data   @Builder   @SuppressWarnings("rawtypes")   private static class RestLog {          private String reqUrl;       private String method;       private HttpHeaders headers;       private String reqBody;       private String resBody;       private long costTime;       private int resStatus;   }}
@Beanpublic RestTemplate restTemplate(ClientHttpRequestFactory httpRequestFactory) {       RestTemplate restTemplate = new RestTemplate();    /**     * StringHttpMessageConverter 默认使用ISO-8859-1编码,此处修改为UTF-8     */    List
> messageConverters = restTemplate.getMessageConverters(); Iterator
> iterator = messageConverters.iterator(); while (iterator.hasNext()) { HttpMessageConverter
converter = iterator.next(); if (converter instanceof StringHttpMessageConverter) { ((StringHttpMessageConverter) converter).setDefaultCharset(Charset.forName("UTF-8")); } } //Interceptors 添加写的 Interceptors restTemplate.setInterceptors(Lists.newArrayList( new LogClientHttpRequestInterceptor())); //BufferingClientHttpRequestFactory 此处替换为BufferingClientHttpRequestFactory restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(httpRequestFactory)); return restTemplate;}

完成搞定,当然还可以扩展

扩展1 上面指定了@Slf4j(topic = "outgoing") 可以配合logback 输出到指定的文件,收集到es中做数据监控用,毕竟是符合规则的json格式,当然接口间调用媒体类型都是application/json格式,response对象也可以通过判断contentType进行格式化输出标准的json格式,出去tostring带来的转移符。
扩展2 应用于Interceptors 我们还可以添加相应的请求头信息,方便日志追踪,附赠

ClientHttpRequestInterceptor.java

public class RequestIdInterceptor implements ClientHttpRequestInterceptor {      private static final String REQUEST_ID = "X-Request-Id";   @Override   public ClientHttpResponse intercept(HttpRequest request, byte[] body,                                       ClientHttpRequestExecution execution) throws IOException {          if (CollectionUtils.isEmpty(request.getHeaders().get(REQUEST_ID))) {              request.getHeaders().set(REQUEST_ID, UUID.randomUUID().toString());       }       return execution.execute(request, body);   }}

初始化restTemplate对象时restTemplate.setInterceptors(Lists.newArrayList(new RequestIdInterceptor(), new LogClientHttpRequestInterceptor())); 即可。

那么问题来了,很多个Interceptors 执行的顺序怎么保证,如果你能想到这个问题,那么你真是个机灵鬼。话不多说,详看InterceptingHttpAccessor.java#setInterceptors

当然spring也有自己的详看HttpHeaderInterceptor 当然还是自己撸一把比较爽吧。

/** * Set the request interceptors that this accessor should use. * 

The interceptors will get sorted according to their order * once the {@link ClientHttpRequestFactory} will be built. * @see #getRequestFactory() * @see AnnotationAwareOrderComparator */public void setInterceptors(List

interceptors) { // Take getInterceptors() List as-is when passed in here if (this.interceptors != interceptors) { this.interceptors.clear(); this.interceptors.addAll(interceptors); AnnotationAwareOrderComparator.sort(this.interceptors); }}

好吧,只能说spring是真鸡儿机智。

参考

本文转载自CSDN平台SoberChina的一文。

你可能感兴趣的文章
MySQL5.7.18主从复制搭建(一主一从)
查看>>
MySQL5.7.19-win64安装启动
查看>>
mysql5.7.19安装图解_mysql5.7.19 winx64解压缩版安装配置教程
查看>>
MySQL5.7.37windows解压版的安装使用
查看>>
mysql5.7免费下载地址
查看>>
mysql5.7命令总结
查看>>
mysql5.7安装
查看>>
mysql5.7性能调优my.ini
查看>>
MySQL5.7新增Performance Schema表
查看>>
Mysql5.7深入学习 1.MySQL 5.7 中的新增功能
查看>>
Webpack 之 basic chunk graph
查看>>
Mysql5.7版本单机版my.cnf配置文件
查看>>
mysql5.7的安装和Navicat的安装
查看>>
mysql5.7示例数据库_Linux MySQL5.7多实例数据库配置
查看>>
Mysql8 数据库安装及主从配置 | Spring Cloud 2
查看>>
mysql8 配置文件配置group 问题 sql语句group不能使用报错解决 mysql8.X版本的my.cnf配置文件 my.cnf文件 能够使用的my.cnf配置文件
查看>>
MySQL8.0.29启动报错Different lower_case_table_names settings for server (‘0‘) and data dictionary (‘1‘)
查看>>
MYSQL8.0以上忘记root密码
查看>>
Mysql8.0以上重置初始密码的方法
查看>>
mysql8.0新特性-自增变量的持久化
查看>>