现如今比较火的微服务架构,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


/**
* restTemplate log interceptor
*
* @author liweigao
* @date 2019/7/9 下午2:06
*/
@Slf4j(topic = "outgoing")
public class LogClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {@Overridepublic 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<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();Iterator<HttpMessageConverter<?>> iterator = messageConverters.iterator();while (iterator.hasNext()) {HttpMessageConverter<?> converter = iterator.next();if (converter instanceof StringHttpMessageConverter) {((StringHttpMessageConverter) converter).setDefaultCharset(Charset.forName("UTF-8"));}}//Interceptors 添加写的 InterceptorsrestTemplate.setInterceptors(Lists.newArrayList(new LogClientHttpRequestInterceptor()));//BufferingClientHttpRequestFactory  此处替换为BufferingClientHttpRequestFactoryrestTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(httpRequestFactory));return restTemplate;}

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

ClientHttpRequestInterceptor.java

/**
* requestId interceptor
*
* @author liweigao
* @date 2019/7/9 下午2:52
*/
public class RequestIdInterceptor implements ClientHttpRequestInterceptor {private static final String REQUEST_ID = "X-Request-Id";@Overridepublic 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.* <p>The interceptors will get sorted according to their order* once the {@link ClientHttpRequestFactory} will be built.* @see #getRequestFactory()* @see AnnotationAwareOrderComparator*/public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {// Take getInterceptors() List as-is when passed in hereif (this.interceptors != interceptors) {this.interceptors.clear();this.interceptors.addAll(interceptors);AnnotationAwareOrderComparator.sort(this.interceptors);}}
好吧,只能说spring是真鸡儿机智。
如果本片对你有帮助,点不点赞没关系,方正不费事,评论不评论也没关系,方正也不费事。大神留步欢迎留下脚印,欢迎指导教育,谢谢~

如何配合RestTemplate优雅的记录请求以及返回的信息相关推荐

  1. nginx log记录请求响应日志及日志分割

    之前部署了quic的集群在aws,在测试的时候发现在大报文的情形下HTTP3的请求耗时比较不稳定,并且耗时比普通的HTTP2要大很多,就想看看请求的具体耗时有多少 请求响应日志记录 我的quic集群是 ...

  2. Springboot使用AOP记录请求日志和返回数据

    首先是日志表结构 DROP TABLE IF EXISTS `protal_logvo`; CREATE TABLE `protal_logvo` (`id` varchar(255) NOT NUL ...

  3. ASP.NET Web API 记录请求响应数据到日志的一个方法

    原文:ASP.NET Web API 记录请求响应数据到日志的一个方法 原文:http://blog.bossma.cn/dotnet/asp-net-web-api-log-request-resp ...

  4. 在业务层实现记录请求日志

    前言 上次,我们介绍了如何<在业务层实现响应缓存>. 今天,我们同样使用IPipelineBehavior,介绍如何在业务层实现记录请求日志,用于跟踪每个请求执行的耗时. Demo 创建A ...

  5. 记录请求的耗时(拦截器、过滤器、aspect)

    记录请求的耗时(拦截器.过滤器.aspect) 文章前言 记录控制器请求的耗时处理通常有三种实现方式,分别是:过滤器.拦截器.aspect:下文将逐一实现. 1.Filter 过滤器 1.1.方法说明 ...

  6. log nginx 客户端请求大小_nginx log记录请求响应时间

    有时为了方便分析接口性能等,需要记录请求的时长,通过修改nginx的日志格式可以做到,如 添加一个新的log_format log_format  timed_combined  '$remote_a ...

  7. 添加日志_第五章springboot2.0添加aop日志实现记录请求地址

    1. 添加spring-boot-starter-aop包 <dependency> <groupId>org.springframework.bootgroupId> ...

  8. springboot+aop切点记录请求和响应信息

    本篇主要分享的是springboot中结合aop方式来记录请求参数和响应的数据信息:这里主要讲解两种切入点方式,一种方法切入,一种注解切入:首先创建个springboot测试工程并通过maven添加如 ...

  9. 【实践】万字干货:如何优雅地记录操作日志?(附代码)

    猜你喜欢 1.如何搭建一套个性化推荐系统? 2.从零开始搭建创业公司后台技术栈 3.某视频APP推荐详解(万字长文) 4.微博推荐算法实践与机器学习平台演进 5.腾讯PCG推荐系统应用实践 6.强化学 ...

最新文章

  1. bzoj 3505: [Cqoi2014]数三角形
  2. 最大子段和问题分析和总结
  3. JavaScript高级程序设计(第3版)手写第一天。2019年2月23日,星期六
  4. 李嘉诚那么有钱,为什么还要把国内很多资产卖掉?
  5. mongodb php 扩展 linux,CentOS Linux 安装PHP的MongoDB扩展
  6. thinkphp json_原创干货 | Thinkphp序列化合总
  7. java学习笔记之条件语句(if...else)
  8. 前端学习(1266):axios的常见api
  9. python exchangelib 删除邮件_Python优雅的操作Exchange邮箱——exchangelib模块使用介绍...
  10. 每天10分钟就能练出流利口语
  11. java 代理ip工具类_Java基础之java处理ip的工具类
  12. 芝柏 bmw oracle,魅力十足的潜水腕表
  13. 尺取法 POJ 3320 Jessica's Reading Problem
  14. 优化 Go 中的 map 并发存取
  15. elementui表单校验原始密码_玩转表单交互,提升用户体验
  16. centos 7安装搭建confluence-wiki
  17. Hadoop安装教程 Mac版
  18. DotNetTools Workflow教程
  19. 初中学历可以做原画师吗?原画师需要绘画基础吗
  20. 服装行业施行ERP体系的首要好处是什么?

热门文章

  1. pytorch中的Variable还有必要使用吗?
  2. 软件测试01 概念、流程、分类、策略
  3. Java语言每日一练—第14天:银行收入计算
  4. 虚幻C++学习笔记01--官方案例:FloatingActor
  5. 日程安排小程序实战教程(上篇)
  6. altera 设计--仿真--下载
  7. 全面解读 Java 现状及未来发展趋势
  8. win7删除u盘linux分区,WIN7系统不用分区工具如何创建、删除和格式化分区
  9. 7、数据恢复:文件误删、硬盘被格式化后的数据恢复
  10. js中一种常见条件判断if(var)的坑