fix: - BFI 调用:完整出入参日志

This commit is contained in:
刘泉佳
2026-09-18 10:52:24 +08:00
parent 20fddf2c60
commit c655e9f3b9
2 changed files with 138 additions and 0 deletions
@@ -26,8 +26,13 @@
package org.springblade.common.config;
import feign.Logger;
import lombok.AllArgsConstructor;
import org.springblade.common.feign.CustomFeignLogger;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
/**
* 公共封装包配置类
@@ -38,4 +43,11 @@ import org.springframework.context.annotation.Configuration;
@AllArgsConstructor
public class BladeCommonConfiguration {
@Bean
@Primary
@ConditionalOnClass(name = "feign.Logger")
public Logger feignLogger() {
return new CustomFeignLogger();
}
}
@@ -0,0 +1,126 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.common.feign;
import feign.Logger;
import feign.Request;
import feign.Response;
import feign.Util;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Map;
/**
* 自定义Feign日志记录器,只记录出入参信息
*
* @author Chill
*/
@Slf4j
public class CustomFeignLogger extends Logger {
private static final String BINARY_CONTENT_TYPE_PREFIX = "application/octet-stream";
private static final String IMAGE_CONTENT_TYPE_PREFIX = "image/";
private static final String AUDIO_CONTENT_TYPE_PREFIX = "audio/";
private static final String VIDEO_CONTENT_TYPE_PREFIX = "video/";
@Override
protected void log(String configKey, String format, Object... args) {
// 不处理默认日志格式
}
@Override
protected void logRequest(String configKey, Level logLevel, Request request) {
String url = request.url();
String method = request.httpMethod().name();
Map<String, Collection<String>> headers = request.headers();
byte[] body = request.body();
String bodyStr;
if (body == null || body.length == 0) {
bodyStr = "[无]";
} else if (isBinaryContent(getContentType(headers))) {
bodyStr = "[二进制数据,长度=" + body.length + "字节]";
} else {
bodyStr = new String(body, StandardCharsets.UTF_8);
}
log.info("===> [{}] {} {} | Headers: {} | Body: {}", configKey, method, url, headers, bodyStr);
}
@Override
protected Response logAndRebufferResponse(String configKey, Level logLevel, Response response, long millis) throws IOException {
String url = response.request().url();
int status = response.status();
Map<String, Collection<String>> headers = response.headers();
if (response.body() == null) {
log.info("<==== [{}] {} | Status: {} | Duration: {}ms | Body: [空响应]", configKey, url, status, millis);
return response;
}
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
String bodyStr;
if (bodyData.length == 0) {
bodyStr = "[空响应]";
} else if (isBinaryContent(getContentType(headers))) {
bodyStr = "[二进制数据]";
} else {
bodyStr = new String(bodyData, StandardCharsets.UTF_8);
}
log.info("<==== [{}] {} | Status: {} | Duration: {}ms | Body: {}", configKey, url, status, millis, bodyStr);
return response.toBuilder().body(bodyData).build();
}
/**
* 获取Content-Type
*/
private String getContentType(Map<String, Collection<String>> headers) {
Collection<String> contentTypes = headers.get("Content-Type");
if (contentTypes != null && !contentTypes.isEmpty()) {
return contentTypes.iterator().next();
}
return null;
}
/**
* 判断是否为二进制内容
*/
private boolean isBinaryContent(String contentType) {
if (contentType == null || contentType.isEmpty()) {
return false;
}
String lowerContentType = contentType.toLowerCase();
return lowerContentType.startsWith(BINARY_CONTENT_TYPE_PREFIX)
|| lowerContentType.startsWith(IMAGE_CONTENT_TYPE_PREFIX)
|| lowerContentType.startsWith(AUDIO_CONTENT_TYPE_PREFIX)
|| lowerContentType.startsWith(VIDEO_CONTENT_TYPE_PREFIX);
}
}