伪装客户端请求和响应以及URL记录

时间:2019-04-11 04:25:25

标签: logging request interceptor feign

如何记录伪装客户端请求,响应和URL的有效负载。我必须实现拦截器吗?

4 个答案:

答案 0 :(得分:3)

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

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

使用Slf4jLogger伪装日志。Feign logging documentation

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

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

注入Logger.Level bean就足够了。

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

OR

如果您希望使用配置属性来配置所有@FeignClient,则可以使用默认伪名称创建配置属性。

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);

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

默认的伪装请求和响应日志记录

Request logging

Resopnse logging

我们可以通过覆盖Logger#logRequestLogger#logAndRebufferResponse方法来自定义伪装请求,响应日志记录模式。在以下示例中,我们定制了请求记录模式

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

的原因

答案 1 :(得分:0)

没有Feign客户响应的拦截器。请求拦截器仅适用于Feign客户端。

最好的解决方案是使用RestTemplate而不是Feign:

export default (props) => (
  <TouchableOpacity {...props} style={styles.button}>
    {this.renderChildren()}
  </TouchableOpacity>
);

renderChildren() {
    return this.props.children.map(children => {
        return React.cloneElement(children, {style: { color: 'grey', fontFamily: 'cochin' }});
    })
}

然后@Autowire用作您要在其中使用的 restTemplate

@Configuration
public class RestConfiguration {
    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate
                = new RestTemplate(
                new BufferingClientHttpRequestFactory(
                        new SimpleClientHttpRequestFactory()
                )
        );

        List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
        if (CollectionUtils.isEmpty(interceptors)) {
            interceptors = new ArrayList<>();
        }
        interceptors.add(new UserRestTemplateClientInterceptor());
        restTemplate.setInterceptors(interceptors);
        return restTemplate;
    }

}

答案 2 :(得分:0)

Feign提供一个Logger界面,可以记录完整的请求和响应。您需要在Feign Builder或配置中设置Logger.Level

Feign.builder()
   .logLevel(Logger.Level.FULL) // this will log the request and response
   .target(MyApi, "my host");

答案 3 :(得分:0)

在RestConfiguration中,您需要提高日志记录feignClient的默认级别,并通过@Bean feignLogger进行覆盖,例如:

@Configuration(proxyBeanMethods = false)
@EnableCircuitBreaker
@EnableFeignClients(basePackageClasses = [Application::class])
class RestConfiguration: WebMvcConfigurer {

    @Bean
    fun feignLoggerLevel(): Logger.Level {
        return Logger.Level.FULL
    }

    @Bean
    fun feignLogger(): Logger {
        return FeignClientLogger()
    }
}

并根据需要实施记录器。例如,以日志格式登录:

import feign.Logger
import feign.Request
import feign.Response
import feign.Util.*
import org.slf4j.LoggerFactory

class FeignClientLogger : Logger() {
    private val log = LoggerFactory.getLogger(this::class.java)

    override fun logRequest(configKey: String?, logLevel: Level?, request: Request?) {
        if (request == null)
            return

        val feignRequest = FeignRequest()
        feignRequest.method = request.httpMethod().name
        feignRequest.url = request.url()
        for (field in request.headers().keys) {
            for (value in valuesOrEmpty(request.headers(), field)) {
                feignRequest.addHeader(field, value)
            }
        }

        if (request.requestBody() != null) {
            feignRequest.body = request.requestBody().asString()
        }

        log.trace(feignRequest.toString())
    }

    override fun logAndRebufferResponse(
        configKey: String?,
        logLevel: Level?,
        response: Response?,
        elapsedTime: Long
    ): Response? {
        if (response == null)
            return response

        val feignResponse = FeignResponse()
        val status = response.status()
        feignResponse.status = response.status()
        feignResponse.reason =
            (if (response.reason() != null && logLevel!! > Level.NONE) " " + response.reason() else "")
        feignResponse.duration = elapsedTime

        if (logLevel!!.ordinal >= Level.HEADERS.ordinal) {
            for (field in response.headers().keys) {
                for (value in valuesOrEmpty(response.headers(), field)) {
                    feignResponse.addHeader(field, value)
                }
            }

            if (response.body() != null && !(status == 204 || status == 205)) {
                val bodyData: ByteArray = toByteArray(response.body().asInputStream())
                if (logLevel.ordinal >= Level.FULL.ordinal && bodyData.isNotEmpty()) {
                    feignResponse.body = decodeOrDefault(bodyData, UTF_8, "Binary data")
                }
                log.trace(feignResponse.toString())

                return response.toBuilder().body(bodyData).build()
            } else {
                log.trace(feignResponse.toString())
            }
        }
        return response
    }

    override fun log(p0: String?, p1: String?, vararg p2: Any?) {}
}

class FeignResponse {
    var status = 0
    var reason: String? = null
    var duration: Long = 0
    private val headers: MutableList<String> = mutableListOf()
    var body: String? = null

    fun addHeader(key: String?, value: String?) {
        headers.add("$key: $value")
    }

    override fun toString() =
        """{"type":"response","status":"$status","duration":"$duration","headers":$headers,"body":$body,"reason":"$reason"}"""
}

class FeignRequest {
    var method: String? = null
    var url: String? = null
    private val headers: MutableList<String> = mutableListOf()
    var body: String? = null

    fun addHeader(key: String?, value: String?) {
        headers.add("$key: $value")
    }

    override fun toString() =
        """{"type":"request","method":"$method","url":"$url","headers":$headers,"body":$body}"""
}