1,项目启动时打印项目中使用feignclient的name及url相关信息
2,在调用feignclient方法时,打印request, response信息,并有开关来控制此项功能,因为并不是所有feignclient都需要打印request, response,所以颗粒度需要细化到具体的feignclient
方案1: 此种方式只能打印项目中注入的feignclient信息
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.openfeign.FeignClientSpecification;
import org.springframework.stereotype.Component;
import java.lang.reflect.Proxy;
import java.util.Set;
@Component
@Slf4j
public class FeignClientInfoPrinter implements BeanPostProcessor {
@Autowired
protected Set<FeignClientSpecification> feignClientSpecificationList;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Proxy && feignClientSpecificationList.stream().anyMatch(x -> x.getClassName().equals(beanName))){
log.info("Information about feign client: {}", bean);
}
return bean;
}
}
// 打印示例如下:
// Information about feign client: HardCodedTarget(type=XXServiceClient, name=XXClient, url=https://xxxx/)
方案2: 此种方式可以打印项目中所有的feignclient信息
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.openfeign.FeignClientFactoryBean;
import javax.annotation.PostConstruct;
import java.util.Set;
@Component
@Slf4j
public class FeignClientInfoPrinter {
@Autowired
protected Set<FeignClientFactoryBean> feignClientFactoryBeans;
@PostConstruct
public void init() {
feignClientFactoryBeans.forEach(x -> log.info("Information about feign client: {}, {}, {}", x.getObjectType(), x.getName(), x.getUrl()));
}
}
首先我们需要配置两个开关,
feign.third-party-logger:true #此开关开启代表允许此功能启用 feign.third-party-name:XXServiceClient, OOServiceClient... #此开关用来定义哪些feignclient类允许调用方法时打印请求响应体
代码如下:
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfiguration {
@Bean
@ConditionalOnProperty(value = "feign.third-party-logger", havingValue = "true")
FeignThirdPartyLogger createMyLogger() {
return new FeignThirdPartyLogger();
}
}
import feign.Logger;
import feign.Request;
import feign.Response;
import feign.Util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static feign.Util.decodeOrDefault;
import static feign.form.util.CharsetUtil.UTF_8;
@Slf4j
public class FeignThirdPartyLogger extends Logger {
@Value("#{'${feign.third-party-name}'.split(',')}")
List<String> feignThirdName;
@Override
protected void logRequest(String configKey, Level logLevel, Request request) {
if(!CollectionUtils.isEmpty(feignThirdName) && feignThirdName.contains(configKey.split("#")[0])){
byte[] arrBody = request.body();
String body = arrBody == null ? "" : new String(arrBody);
log.info("[feign log request started]\n{} Request URL: {}\nRequest Body:\n{}",
request.httpMethod(),
request.url(),
body);
}
}
@Override
protected Response logAndRebufferResponse(String configKey,
Level logLevel,
Response response,
long elapsedTime) {
if(!CollectionUtils.isEmpty(feignThirdName) && feignThirdName.contains(configKey.split("#")[0])){
int status = response.status();
String content = "";
if (response.body() != null && !(status == 204 || status == 205)) {
byte[] bodyData;
try {
bodyData = Util.toByteArray(response.body().asInputStream());
} catch (IOException e) {
throw new RuntimeException(e);
}
if (bodyData.length > 0) {
content = decodeOrDefault(bodyData, UTF_8, "Binary data");
}
response = response.toBuilder().body(bodyData).build();
}
log.info("[feign log request ended]\ncost time(ms): {} status:{} from {} {}\nResponse Body:\n{}",
elapsedTime,
status,
response.request().httpMethod(),
response.request().url(),
content);
return response;
}else{
return response;
}
}
@Override
protected void log(String configKey, String format, Object... args) {
}
// 该方法可以打印header中的信息
private static String CombineHeaders(Map<String, Collection<String>> headers) {
StringBuilder sb = new StringBuilder();
if (headers != null && !headers.isEmpty()) {
sb.append("Headers:\r\n");
for (Map.Entry<String, Collection<String>> ob : headers.entrySet()) {
for (String val : ob.getValue()) {
sb.append(" ").append(ob.getKey()).append(": ").append(val).append("\r\n");
}
}
}
return sb.toString();
}
}