diff --git a/easy-agents-core/src/main/java/com/easyagents/core/model/chat/log/ChatLogSanitizer.java b/easy-agents-core/src/main/java/com/easyagents/core/model/chat/log/ChatLogSanitizer.java new file mode 100644 index 0000000..700ee4e --- /dev/null +++ b/easy-agents-core/src/main/java/com/easyagents/core/model/chat/log/ChatLogSanitizer.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2023-2026, Easy-Agents (fuhai999@gmail.com). + *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ */
+package com.easyagents.core.model.chat.log;
+
+/**
+ * 清理聊天请求日志中的大体积或敏感媒体内容。
+ */
+final class ChatLogSanitizer {
+
+ private static final String DATA_URI_PREFIX = "data:image/";
+ private static final String BASE64_MARKER = ";base64,";
+
+ private ChatLogSanitizer() {
+ }
+
+ /**
+ * 隐去图片 Data URI 的编码正文,仅保留 MIME 和字符长度。
+ *
+ * @param message 原始日志消息
+ * @return 脱敏后的日志消息
+ */
+ static String redactImageDataUris(String message) {
+ if (message == null || message.indexOf(DATA_URI_PREFIX) < 0) {
+ return message;
+ }
+
+ StringBuilder sanitized = new StringBuilder(message.length());
+ int cursor = 0;
+ while (cursor < message.length()) {
+ int dataUriStart = message.indexOf(DATA_URI_PREFIX, cursor);
+ if (dataUriStart < 0) {
+ sanitized.append(message, cursor, message.length());
+ break;
+ }
+ int base64Marker = message.indexOf(BASE64_MARKER, dataUriStart);
+ if (base64Marker < 0) {
+ sanitized.append(message, cursor, message.length());
+ break;
+ }
+ int payloadStart = base64Marker + BASE64_MARKER.length();
+ int payloadEnd = payloadStart;
+ while (payloadEnd < message.length() && isBase64Character(message.charAt(payloadEnd))) {
+ payloadEnd++;
+ }
+
+ sanitized.append(message, cursor, payloadStart);
+ sanitized.append("<已脱敏,编码长度=").append(payloadEnd - payloadStart).append('>');
+ cursor = payloadEnd;
+ }
+ return sanitized.toString();
+ }
+
+ /**
+ * 判断字符是否可能属于 Base64 正文。
+ *
+ * @param value 待判断字符
+ * @return 是否属于 Base64 字符集
+ */
+ private static boolean isBase64Character(char value) {
+ return value >= 'A' && value <= 'Z'
+ || value >= 'a' && value <= 'z'
+ || value >= '0' && value <= '9'
+ || value == '+'
+ || value == '/'
+ || value == '='
+ || value == '-'
+ || value == '_';
+ }
+}
diff --git a/easy-agents-core/src/main/java/com/easyagents/core/model/chat/log/DefaultChatMessageLogger.java b/easy-agents-core/src/main/java/com/easyagents/core/model/chat/log/DefaultChatMessageLogger.java
index 809a75e..0f328e9 100644
--- a/easy-agents-core/src/main/java/com/easyagents/core/model/chat/log/DefaultChatMessageLogger.java
+++ b/easy-agents-core/src/main/java/com/easyagents/core/model/chat/log/DefaultChatMessageLogger.java
@@ -36,7 +36,8 @@ public class DefaultChatMessageLogger implements IChatMessageLogger {
if (shouldLog(config)) {
String provider = getProviderName(config);
String model = getModelName(config);
- logConsumer.accept(String.format("[%s/%s] >>>> request: %s", provider, model, message));
+ logConsumer.accept(String.format("[%s/%s] >>>> request: %s",
+ provider, model, ChatLogSanitizer.redactImageDataUris(message)));
}
}
diff --git a/easy-agents-core/src/test/java/com/easyagents/core/model/chat/log/DefaultChatMessageLoggerTest.java b/easy-agents-core/src/test/java/com/easyagents/core/model/chat/log/DefaultChatMessageLoggerTest.java
new file mode 100644
index 0000000..7e7fd95
--- /dev/null
+++ b/easy-agents-core/src/test/java/com/easyagents/core/model/chat/log/DefaultChatMessageLoggerTest.java
@@ -0,0 +1,32 @@
+package com.easyagents.core.model.chat.log;
+
+import com.easyagents.core.model.chat.ChatConfig;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * 默认聊天日志记录器测试。
+ */
+public class DefaultChatMessageLoggerTest {
+
+ /**
+ * 验证请求日志不会输出完整图片 Base64。
+ */
+ @Test
+ public void shouldRedactImageDataUriPayload() {
+ AtomicReference