【Feign扩展】OpenFeign日志打印Http请求参数和响应数据
SpringBoot使用log4j2
在Spring Boot中所有的starter 都是基于spring-boot-starter-logging的,默认使用Logback。使用Log4j2的话,你需要排除 spring-boot-starter-logging 的依赖,并添加 spring-boot-starter-log4j2的依赖。
配置依赖
<!-- 自定义验证注解 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency>
OpenFeign日志配置
feign的配置类
public class BaseFeignConfig {@BeanLogger.Level feignLoggerLevel() {return Logger.Level.FULL;}
}
单个服务应用配置类
@FeignClient(value = "crm-base",configuration = BaseFeignConfig.class )
public interface BaseFeign {@GetMapping("/getAppInfo")ResponseEx<Object> getAppInfo(@RequestParam(name = "appId") Integer appId);
}
日志级别配置
logging.level.com.charles.feign.BaseFeign=debug
打印效果
23-04-18 15:01:04 DEBUG http-nio-8080-exec-1 crm-emergency traceId- userId- com.linjiu.feign.BaseFeign.log:72:[BaseFeign#getAppInfo] ---> GET http://crm-base/getAppInfo?appId=-1 HTTP/1.1
2023-04-18 15:01:04 DEBUG http-nio-8080-exec-1 crm-emergency traceId- userId- com.linjiu.feign.BaseFeign.log:72:[BaseFeign#getAppInfo] ---> END HTTP (0-byte body)
2023-04-18 15:02:37 DEBUG http-nio-8080-exec-3 crm-emergency traceId- userId- com.linjiu.feign.BaseFeign.log:72:[BaseFeign#getAppInfo] ---> END HTTP (0-byte body)
2023-04-18 15:02:38 DEBUG http-nio-8080-exec-3 crm-emergency traceId- userId- com.linjiu.feign.BaseFeign.log:72:[BaseFeign#getAppInfo] <--- HTTP/1.1 200 (205ms)
2023-04-18 15:02:38 DEBUG http-nio-8080-exec-3 crm-emergency traceId- userId- com.linjiu.feign.BaseFeign.log:72:[BaseFeign#getAppInfo] connection: keep-alive
2023-04-18 15:02:38 DEBUG http-nio-8080-exec-3 crm-emergency traceId- userId- com.linjiu.feign.BaseFeign.log:72:[BaseFeign#getAppInfo] content-type: application/json
2023-04-18 15:02:38 DEBUG http-nio-8080-exec-3 crm-emergency traceId- userId- com.linjiu.feign.BaseFeign.log:72:[BaseFeign#getAppInfo] date: Tue, 18 Apr 2023 07:02:38 GMT
2023-04-18 15:02:38 DEBUG http-nio-8080-exec-3 crm-emergency traceId- userId- com.linjiu.feign.BaseFeign.log:72:[BaseFeign#getAppInfo] keep-alive: timeout=60
2023-04-18 15:02:38 DEBUG http-nio-8080-exec-3 crm-emergency traceId- userId- com.linjiu.feign.BaseFeign.log:72:[BaseFeign#getAppInfo] transfer-encoding: chunked
2023-04-18 15:02:38 DEBUG http-nio-8080-exec-3 crm-emergency traceId- userId- com.linjiu.feign.BaseFeign.log:72:[BaseFeign#getAppInfo]
2023-04-18 15:02:38 DEBUG http-nio-8080-exec-3 crm-emergency traceId- userId- com.linjiu.feign.BaseFeign.log:72:[BaseFeign#getAppInfo] {"data":[{ ...
源码解析
SynchronousMethodHandler#executeAndDecode,发送请求。如果日志级别不是Logger.Level.NONE,打印日志。
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {Request request = targetRequest(template);if (logLevel != Logger.Level.NONE) {logger.logRequest(metadata.configKey(), logLevel, request);}Response response;long start = System.nanoTime();try {response = client.execute(request, options);// ensure the request is set. TODO: remove in Feign 12response = response.toBuilder().request(request).requestTemplate(template).build();} catch (IOException e) {if (logLevel != Logger.Level.NONE) {logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));}throw errorExecuting(request, e);}long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);if (decoder != null)return decoder.decode(response, metadata.returnType());CompletableFuture<Object> resultFuture = new CompletableFuture<>();asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,metadata.returnType(),elapsedTime);try {if (!resultFuture.isDone())throw new IllegalStateException("Response handling not done");return resultFuture.join();} catch (CompletionException e) {Throwable cause = e.getCause();if (cause != null)throw cause;throw e;}}
Slf4jLogger#logRequest,判断是否允许debug日志输出
protected void logRequest(String configKey, Level logLevel, Request request) {if (this.logger.isDebugEnabled()) {super.logRequest(configKey, logLevel, request);}}
Logger#logRequest,打印具体的请求日志。
protected void logRequest(String configKey, Level logLevel, Request request) {log(configKey, "---> %s %s HTTP/1.1", request.httpMethod().name(), request.url());if (logLevel.ordinal() >= Level.HEADERS.ordinal()) {for (String field : request.headers().keySet()) {for (String value : valuesOrEmpty(request.headers(), field)) {log(configKey, "%s: %s", field, value);}}int bodyLength = 0;if (request.body() != null) {bodyLength = request.length();if (logLevel.ordinal() >= Level.FULL.ordinal()) {String bodyText =request.charset() != null? new String(request.body(), request.charset()): null;log(configKey, ""); // CRLFlog(configKey, "%s", bodyText != null ? bodyText : "Binary data");}}log(configKey, "---> END HTTP (%s-byte body)", bodyLength);}}
AsyncResponseHandler#handleResponse,处理响应。打印日志用的是logger.logAndRebufferResponse
void handleResponse(CompletableFuture<Object> resultFuture,String configKey,Response response,Type returnType,long elapsedTime) {// copied fairly liberally from SynchronousMethodHandlerboolean shouldClose = true;try {if (logLevel != Level.NONE) {response = logger.logAndRebufferResponse(configKey, logLevel, response,elapsedTime);}} catch (final Exception e) {resultFuture.completeExceptionally(e);}}
Logger实体类创建,FeignClientFactoryBean#feign创建Feign。
protected Builder feign(FeignContext context) {FeignLoggerFactory loggerFactory = (FeignLoggerFactory)this.get(context, FeignLoggerFactory.class);Logger logger = loggerFactory.create(this.type);Builder builder = ((Builder)this.get(context, Builder.class)).logger(logger).encoder((Encoder)this.get(context, Encoder.class)).decoder((Decoder)this.get(context, Decoder.class)).contract((Contract)this.get(context, Contract.class));this.configureFeign(context, builder);return builder;}
DefaultFeignLoggerFactory#create,创建日志类。
public Logger create(Class<?> type) {return (Logger)(this.logger != null ? this.logger : new Slf4jLogger(type));}
使用slf4j进行创建,类型是com.linjiu.feign.BaseFeign。这也解释了需要配置logging.level.com.charles.feign.BaseFeign=debug才能输出日志。有时间专门来一篇博客,看看slf4j日志的生效逻辑和Springboot日志生效的逻辑。
public Slf4jLogger(Class<?> clazz) {this(LoggerFactory.getLogger(clazz));}

相关文章:
【Feign扩展】OpenFeign日志打印Http请求参数和响应数据
SpringBoot使用log4j2 在Spring Boot中所有的starter 都是基于spring-boot-starter-logging的,默认使用Logback。使用Log4j2的话,你需要排除 spring-boot-starter-logging 的依赖,并添加 spring-boot-starter-log4j2的依赖。 配置依赖 <…...
MongoDB (零) 安装和简单使用
1.安装(Ubuntu) 1.1.安装gnupg sudo apt-get install gnupg1.2.获取GPG Key curl -fsSL https://pgp.mongodb.com/server-6.0.asc | \sudo gpg -o /usr/share/keyrings/mongodb-server-6.0.gpg \--dearmor1.3.创建本地文件 echo "deb [ archamd64,arm64 signed-by/usr…...
Java中的异常是什么?
Java中的异常是指在程序运行时发生的错误或异常情况。这些异常可能会导致程序崩溃或无法正确执行,因此需要在代码中进行处理。Java中的异常机制可以帮助程序员捕获并处理异常,从而保证程序的稳定性和可靠性。 Java中的异常分为两种类型:受检…...
微短剧“小阳春”,“爱优腾芒”抢滩登陆?
降本增效一整年,长视频平台们似乎扭转了市场对于它们“烧钱”的印象。 爱奇艺宣布2022全年盈利,腾讯视频宣布从去年10月起开始盈利,视频平台们结束了一场“无限战争”。 与此同时,随着短视频平台的崛起,视频内容的形…...
C++菱形继承(再剖析)
当子类对象给父类对象的时候,怎么找公共的虚基类(A) 就得通过偏移量来算虚基类的位置 ---------------------------------------------------------------------------------------------------------------------------- 我们来分析一下B…...
java获取星期几
如果你要问 java什么时候学习比较好,那么答案肯定是 java的星期几。 在 Java中,你可以使用 public static void main ()方法来获取一个类的所有成员变量,然后在所有类中调用这个方法来获取对象的所有成员变量。它能以对…...
【TypeScript】03-TypeScript基本类型
TypeScript基本类型 在TypeScript中,基本类型是非常重要的一部分,下面我们将详细介绍TypeScript中的基本类型。 基本类型约束 在TypeScript中,可以使用基本类型来约束变量的类型。常见的基本类型有: number:表示数…...
什么是跨域?
什么是跨域 什么是跨域? 什么是同源策略及其限制内容? 同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。所谓同源是指"协议域名端口"三者相…...
Gradle理论与实践—Gradle构建脚本基础
Gradle构建脚本基础 Project: 根据业务抽取出来的一个个独立的模块Task:一个操作,一个原子性操作。比如上传一个jar到maven中心库等Setting.gradle文件:初始化及整个工程的配置入口build.gradle文件: 每个Project都会有个build.gradle的文件…...
【Vue 基础】vue-cli初始化项目及相关说明
目录 1. 创建项目 2. 项目文件介绍 3. 项目的其它配置 3.1 项目运行时,让浏览器自动打开 3.2 关闭eslint校验功能 3.3 src文件夹简写方法 1. 创建项目 vue create 项目名 2. 项目文件介绍 创建好的项目中包含如下文件: (1)…...
【c语言】详解c语言#预处理期过程 | 宏定义前言
c语言系列专栏: c语言之路重点知识整合 创作不易,本篇文章如果帮助到了你,还请点赞支持一下♡>𖥦<)!! 主页专栏有更多知识,如有疑问欢迎大家指正讨论,共同进步! 给大家跳段街舞感谢支持…...
内网远程控制软件哪个好用
市面上远程控制软件很多,但是支持纯内网环境(无外网)的很少。大部分远程控制软件可以在局域网用,但是它的数据流量还是要走软件公司服务器,也就是要走外网,所以在纯内网环境没法使用。那么什么软件支持纯内…...
【计算机基本原理-数据结构】数据结构中树的详解
【计算机基本原理-数据结构】数据结构中树的详解 1)总览2)树的相关概念3)二叉树、满二叉树、完全二叉树4)二叉查找树 - BST5)平衡二叉树 - AVL6)红黑树7)哈弗曼树8)B 树9)…...
数字设计小思 - D触发器与死缠烂打的亚稳态
前言 本系列整理数字系统设计的相关知识体系架构,为了方便后续自己查阅与求职准备。在FPGA和ASIC设计中,D触发器是最常用的器件,也可以说是时序逻辑的核心,本文根据个人的思考历程结合相关书籍内容和网上文章,聊一聊D…...
Notes/Domino 11.0.1FP7以及在NAS上安装Domino等
大家好,才是真的好。 目前HCL在还是支持更新的Notes/Domino主要是三个版本,V10、11和12,这不,上周HCL Notes/Domino 11.0.1居然推出了FP7补丁包程序。 从V10.0.1开始,Domino的FP补丁包程序主要是用来修复对应主要版本中的一些问…...
【VM服务管家】VM4.x算子SDK开发_3.3 模块工具类
目录 3.3.1 位置修正:位置修正算子工具的使用方法3.3.2 模板保存:实现模板自动加载的方法3.3.3 模板匹配: 获取模板匹配框和轮廓点的方法3.3.4 模板训练:模板训练执行完成的判断方法3.3.5 图像相减:算子SDK开发图像相减…...
Aspose.Pdf使用教程:在PDF文件中添加水印
Aspose.PDF 是一款高级PDF处理API,可以在跨平台应用程序中轻松生成,修改,转换,呈现,保护和打印文档。无需使用Adobe Acrobat。此外,API提供压缩选项,表创建和处理,图形和图像功能&am…...
H.264/AVC加密----选择加密
文献学习: 《Data Hiding in Encrypted H.264/AVC Video Streams by Codeword Substitution》 期刊:IEEE TRANSACTIONS ON INFORMATION FORENSICS AND SECURITY 简介 通过分析H.264/AVC编解码器的特性,提出了三个敏感部分(IPM、MVD和残差系…...
WuThreat身份安全云-TVD每日漏洞情报-2023-04-26
漏洞名称:Google Android 命令注入漏洞 漏洞级别:高危 漏洞编号:CVE-2023-20964,CNNVD-202303-538 相关涉及:None 漏洞状态:POC 参考链接:https://tvd.wuthreat.com/#/listDetail?TVD_IDTVD-2023-05794 漏洞名称:OpenSSL RSA 解密时间差异 漏洞级别:中危 漏洞编号:CVE-2022-4…...
剑指 Offer第二版:1~n 整数中 1 出现的次数、51. 数组中的逆序对、56 - II. 数组中数字出现的次数 II
剑指 Offer第二版 43. 1~n 整数中 1 出现的次数51. 数组中的逆序对56 - II. 数组中数字出现的次数 II 43. 1~n 整数中 1 出现的次数 题目:输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。 例如,…...
思源宋体TTF:5分钟掌握免费商用中文字体的完整使用指南
思源宋体TTF:5分钟掌握免费商用中文字体的完整使用指南 【免费下载链接】source-han-serif-ttf Source Han Serif TTF 项目地址: https://gitcode.com/gh_mirrors/so/source-han-serif-ttf 如果你正在寻找一款既专业又免费的中文字体,那么思源宋体…...
PotPlayer智能字幕翻译:用百度翻译API打破语言障碍的观影体验
PotPlayer智能字幕翻译:用百度翻译API打破语言障碍的观影体验 【免费下载链接】PotPlayer_Subtitle_Translate_Baidu PotPlayer 字幕在线翻译插件 - 百度平台 项目地址: https://gitcode.com/gh_mirrors/po/PotPlayer_Subtitle_Translate_Baidu 你是否曾在观…...
别再死记FPN公式了!用PyTorch手把手带你画一遍特征金字塔的‘数据流图’
用PyTorch动态可视化FPN特征金字塔的数据流动 在目标检测领域,特征金字塔网络(FPN)已经成为处理多尺度目标的标配组件。但很多开发者虽然能背诵FPN的结构图,却对特征图在金字塔各层之间的流动变化缺乏直观感受。本文将带您用PyTor…...
SolidWorks插件开发避坑指南:手把手教你搞定工具栏图标和菜单注册表清理
SolidWorks插件开发深度优化:图标管理与注册表清理实战 当你在SolidWorks插件开发中精心设计了功能完备的工具栏,却遭遇图标显示异常、工具栏名称重复或旧插件残留等问题时,那种挫败感每个开发者都深有体会。这些看似简单的界面问题背后&…...
麦当劳中国启动2026全国招聘周招募新生代人才
美通社消息:麦当劳中国正式启动2026年全国招聘周。今年,首批年满16周岁的10后将步入职场,与00后共同构成新生代主力军。在AI的变革时代,麦当劳以"有保障、有福利、有发展"的薪酬福利成长体系,以及长期、系统…...
桌面音乐可视化革命:Lano Visualizer如何让你的音乐“看得见“
桌面音乐可视化革命:Lano Visualizer如何让你的音乐"看得见" 【免费下载链接】Lano-Visualizer A simple but highly configurable visualizer with rounded bars. 项目地址: https://gitcode.com/gh_mirrors/la/Lano-Visualizer 在数字时代&#…...
FRED应用:背散射教程
这个教程描述一个有散射性质的简单plano-plano透镜,这样一条入射光就会散射回发射方向。教程首先,在FRED中创建一个新的系统,在树视图中的Geometry上右击,选择“Create New Lens…”并在出现的对话框上点OK按钮,在全局…...
别再死记硬背物联网四层架构了!用LoRa和ESP32手把手搭个智能花盆,实战理解每一层
从智能花盆实战理解物联网四层架构:LoRaESP32全流程拆解 每次翻开物联网教材,总能看到那个经典的四层架构图:感知层、网络层、平台层、应用层。但真正动手做项目时,却发现理论和实践之间隔着一道鸿沟。今天我们就用最接地气的方式…...
8051中断向量冲突与Keil调试问题解决方案
1. 问题现象与背景分析最近在调试基于MCBx51评估板的8051应用程序时,遇到了一个相当诡异的现象:原本在评估版上运行正常的程序,移植到实际硬件后出现了异常行为,甚至导致调试连接中断。最典型的错误提示就是"CONNECTION TO T…...
Perplexity搜索效率提升73%的6个隐藏技巧:资深AI分析师亲测有效
更多请点击: https://intelliparadigm.com 第一章:Perplexity搜索效率提升73%的底层动因解析 Perplexity 作为新一代语义优先的AI搜索引擎,其搜索延迟中位数从 1.84s 降至 0.50s(实测提升 73%),这一突破并…...
