🐛 Feign日志multipart请求解析非文件字段打印,文件部分用占位符替代

This commit is contained in:
刘泉佳
2026-09-24 22:51:30 +08:00
parent c10a6fc96f
commit 4185372746
@@ -33,8 +33,12 @@ import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 自定义Feign日志记录器,只记录出入参信息
@@ -62,10 +66,13 @@ public class CustomFeignLogger extends Logger {
Map<String, Collection<String>> headers = request.headers();
byte[] body = request.body();
String contentType = getContentType(headers);
String bodyStr;
if (body == null || body.length == 0) {
bodyStr = "[无]";
} else if (isBinaryContent(getContentType(headers))) {
} else if (isMultipartContent(contentType)) {
bodyStr = parseMultipartBody(body, contentType);
} else if (isBinaryContent(contentType)) {
bodyStr = "[二进制数据,长度=" + body.length + "字节]";
} else {
bodyStr = new String(body, StandardCharsets.UTF_8);
@@ -122,7 +129,7 @@ public class CustomFeignLogger extends Logger {
}
/**
* 判断是否为二进制内容或不应打印的内容(如文件上传)
* 判断是否为二进制内容
*/
private boolean isBinaryContent(String contentType) {
if (contentType == null || contentType.isEmpty()) {
@@ -132,7 +139,77 @@ public class CustomFeignLogger extends Logger {
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)
|| lowerContentType.startsWith(MULTIPART_CONTENT_TYPE_PREFIX);
|| lowerContentType.startsWith(VIDEO_CONTENT_TYPE_PREFIX);
}
/**
* 判断是否为multipart内容
*/
private boolean isMultipartContent(String contentType) {
return contentType != null && contentType.toLowerCase().startsWith(MULTIPART_CONTENT_TYPE_PREFIX);
}
/**
* 解析multipart请求体,文件部分用占位符替代,文本字段保留打印
*/
private String parseMultipartBody(byte[] body, String contentType) {
String boundary = extractBoundary(contentType);
if (boundary == null) {
return "[multipart数据,长度=" + body.length + "字节]";
}
String bodyStr = new String(body, StandardCharsets.UTF_8);
String delimiter = "--" + boundary;
String[] parts = bodyStr.split(delimiter);
List<String> fields = new ArrayList<>();
for (String part : parts) {
if (part.isEmpty() || part.startsWith("--") || part.trim().isEmpty()) {
continue;
}
int headerEnd = part.indexOf("\r\n\r\n");
if (headerEnd < 0) {
continue;
}
String headers = part.substring(0, headerEnd);
String value = part.substring(headerEnd + 4);
if (value.endsWith("\r\n")) {
value = value.substring(0, value.length() - 2);
}
String name = extractParam(headers, "name");
if (headers.contains("filename=")) {
String filename = extractParam(headers, "filename");
fields.add(name + "=[文件: " + filename + "]");
} else {
fields.add(name + "=" + value);
}
}
return fields.isEmpty() ? "[multipart数据]" : "{" + String.join(", ", fields) + "}";
}
/**
* 从Content-Type中提取boundary
*/
private String extractBoundary(String contentType) {
for (String param : contentType.split(";")) {
param = param.trim();
if (param.toLowerCase().startsWith("boundary=")) {
return param.substring("boundary=".length()).trim();
}
}
return null;
}
/**
* 从headers中提取参数值(如name、filename)
*/
private String extractParam(String headers, String paramName) {
Pattern pattern = Pattern.compile(paramName + "=\"([^\"]*)\"");
Matcher matcher = pattern.matcher(headers);
if (matcher.find()) {
return matcher.group(1);
}
return paramName;
}
}