首页 > 解决方案 > Feign 客户端请求和响应以及 URL 日志记录

问题描述

我如何记录Feign客户端请求、响应和 URL 的负载。我必须实现拦截器吗?因为我的要求是在数据库的特殊表上记录请求和响应。

标签: loggingrequestinterceptorfeign

解决方案


Feign 具有开箱即用的日志记录机制,可以通过简单的步骤来实现。

如果您使用的是 spring-cloud-starter-feign

FeignSlf4jLogger用于记录。Feign 日志记录文档

根据文档,可以配置以下日志记录级别,

  • NONE- 不记录(默认)。
  • BASIC- 仅记录请求方法和 URL 以及响应状态代码和执行时间。
  • HEADERS- 记录基本信息以及请求和响应标头。
  • FULL- 记录请求和响应的标头、正文和元数据。

注入Logger.Levelbean就足够了。

    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.BASIC;
    }

或者

如果您更喜欢使用配置属性来配置 all @FeignClient,您可以使用默认的 feign 名称创建配置属性。

feign:
  client:
    config:
      default:
        loggerLevel: basic

如果您正在使用'io.github.openfeign:feign-core'

如果您正在构建 Feign 构建器,那么您可以提及logLevel(Level.BASIC)

Feign.builder()
    .logger(new Slf4jLogger())
    .logLevel(Level.BASIC)
    .target(SomeFeignClient.class, url);

我们可以灵活地自定义日志消息

默认的 feign 请求和响应日志

请求记录

响应记录

Logger#logRequest我们可以通过覆盖和Logger#logAndRebufferResponse方法自定义 feign 请求、响应日志记录模式。在以下示例中,我们自定义了请求日志记录模式

log(configKey, "---> %s %s HTTP/1.1 (%s-byte body) ", request.httpMethod().name(), request.url(), bodyLength);

和响应记录模式

log(configKey, "<--- %s %s HTTP/1.1 %s (%sms) ", request.httpMethod().name(), request.url(), status, elapsedTime);

完整的例子是


import feign.Logger;
import feign.Request;
import feign.Response;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;

import static feign.Logger.Level.HEADERS;

@Slf4j
public class CustomFeignRequestLogging extends Logger {

    @Override
    protected void logRequest(String configKey, Level logLevel, Request request) {

        if (logLevel.ordinal() >= HEADERS.ordinal()) {
            super.logRequest(configKey, logLevel, request);
        } else {
            int bodyLength = 0;
            if (request.requestBody().asBytes() != null) {
                bodyLength = request.requestBody().asBytes().length;
            }
            log(configKey, "---> %s %s HTTP/1.1 (%s-byte body) ", request.httpMethod().name(), request.url(), bodyLength);
        }
    }

    @Override
    protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, long elapsedTime)
            throws IOException {
        if (logLevel.ordinal() >= HEADERS.ordinal()) {
            super.logAndRebufferResponse(configKey, logLevel, response, elapsedTime);
        } else {
            int status = response.status();
            Request request = response.request();
            log(configKey, "<--- %s %s HTTP/1.1 %s (%sms) ", request.httpMethod().name(), request.url(), status, elapsedTime);
        }
        return response;
    }


    @Override
    protected void log(String configKey, String format, Object... args) {
        log.debug(format(configKey, format, args));
    }

    protected String format(String configKey, String format, Object... args) {
        return String.format(methodTag(configKey) + format, args);
    }
}

注意: 请求有效负载可以很容易地通过

String bodyText =
              request.charset() != null ? new String(request.body(), request.charset()) : null;

但是在读取输入流之后要小心编写响应有效负载,Util.toByteArray(response.body().asInputStream())然后您必须再次构造响应,例如response.toBuilder().body(bodyData).build(). 否则,你最终会得到期望。原因是响应流被读取并在返回之前总是关闭,这就是为什么该方法被命名为logAndRebufferResponse

如何使用自定义CustomFeignRequestLogging

如果您正在使用仅构建假装客户端'io.github.openfeign:feign-core'

Feign.builder()
     .logger(new CustomFeignRequestLogging())
     .logLevel(feign.Logger.Level.BASIC);

如果您正在使用'org.springframework.cloud:spring-cloud-starter-openfeign'

@Configuration
public class FeignLoggingConfiguration {

    @Bean
    public CustomFeignRequestLogging customFeignRequestLogging() {
        return new CustomFeignRequestLogging();
    }

    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.BASIC;
    }
}


推荐阅读