自定义Feign日志打印

发布时间 2023-09-19 17:09:18作者: 品书读茶

说明

想要自定义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 com.alibaba.fastjson.JSON;
import feign.Logger;
import feign.Request;
import feign.Response;
import feign.Util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class LogFeignConfig 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) {
        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", JSON.toJSONString(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;
    }

}

全局生效

在自定义的打印feign日志类上打上@Component

局部feign接口生效

@FeignClient(configuration = {LogFeignConfig2.class})