全局记录Feign的请求和响应日志方式

网友投稿 1499 2022-09-28

全局记录Feign的请求和响应日志方式

目录1、项目里定义FeignClient接口2、单个FeignClient接口开启日志3、所有FeignClient接口 开启日志3.1、修改FeignConfiguration3.2、还是修改 application.yml 配置3.3、OK了,此时项目里4、重写FeignClient输出日志5、使用Aspect切面输出日志

项目里使用了Feign进行远程调用,有时为了问题排查,需要开启请求和响应日志

下面简介一下如何开启Feign日志:

注:本文基于

spring-boot-starter-parent 2.3.4.RELEASEspring-cloud-starter-openfeign 2.2.3.RELEASE

1、项目里定义FeignClient接口

package com.example.demo.feign;

import org.springframework.cloud.openfeign.FeignClient;

import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(name = "deom", url = "https://baidu.com")

public interface FeignDemo {

@GetMapping("/")

String test();

}

2、单个FeignClient接口开启日志

在 application.yml 里指定Feign接口日志级别为DEBUG,类型为FULL:

注:com.example.demo.feign.FeignDemo就是上面定义的FeignClient接口

logging:

level:

com.example.demo.feign.FeignDemo: debug

# 下面的配置,也可以写代码代替

# @Bean

# public Logger.Level level() { return Logger.Level.FULL; }

feign:

client:

config:

default:

loggerLevel: full

OK了,重启项目,调用 FeignDemo.test() 方法后,会输出如下日志:

2020-10-13 11:46:24.161 DEBUG 20824 --- [nio-8080-exec-4] com.example.demo.feign.FeignDemo         : [FeignDemo#test] ---> GET https://baidu.com HTTP/1.12020-10-13 11:46:24.162 DEBUG 20824 --- [nio-8080-exec-4] com.example.demo.feign.FeignDemo         : [FeignDemo#test] ---> END HTTP (0-byte body)2020-10-13 11:46:24.255 DEBUG 20824 --- [nio-8080-exec-4] com.example.demo.feign.FeignDemo         : [FeignDemo#test] <--- HTTP/1.1 200 OK (93ms)2020-10-13 11:46:24.255 DEBUG 20824 --- [nio-8080-exec-4] com.example.demo.feign.FeignDemo         : [FeignDemo#test] content-length: 24432020-10-13 11:46:24.255 DEBUG 20824 --- [nio-8080-exec-4] com.example.demo.feign.FeignDemo         : [FeignDemo#test] content-type: text/html2020-10-13 11:46:24.256 DEBUG 20824 --- [nio-8080-exec-4] com.example.demo.feign.FeignDemo         : [FeignDemo#test] date: Tue, 13 Oct 2020 03:46:24 GMT2020-10-13 11:46:24.256 DEBUG 20824 --- [nio-8080-exec-4] com.example.demo.feign.FeignDemo         : [FeignDemo#test] server: bfe2020-10-13 11:46:24.256 DEBUG 20824 --- [nio-8080-exec-4] com.example.demo.feign.FeignDemo         : [FeignDemo#test] 2020-10-13 11:46:24.257 DEBUG 20824 --- [nio-8080-exec-4] com.example.demo.feign.FeignDemo         : [FeignDemo#test]

关于百度 About Baidu

©2017 Baidu 使用百度前必读  意见反馈 京ICP证030173号 

3、所有FeignClient接口 开启日志

上面的方法,只能开启单个FeignClient接口,如果项目里有10个接口,那么要在yml里配置10项,而且以后添加新的FeignClient,还要记得去修改yml配置,太麻烦。

所以,下面是开启所有FeignClient接口日志的配置:

3.1、修改FeignConfiguration

自定义feign.Logger,如下:

package com.example.demo.cacheDemo;

import feign.slf4j.Slf4jLogger;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

public class FeignConfiguration {

@Bean

public feign.Logger logger() {

return new Slf4jLogger();

}

}

3.2、还是修改 application.yml 配置

logging:

level:

# 删除具体的FeignClient接口配置,只保留这一个就好了

feign.Logger: debug

# 也可以写代码代替

# @Bean

# public Logger.Level level() { return Logger.Level.FULL; }

feign:

client:

config:

default:

loggerLevel: full

3.3、OK了,此时项目里

不管新增多少个 FeignClient,都会输出日志。

4、重写FeignClient输出日志

根据上面输出的日志,可以看到是多条INFO日志,在并发时,很有可能会互相干扰,而且格式也无法调整。

我们知道,Feign默认情况下,是使用 feign.Client.Default 发起http请求;

我们可以重写Client,并注入Bean来替换掉 feign.Client.Default,从而实现日志记录,当然也可以做其它任意事情了,比如添加Header。下面是注入Bean的代码:

// 默认不注入,如果yml配置里有 logging.level.beinet-.demostudy.MyClient 才注入

@Bean

@ConditionalOnProperty("logging.level.beinet-.demostudy.MyClient")

MyClient getClient() throws NoSuchAlgorithmException, KeyManagementException {

// 忽略SSL校验

SSLContext ctx = SSLContext.getInstance("SSL");

X509TrustManager tm = new X509TrustManager() {

@Override

public void checkClientTrusted(X509Certificate[] chain, String authType) {

}

@Override

public void checkServerTrusted(X509Certificate[] chain, String authType) {

}

@Override

public X509Certificate[] getAcceptedIssuers() {

return null;

}

};

ctx.init(null, new TrustManager[]{tm}, null);

return new MyClient(ctx.getSocketFactory(), (hostname, sslSession) -> true);

}

下面是重写的Client完整代码:

package beinet-.demostudy;

import feign.Client;

import feign.Request;

import feign.Response;

import lombok.extern.slf4j.Slf4j;

import org.springframework.util.StreamUtils;

import javax-.ssl.HostnameVerifier;

import javax-.ssl.SSLSocketFactory;

import java.io.*;

import java.util.Collection;

import java.util.Map;

@Slf4j

public class MyClient extends Client.Default {

public MyClient(SSLSocketFactory socketFactory, HostnameVerifier hostnameVerifier) {

super(socketFactory, hostnameVerifier);

}

@Override

public Response execute(Request request, Request.Options options) throws IOException {

StringBuilder sb = new StringBuilder("[log started]\r\n");

sb.append(request.httpMethod()).append(" ").append(request.url()).append("\r\n");

CombineHeaders(sb, request.headers()); // 请求Header

CombineBody(sb, request.body());

long costTime = -1;

Exception exception = null;

BufferingFeignClientResponse response = null;

long begin = System.currentTimeMillis();

try {

response = new BufferingFeignClientResponse(super.execute(request, options));

costTime = (System.currentTimeMillis() - begin);

} catch (Exception exp) {

costTime = (System.currentTimeMillis() - begin);

exception = exp;

throw exp;

} finally {

sb.append("\r\nResponse cost time(ms): ").append(String.valueOf(costTime));

if (response != null)

sb.append(" status: ").append(response.status());

sb.append("\r\n");

if (response != null) {

CombineHeaders(sb, response.headers()); // 响应Header

sb.append("Body:\r\n").append(response.body()).append("\r\n");

}

if (exception != null) {

sb.append("Exception:\r\n ").append(exception.getMessage()).append("\r\n");

}

sb.append("\r\n[log ended]");

log.debug(sb.toString());

}

Response ret = response.getResponse().toBuilder()

.body(response.getBody(),

response.getResponse().body().length()).build();

response.close();

return ret;

}

private static void CombineHeaders(StringBuilder sb, Map> headers) {

if (headers != null && !headers.isEmpty()) {

sb.append("Headers:\r\n");

for (Map.Entry> ob : headers.entrySet()) {

for (String val : ob.getValue()) {

sb.append(" ").append(ob.getKey()).append(": ").append(val).append("\r\n");

}

}

}

}

private static void CombineBody(StringBuilder sb, byte[] body) {

if (body == null || body.length <= 0)

return;

sb.append("Body:\r\n").append(new String(body)).append("\r\n");

}

static final class BufferingFeignClientResponse implements Closeable {

private Response response;

private byte[] body;

private BufferingFeignClientResponse(Response response) {

this.response = response;

}

private Response getResponse() {

return this.response;

}

private int status() {

return this.response.status();

}

private Map> headers() {

return this.response.headers();

}

private String body() throws IOException {

StringBuilder sb = new StringBuilder();

try (InputStreamReader reader = new InputStreamReader(getBody())) {

char[] tmp = new char[1024];

int len;

while ((len = reader.read(tmp, 0, tmp.length)) != -1) {

sb.append(new String(tmp, 0, len));

}

}

return sb.toString();

}

private InputStream getBody() throws IOException {

if (this.body == null) {

this.body = StreamUtils.copyToByteArray(this.response.body().asInputStream());

}

return new ByteArrayInputStream(this.body);

}

@Override

public void close() {

this.response.close();

}

}

}

输出日志示例:

2020-10-15 16:48:26.081 DEBUG 15664 --- [           main] beinet-.demostudy.MyClient             : [log started]POST https://baidu.com?flg=3Headers:  Content-Length: 14  Content-Type: text/plain;charset=UTF-8Body:abcde我是ddd

Response cost time(ms): 207  status: 200Headers:  content-length: 2443  content-type: text/html  date: Thu, 15 Oct 202DzEvA0 08:48:27 GMT  server: bfeBody: 百度的html

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:「首席架构师推荐」精选企业门户系统列表
下一篇:【SemiDrive源码分析】【X9芯片启动流程】30 - AP1 Android Kernel 启动流程 start_kernel 函数详细分析
相关文章

 发表评论

暂时没有评论,来抢沙发吧~