From c655e9f3b98d5dc6ea695ab2940a38a3c8eca5f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com> Date: Fri, 18 Sep 2026 10:52:24 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20-=20BFI=20=E8=B0=83=E7=94=A8=EF=BC=9A?= =?UTF-8?q?=E5=AE=8C=E6=95=B4=E5=87=BA=E5=85=A5=E5=8F=82=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/BladeCommonConfiguration.java | 12 ++ .../common/feign/CustomFeignLogger.java | 126 ++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 blade-common/src/main/java/org/springblade/common/feign/CustomFeignLogger.java diff --git a/blade-common/src/main/java/org/springblade/common/config/BladeCommonConfiguration.java b/blade-common/src/main/java/org/springblade/common/config/BladeCommonConfiguration.java index d3a2164d..7e8c2bf7 100644 --- a/blade-common/src/main/java/org/springblade/common/config/BladeCommonConfiguration.java +++ b/blade-common/src/main/java/org/springblade/common/config/BladeCommonConfiguration.java @@ -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(); + } + } diff --git a/blade-common/src/main/java/org/springblade/common/feign/CustomFeignLogger.java b/blade-common/src/main/java/org/springblade/common/feign/CustomFeignLogger.java new file mode 100644 index 00000000..f22a743d --- /dev/null +++ b/blade-common/src/main/java/org/springblade/common/feign/CustomFeignLogger.java @@ -0,0 +1,126 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * 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> 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> 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> headers) { + Collection 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); + } +}