自定义Feign日志打印
说明
想要自定义feign日志打印只需要继承feign.Logger抽象类,在自定义类中一般会重写三个方法
- logRequest
这个方法入参分别是String configKey, Logger.Level logLevel, Request request
- configKey: feign调用的所在接口名+方法名
- logLevel: 日志层级NONE,BASIC,HEADERS,FULL
- request: 请求信息
- logAndRebufferResponse
这个方法入参分别是String configKey, Logger.Level logLevel, Response response, long elapsedTime
。出参Response
- configKey: feign调用的所在接口名+方法名
- logLevel: 日志层级NONE,BASIC,HEADERS,FULL
- response: 接口返回信息
- elapsedTime: 调用接口耗时,单位ms
- logIOException
这个方法入参分别是String configKey, Logger.Level logLevel, IOException ioe, long elapsedTime
。出参IOException
- configKey: feign调用的所在接口名+方法名
- logLevel: 日志层级NONE,BASIC,HEADERS,FULL
- IOException: 接口调用返回异常信息
- elapsedTime: 调用接口耗时,单位ms
自定义feign日志代码
import feign.Logger;
import feign.Request;
import feign.Response;
import feign.Util;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class FeignLogger extends Logger {
@Override
protected void log(String s, String s1, Object... objects) {
log.info(String.format(methodTag(s) + s1, objects));
}
protected void logRequest(String configKey, Logger.Level logLevel, Request request) {
// log.info("局部GlobalLogFeignLogger");
this.log(configKey, "---> %s %s HTTP/1.1", request.httpMethod().name(), request.url());
if (logLevel.ordinal() >= Logger.Level.BASIC.ordinal()) {
String bodyText;
if (logLevel.ordinal() >= Level.HEADERS.ordinal()) {
Map<String, Object> headMap = new HashMap<>();
for (String s : request.headers().keySet()) {
bodyText = s;
for (String value : Util.valuesOrEmpty(request.headers(), bodyText)) {
headMap.put(bodyText, value);
}
}
this.log(configKey, "---> 头信息 %s", headMap);
}
int bodyLength = 0;
if (request.body() != null) {
bodyLength = request.length();
if (logLevel.ordinal() >= Logger.Level.BASIC.ordinal()) {
bodyText = request.charset() != null ? new String(request.body(), request.charset()) : null;
this.log(configKey, "入参---> %s", bodyText != null ? bodyText : "Binary data");
}
}
if (logLevel.ordinal() >= Level.FULL.ordinal()) {
this.log(configKey, "---> END HTTP (%s-byte body)", bodyLength);
}
}
}
protected Response logAndRebufferResponse(String configKey, Logger.Level logLevel, Response response, long elapsedTime) throws IOException {
int status = response.status();
if (logLevel.ordinal() >= Logger.Level.BASIC.ordinal()) {
if (logLevel.ordinal() >= Level.FULL.ordinal()) {
for (String field : response.headers().keySet()) {
for (String value : Util.valuesOrEmpty(response.headers(), field)) {
this.log(configKey, "<--- %s: %s", field, value);
}
}
}
int bodyLength = 0;
if (response.body() != null && status != 204 && status != 205) {
if (logLevel.ordinal() >= Logger.Level.FULL.ordinal()) {
this.log(configKey, "<---");
}
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
bodyLength = bodyData.length;
if (logLevel.ordinal() >= Level.BASIC.ordinal() && bodyLength > 0) {
this.log(configKey, "出参<--- %s", Util.decodeOrDefault(bodyData, Util.UTF_8, "Binary data"));
}
if (logLevel.ordinal() >= Level.FULL.ordinal()) {
this.log(configKey, "<--- END HTTP (%s-byte body)", bodyLength);
}
this.log(configKey, "<--- 接口耗时 -- %sms", elapsedTime);
return response.toBuilder().body(bodyData).build();
}
if (logLevel.ordinal() >= Level.FULL.ordinal()) {
this.log(configKey, "<--- END HTTP (%s-byte body)", bodyLength);
}
}
this.log(configKey, "<--- 接口耗时--%sms", elapsedTime);
return response;
}
protected IOException logIOException(String configKey, Logger.Level logLevel, IOException ioe, long elapsedTime) {
this.log(configKey, "<--- ERROR %s: %s (%sms)", ioe.getClass().getSimpleName(), ioe.getMessage(), elapsedTime);
log.error("<---", ioe);
if (logLevel.ordinal() >= Level.BASIC.ordinal()) {
StringWriter sw = new StringWriter();
ioe.printStackTrace(new PrintWriter(sw));
this.log(configKey, "%s", sw.toString());
}
return ioe;
}
}
局部生效
说明:只针对一个feignClient 接口生效,使用@FeignClient中的configuration指定即可
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.test.boot.param.FeignResultDto;
import com.test.boot.param.PageQueryDto;
import com.test.boot.param.TransferImportListInDto;
import com.test.boot.param.TransferImportListOutDto;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(name = "stockFeign", url = "http://stock-center.net", configuration = {FeignLogger.class})
public interface StockFeign {
@PostMapping("/api/v1/transferorder/queryBatchImportTransferOrder")
FeignResultDto<Page<TransferImportListOutDto>> queryBatchTransferOrder(@RequestBody PageQueryDto<TransferImportListInDto> pageQueryDto);
}
全局生效
说明:@FeignClient中的configuration无需指定,加个配置类即可
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CustomDecoderConfiguration {
@Bean
public Logger feignLogger() {
return new FeignLogger();
}
}
当局部和全局类同时存在的时候,局部的生效
纸上得来终觉浅,绝知此事要躬行。