🐛 Feign日志multipart请求解析非文件字段打印,文件部分用占位符替代
This commit is contained in:
@@ -33,8 +33,12 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义Feign日志记录器,只记录出入参信息
|
* 自定义Feign日志记录器,只记录出入参信息
|
||||||
@@ -62,10 +66,13 @@ public class CustomFeignLogger extends Logger {
|
|||||||
Map<String, Collection<String>> headers = request.headers();
|
Map<String, Collection<String>> headers = request.headers();
|
||||||
byte[] body = request.body();
|
byte[] body = request.body();
|
||||||
|
|
||||||
|
String contentType = getContentType(headers);
|
||||||
String bodyStr;
|
String bodyStr;
|
||||||
if (body == null || body.length == 0) {
|
if (body == null || body.length == 0) {
|
||||||
bodyStr = "[无]";
|
bodyStr = "[无]";
|
||||||
} else if (isBinaryContent(getContentType(headers))) {
|
} else if (isMultipartContent(contentType)) {
|
||||||
|
bodyStr = parseMultipartBody(body, contentType);
|
||||||
|
} else if (isBinaryContent(contentType)) {
|
||||||
bodyStr = "[二进制数据,长度=" + body.length + "字节]";
|
bodyStr = "[二进制数据,长度=" + body.length + "字节]";
|
||||||
} else {
|
} else {
|
||||||
bodyStr = new String(body, StandardCharsets.UTF_8);
|
bodyStr = new String(body, StandardCharsets.UTF_8);
|
||||||
@@ -122,7 +129,7 @@ public class CustomFeignLogger extends Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断是否为二进制内容或不应打印的内容(如文件上传)
|
* 判断是否为二进制内容
|
||||||
*/
|
*/
|
||||||
private boolean isBinaryContent(String contentType) {
|
private boolean isBinaryContent(String contentType) {
|
||||||
if (contentType == null || contentType.isEmpty()) {
|
if (contentType == null || contentType.isEmpty()) {
|
||||||
@@ -132,7 +139,77 @@ public class CustomFeignLogger extends Logger {
|
|||||||
return lowerContentType.startsWith(BINARY_CONTENT_TYPE_PREFIX)
|
return lowerContentType.startsWith(BINARY_CONTENT_TYPE_PREFIX)
|
||||||
|| lowerContentType.startsWith(IMAGE_CONTENT_TYPE_PREFIX)
|
|| lowerContentType.startsWith(IMAGE_CONTENT_TYPE_PREFIX)
|
||||||
|| lowerContentType.startsWith(AUDIO_CONTENT_TYPE_PREFIX)
|
|| lowerContentType.startsWith(AUDIO_CONTENT_TYPE_PREFIX)
|
||||||
|| lowerContentType.startsWith(VIDEO_CONTENT_TYPE_PREFIX)
|
|| lowerContentType.startsWith(VIDEO_CONTENT_TYPE_PREFIX);
|
||||||
|| lowerContentType.startsWith(MULTIPART_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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user