一、背景介绍
spring cloud 微服务架构中,需要对依赖服务的请求留痕(URL,出参,入参、耗时等)
1.现状
调用FeignClient(Integration类中)时 手工记录log
缺点:重复工作、log格式不统一
优点:充分满足个性化需求、灵活度极高(再说上天了)
2.目标
实现简洁统一的全局外部服务调用日志输出
示例如下:
get: GET 200 http://wwww.feigntest.com/dict/items?systemId=11 86 RESPONSE BODY:{"code":"000000","success":true} post: POST 200 http://wwww.feigntest.com/limitBill 85 REQUEST BODY:{"custNo":"121212","startTime":null,"endTime":null} RESPONSE BODY:{"code":"001001010","success":false} 异常: POST UnknownHostException http://wwww.feigntest.com/errorUri 206 REQUEST BODY:{"custNo":"121212"}
3.实现基础
基于FeignClient已有的全局请求日志处理类feign.Logger
feign.Logger日志输出级别为debug
输出header信息较多,大多服务并不需要
网上有类似资料,讲解如何启用feign.Logger、如何修改debug为info级别
本文增加对输出内容的简化.
二、如何使用
step 1.重写feign.Logger
-> 修改feign.Logger日志输出级别为info,简化输出内容
package com.cdfinance.mall.config; import feign.Request; import feign.Response; import feign.Util; import lombok.extern.slf4j.Slf4j; import java.io.IOException; import java.util.HashMap; import java.util.Map; import static feign.Util.UTF_8; import static feign.Util.decodeOrDefault; /** * Feign请求日志处理类<br/> * 所有通过Feign请求的外部接口都会被监控 * * @version V1.0 * @author: maoliang * @date: 2020/3/27 */ public class FeignLogger extends feign.Logger { static ThreadLocal<Map<String, String>> logContext = new ThreadLocal(); static String PATH = "path"; static String METHOD = "method"; static String REQUEST_BODY = "body"; protected void logRequest(String configKey, Level logLevel, Request request) { Map<String, String> logMap = new HashMap<>(3); logMap.put(PATH, request.url()); logMap.put(METHOD, request.method()); logMap.put(REQUEST_BODY, request.body() == null ? null : request.charset() == null ? null : new String(request.body(), request.charset())); logContext.set(logMap); } protected Response logAndRebufferResponse( String configKey, Level logLevel, Response response, long elapsedTime) throws IOException { Map<String, String> requetParam = logContext.get(); StringBuilder stringBuilder = new StringBuilder(); stringBuilder .append(requetParam.get(METHOD)).append(" ") .append(response.status()).append(" ") .append(requetParam.get(PATH)).append(" ") .append(elapsedTime); if (requetParam.get(REQUEST_BODY) != null) { stringBuilder.append(" REQUEST BODY:").append(requetParam.get(REQUEST_BODY)); } logContext.remove(); // 返回参数 if (response.body() != null && !(response.status() == 204 || response.status() == 205)) { byte[] bodyData = Util.toByteArray(response.body().asInputStream()); if (bodyData.length > 0) { String responseBody = decodeOrDefault(bodyData, UTF_8, "Binary data"); stringBuilder .append(" RESPONSE BODY:") .append(responseBody.replaceAll("\\s*|\t|\r|\n", "")); } log.info(stringBuilder.toString()); return response.toBuilder().body(bodyData).build(); } log.info(stringBuilder.toString()); return response; } protected IOException logIOException(String configKey, Level logLevel, IOException ioe, long elapsedTime) { Map<String, String> requetParam = logContext.get(); StringBuilder stringBuilder = new StringBuilder(); stringBuilder .append(requetParam.get(METHOD)).append(" ") .append(ioe.getClass().getSimpleName()).append(" ") .append(requetParam.get(PATH)).append(" ") .append(elapsedTime); if (requetParam.get(REQUEST_BODY) != null) { stringBuilder.append(" REQUEST BODY:").append(requetParam.get(REQUEST_BODY)); } log.warn(stringBuilder.toString()); logContext.remove(); return ioe; } protected void log(String configKey, String format, Object... args) { if (log.isInfoEnabled()) { log.info(String.format(methodTag(configKey) + format, args)); } } }
step 2.注册FeignLogger
-> 注册FeignLogger, 设置 Logger.Level为FULL
import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 注册FeignClient请求日志配置 * * @version V1.0 * @author: mlxnle * @date: 2020/04/01 */ public class FeignLoggerConfig { Logger.Level loggerLevel() { return Logger.Level.FULL; } Logger feignLogger() { return new FeignLogger(); // 你的继承类 } }
step 3.开始你的请求
-> 没有第三步! 惊不惊喜
相关推荐
默认情况下,feign通过jdk中的HttpURLConnection向下游服务发起http请求,这种情况下,由于缺乏连接池的支持,在达到一定流量的后服务肯定会出问题.本指南详细介绍feign的实现以及优化
Feign HTTP Client 使用指南 - Tower.zip Feign HTTP Client 使用指南 - Tower.zip
但是,如果是,则请注意将其从任何@ComponentScan中排除,否则将包含此配置,因为它将成为feign.Decoder,feign.Encoder,fei
前八篇文章介绍完了feign-core核心内容,从本篇开始将介绍它的“其它模块”。其实核心模块可以独立的work,但是不免它的能力偏弱,比如只能编码字符串类型、只能解码字符串类型,默认使用java.net.HttpURLConnection...
我们在使用spring cloud时如果集成了springsecurity,那么应用服务A再调用服务B时使用Feign请求会出现401授权认证的问题,那么解决办法就是在feign调用请求时把token携带过去就可以解决这个问题了,引入资源包中的...
springcloud应用之feign
假装React 将Feign与Spring WebFlux一起使用 ... feign-reactor-rx2 :React堆Feign的Rx2兼容实现(取决于feign-reactor-webclient) feign-reactor-jetty :基于React堆Feign的实验性Reactive Jetty客户端实现(不依
feign+springboot的使用
了解RPC框架Feign并实现简单RPC框架
eurekaserver-a,eurekaserver-b,eurekaclient-a,eurekaclient-b是做eureka-server和eureka-client以及整合feign的,该demo整合了通过eureka来创建基于spring cloud的服务注册中心,以及通过feign来进行微服务接口调用.
feign-gson-9.5.1feign-gson-9.5.1 feign-gson-9.5.1feign-gson-9.5.1feign-gson-9.5.1
从零开始学springcloud-Feign附带的示例代码,改代码是feign调用client的示例
赠送jar包:feign-form-spring-3.8.0.jar; 赠送原API文档:feign-form-spring-3.8.0-javadoc.jar; 赠送源代码:feign-form-spring-3.8.0-sources.jar; 赠送Maven依赖信息文件:feign-form-spring-3.8.0.pom; ...
知识点:feign以XML格式传输,domain通过Jackson转成XML, 项目总体理解:此文档搭建了eureka注册中心,和生产者,以及feign消费者。feign消费者以XML格式传输数据
微服务(fegn,eurake)的很精简的demo,分以下几个模块:很合适初学者,以及阅读源码 eureka-client eureka-feign-client eureka-server
Feign是一个声明式的Web Service客户端,它能够让Web Service客户端的编写变得更加容易(你只需创建一个接口,并在接口上添加相应注解即可)。除了Feign自带的注解外它还支持JAX-RS注解,SpringCloud又为Feign增加了...
赠送jar包:feign-core-10.12.jar; 赠送原API文档:feign-core-10.12-javadoc.jar; 赠送源代码:feign-core-10.12-sources.jar; 赠送Maven依赖信息文件:feign-core-10.12.pom; 包含翻译后的API文档:feign-core...
feign例子,创建一个接口并用注解的方式来配置它,即可完成对服务提供方的接口绑定。
Spring Cloud Feign的Decoder 在HTTP协议不是很规范的情况下,需要配置Decoder 具体来说:就是返回数据是JSON,而ContentType 为 text/html;charset=UTF-8
赠送jar包:feign-form-3.8.0.jar; 赠送原API文档:feign-form-3.8.0-javadoc.jar; 赠送源代码:feign-form-3.8.0-sources.jar; 赠送Maven依赖信息文件:feign-form-3.8.0.pom; 包含翻译后的API文档:feign-form...