【OpenFeign使用】Feign Logger 的扩展
背景
我们在日常查看日志中,对于feign调用的日志链路一般实现是两种方式: aop 或者 feign原生的日志能力
本文介绍了feign原生的日志能力
Feign的日志开启
开启feign的日志总共有两步:
-
开启slf4j中debug 的日志级别
-
开启 feign-client 的日志等级
feign: httpclient: enabled: true client: config: default: # 这个key值可以是你指定的feign client名称, 这里配置为default,意思是默认的日志等级策略,相当于全局的策略 logger-level: BASIC # 设置日志等级 logging: level: com.**.client: debug # 该行的key为你要输出的包路径
Feign 四种日志级别的对比
以下级别从低到高输出内容
| 日志等级 | 输出内容 |
|---|---|
| NONE | 什么都不输出 |
| BASIC | 输出请求方法,url 以及响应码 |
| HEADERS | 输出请求头 |
| FULL | 输出请求体以及响应体 |
Feign 原生日志的优缺点
我们可以不修改代码很快的实现了日志的输出,在排查问题的时候可以很快的查看到日志;
但是看到这里的时候,我们可以发现原生日志的缺点:
- 输出内容凌乱,需要我们自己去整合日志内容,才能查看到完整的日志链路
- 输出太多的无用信息,让整个日志被无效信息占满,干扰了我们对日志的定位
对于这种情况我们可以怎么去解决呢?接下来我们可以通过查看Feign 对于整个日志框架的设计进行查看
Feign 对于日志的处理
最主要类:
Logger (feign.Logger) 主要该类是在feign包下面,要区别开log4j和logback 下面的Logger
FeignLoggerFactory: 该类主要控制了Logger的创建,会为每一个feign client 创建出对应的logger
org.springframework.cloud.openfeign.FeignClientFactoryBean#feign 在这个方法中会创建对应的Logger到feign中
我们在阅读源码的时候发现 feign.SynchronousMethodHandler#executeAndDecode 以及feign.AsyncResponseHandler#handleResponse时可以发现,feign原生对于日志的支持能力
我们不难发现,当日志级别不为NONE时,我们可以输出request,以及response, (但是这里有一个弊端,request和response是分开的,打印日志时不能进行汇总在一起进行打印 , 真想打印在一起的话,可以尝试去扩展下 SynchronousMethodHandler 中的方法)
实现我们自己的日志记录
1.1 声明一个logger factory
import feign.Logger;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FeignLoggerFactory;
@Slf4j
public class CustomFeignLoggerFactory implements FeignLoggerFactory {
@Override
public Logger create(Class<?> type) {
log.info("create [{}] logger", type.getSimpleName());
return new FeignLogger(type);
}
}
1.2 声明自己的日志器
import feign.Request;
import feign.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class FeignLogger extends feign.Logger {
private final Logger logger;
public FeignLogger(Class<?> clazz) {
this(LoggerFactory.getLogger(clazz));
}
FeignLogger(Logger logger) {
this.logger = logger;
}
@Override
protected void logRequest(String configKey, Level logLevel, Request request) {
// 我们可以在这里扩展请求相关的日志内容
logger.debug("我是自己在这里实现的请求日志");
switch (logLevel) {
case FULL:
case HEADERS:
case BASIC:
log(configKey, "user custom log content: [%s]", logLevel);
break;
case NONE:
logger.info("none");
break;
default:
super.logRequest(configKey, logLevel, request);
break;
}
}
@Override
protected Response logAndRebufferResponse(String configKey, Level logLevel,
Response response, long elapsedTime) throws IOException {
// 可以在这里输出我们想要的响应内容
logger.debug("我是自己在这里实现的响应日志");
return super.logAndRebufferResponse(configKey, logLevel, response, elapsedTime);
}
@Override
protected void log(String configKey, String format, Object... args) {
if (logger.isDebugEnabled()) {
logger.debug(String.format(methodTag(configKey) + format, args));
}
}
}
1.3 注入我们的日志工厂
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignLoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableFeignClients
public class FeignConfig {
@Bean
public FeignLoggerFactory feignLoggerFactory() {
return new CustomFeignLoggerFactory();
}
}
1.4 测试我们的日志输出
至此,我们就实现了自己的Feign基于原生的日志输出