当前位置: 首页 > news >正文

【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));}

在这里插入图片描述

http://www.lryc.cn/news/62999.html

相关文章:

  • MongoDB (零) 安装和简单使用
  • Java中的异常是什么?
  • 微短剧“小阳春”,“爱优腾芒”抢滩登陆?
  • C++菱形继承(再剖析)
  • java获取星期几
  • 【TypeScript】03-TypeScript基本类型
  • 什么是跨域?
  • Gradle理论与实践—Gradle构建脚本基础
  • 【Vue 基础】vue-cli初始化项目及相关说明
  • 【c语言】详解c语言#预处理期过程 | 宏定义前言
  • 内网远程控制软件哪个好用
  • 【计算机基本原理-数据结构】数据结构中树的详解
  • 数字设计小思 - D触发器与死缠烂打的亚稳态
  • Notes/Domino 11.0.1FP7以及在NAS上安装Domino等
  • 【VM服务管家】VM4.x算子SDK开发_3.3 模块工具类
  • Aspose.Pdf使用教程:在PDF文件中添加水印
  • H.264/AVC加密----选择加密
  • WuThreat身份安全云-TVD每日漏洞情报-2023-04-26
  • 剑指 Offer第二版:1~n 整数中 1 出现的次数、51. 数组中的逆序对、56 - II. 数组中数字出现的次数 II
  • 云原生-k8s核心概念(pod,deploy,service,ingress,configmap,volume)
  • 他工作10年,老板却让他走人
  • vpp怎么写node
  • 【4. ROS的主要通讯方式:Topic话题与Message消息】
  • 【react全家桶学习】react中组件定义及state属性(超详/必看)
  • 如何以产品经理思维打造一所高品质学校?
  • 根治Spring中使用Mongo时报错InvalidMongoDbApiUsageException
  • 【计算机组成原理】数据的表示和运算·进位计数制
  • C++ Primer第五版_第十四章习题答案(21~30)
  • 服务器性能调优
  • 带你深入学习k8s--(三) pod 管理