《Feign日志记录方案设计》
随着我们内部系统间交互越发紧密,Feign的使用越来越多,但目前存在着Feign方式调用日志无法保存,报错难以排查等问题。
为了解决上述问题,通过对Feign的日志功能进行重写的方式,实现了日志的格式化输出和存储。
Feign默认提供了获取请求和响应报文并打印到控制台的功能,但需要进行开启,且只能打印到控制台中。为了将请求和响应信息进行存储,对Feign的功能进行了重新实现。
通过自定义CustomizationFeignLoggerFactory来重新实现FeignLoggerFactory接口,可以将打印log的对象设置为我们新定义的对象,从而实现调用进行其他操作。
重写create方法,定义logger对象为
通过设置Logger.Level为FULL,开启日志信息的获取和打印
@Configuration
public class CustomizationFeignLoggerFactory implements FeignLoggerFactory {
/**
* 是否开启feign日志记录功能标识
* true-是,false-否
*/
@Value("${feignlog.flag:false}")
private boolean openFlag;
public CustomizationFeignLoggerFactory() {
}
//实现create方法 new自定义的CustomizationFeignLogger
@Override
public Logger create(Class<?> type) {
return openFlag ? new CustomizationFeignLogger() : new Slf4jLogger(type);
}
//开启openfeign的日志,Primary用来解决bean冲突
@Bean
@Primary
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
通过自定义CustomizationFeignLogger来重写feign.Logger对象,来重写实现日志相关操作。
通过重写logRequest()方法,来获取请求信息并将请求信息进行保存。
通过重写logAndRebufferResponse()方法,来获取响应信息并将响应信息进行对应的更新。
@Slf4j
public class CustomizationFeignLogger extends feign.Logger {
/**
* 用来存放logId
*/
private static final ThreadLocal<Long> logIdCache = new ThreadLocal<>();
private static final Long workerId = 1L;
private static final Long dataCenterId = 1L;
private static final String defaultValue = "result data is null";
public CustomizationFeignLogger() {
}
@Override
protected void log(String configKey, String format, Object... args) {
}
@Override
protected void logRequest(String configKey, Level logLevel, Request request) {
String method = request.method();
String url = request.url();
String param = (request.body() == null || request.body().length == 0) ? "" : new String(request.body(), UTF_8);
//通过雪花算法生成主键logId
Snowflake snowflake = new Snowflake(workerId, dataCenterId);
long logId = snowflake.nextId();
logIdCache.set(logId);
//保存请求信息
SpringUtils.getBean(FeignLoggerService.class).saveFeignLog(url, param, logId);
log.debug("Feign请求信息为:method:{}, url:{}, param:{}", method, url, param);
}
//自定义响应日志
@Override
protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, long elapsedTime) throws IOException {
String result = "";
int status = response.status();
try {
if (response.body() != null) {
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
result = decodeOrDefault(bodyData, UTF_8, defaultValue);
return response.toBuilder().body(bodyData).build();
}
return response;
} finally {
//更新响应信息
Long logId = logIdCache.get();
SpringUtils.getBean(FeignLoggerService.class).updateFeignLog(result, elapsedTime, logId);
logIdCache.remove();
log.debug("Feign响应信息为: status:{}, result:{}, ElapsedTime:[{}ms]", status, result, elapsedTime);
}
}
}
选择在dbinc的库中新增t_feign_trans_log表,来记录feign的调用日志。
CREATE TABLE `t_feign_trans_log` (
`log_id` bigint(20) NOT NULL COMMENT '日志ID(对应交易流水号transId)',
`pre_log_id` bigint(20) NOT NULL COMMENT '上一条日志ID',
`trans_id` bigint(200) NOT NULL COMMENT '交易接口ID:对应d_trans表',
`tenant_id` bigint(20) NOT NULL COMMENT '商户ID:对应t_tenant表',
`application_id` bigint(20) NOT NULL COMMENT '应用ID:对应t_application表',
`open_id` varchar(100) DEFAULT '1' COMMENT '请求用户ID:对应cms_user表id',
`client_ip` varchar(255) DEFAULT NULL COMMENT '客户端IP地址',
`req_url` varchar(100) DEFAULT NULL COMMENT '请求地址',
`trans_time` datetime DEFAULT NULL COMMENT '交易时间',
`req_time` datetime DEFAULT NULL COMMENT '请求时间',
`rep_time` datetime DEFAULT NULL COMMENT '响应时间',
`cost` double DEFAULT NULL COMMENT '耗时 单位秒',
`req_msg` mediumtext COMMENT '请求报文',
`rep_msg` mediumtext COMMENT '响应报文',
`state` char(30) DEFAULT NULL COMMENT '交易状态 10-成功 12-异常',
`trans_state` char(30) DEFAULT NULL COMMENT '交易状态 10-成功 12-异常',
`busi_state` char(30) DEFAULT NULL COMMENT '业务状态 10-成功 11 校验失败',
`error_detail` mediumtext COMMENT '错误信息',
`CREATE_ID` varchar(60) DEFAULT NULL COMMENT '创建人',
`MODIFY_ID` varchar(60) DEFAULT NULL COMMENT '修改人',
`INSERT_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '插入时间',
`UPDATE_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`log_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='feign日志记录表';

添加以下依赖:
<!--引入feignlog依赖-->
<dependency>
<groupId>com.micro</groupId>
<artifactId>micro-feignlog</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
# feign日志记录开启标识,true-开启,false-关闭
feignlog:
flag: true
如果添加修改yml信息或配置为false,则不会记录feign调用日志