task-19: Coze 请求/响应与 Python 回传日志改采样、截断、DEBUG
- 新增 SimilarAsinLogSupport 纯函数:truncate(2000 前缀+长度后缀,代理对安全) 与 shouldLog(每 N 次采样,计数 0 恒采样) - Coze 请求 body 与 submit 响应正文日志降 DEBUG + 截断; history 轮询响应降 DEBUG + 每 20 次记一次完整正文,其余只记状态 - Python 回传逐行日志降 DEBUG + 每 20 行采样一行,大任务日志量降到 5%
This commit is contained in:
+13
-6
@@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.nanri.aiimage.config.SimilarAsinProperties;
|
||||
import com.nanri.aiimage.modules.coze.service.CozeCredentialPoolService;
|
||||
import com.nanri.aiimage.modules.similarasin.model.dto.SimilarAsinResultRowDto;
|
||||
import com.nanri.aiimage.modules.similarasin.util.SimilarAsinLogSupport;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -30,11 +31,14 @@ public class SimilarAsinCozeClient {
|
||||
|
||||
private static final String MODULE_TYPE = "SIMILAR_ASIN";
|
||||
private static final MediaType APPLICATION_JSON_UTF8 = new MediaType("application", "json", StandardCharsets.UTF_8);
|
||||
/** Task 19:history 轮询响应正文采样频率(每 N 次记一次完整正文,其余只记状态)。 */
|
||||
static final long HISTORY_RESPONSE_LOG_EVERY_N = 20L;
|
||||
|
||||
private final SimilarAsinProperties properties;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final CozeCredentialPoolService cozeCredentialPoolService;
|
||||
private final AtomicLong credentialCursor = new AtomicLong();
|
||||
private final AtomicLong historyResponseLogCounter = new AtomicLong();
|
||||
/**
|
||||
* P1-7:单例 RestClient。原 restClient() 每次提交/poll 都新建 SimpleClientHttpRequestFactory + RestClient,
|
||||
* 几千行任务并发时会反复创建短命对象造成不必要 GC 压力。RestClient 与 SimpleClientHttpRequestFactory
|
||||
@@ -267,10 +271,10 @@ public class SimilarAsinCozeClient {
|
||||
body.put("workflow_id", credential.workflowId());
|
||||
body.put("parameters", parameters);
|
||||
body.put("is_async", Boolean.TRUE);
|
||||
log.info("[similar-asin] coze request credential={} url={} body={}",
|
||||
log.debug("[similar-asin] coze request credential={} url={} body={}",
|
||||
credential.name(),
|
||||
joinUrl(properties.getCozeBaseUrl(), properties.getCozeWorkflowPath()),
|
||||
writeJson(maskCozeRequestBody(body)));
|
||||
SimilarAsinLogSupport.truncate(writeJson(maskCozeRequestBody(body))));
|
||||
|
||||
RestClient.RequestBodySpec request = restClient().post()
|
||||
.uri(joinUrl(properties.getCozeBaseUrl(), properties.getCozeWorkflowPath()))
|
||||
@@ -283,9 +287,9 @@ public class SimilarAsinCozeClient {
|
||||
return request.exchange((clientRequest, clientResponse) -> {
|
||||
byte[] responseBytes = StreamUtils.copyToByteArray(clientResponse.getBody());
|
||||
String responseText = responseBytes.length == 0 ? "" : new String(responseBytes, StandardCharsets.UTF_8);
|
||||
log.info("[similar-asin] coze submit response status={} body={}",
|
||||
log.debug("[similar-asin] coze submit response status={} body={}",
|
||||
clientResponse.getStatusCode(),
|
||||
responseText);
|
||||
SimilarAsinLogSupport.truncate(responseText));
|
||||
return responseText;
|
||||
});
|
||||
}
|
||||
@@ -294,6 +298,7 @@ public class SimilarAsinCozeClient {
|
||||
String path = properties.getCozeWorkflowHistoryPath()
|
||||
.replace("{workflow_id}", credential.workflowId())
|
||||
.replace("{execute_id}", executeId);
|
||||
long historyLogCounter = historyResponseLogCounter.getAndIncrement();
|
||||
return restClient().get()
|
||||
.uri(joinUrl(properties.getCozeBaseUrl(), path))
|
||||
.headers(headers -> {
|
||||
@@ -304,10 +309,12 @@ public class SimilarAsinCozeClient {
|
||||
.exchange((clientRequest, clientResponse) -> {
|
||||
byte[] responseBytes = StreamUtils.copyToByteArray(clientResponse.getBody());
|
||||
String responseText = responseBytes.length == 0 ? "" : new String(responseBytes, StandardCharsets.UTF_8);
|
||||
log.info("[similar-asin] coze history response credential={} executeId={} status={} body={}",
|
||||
log.debug("[similar-asin] coze history response credential={} executeId={} status={} body={}",
|
||||
credential.name(), executeId,
|
||||
clientResponse.getStatusCode(),
|
||||
responseText);
|
||||
SimilarAsinLogSupport.shouldLog(historyLogCounter, HISTORY_RESPONSE_LOG_EVERY_N)
|
||||
? SimilarAsinLogSupport.truncate(responseText)
|
||||
: "[sampled out]");
|
||||
return responseText;
|
||||
});
|
||||
}
|
||||
|
||||
+9
-2
@@ -40,6 +40,7 @@ import com.nanri.aiimage.modules.similarasin.model.vo.SimilarAsinTaskItemVo;
|
||||
import com.nanri.aiimage.modules.similarasin.util.BoundedImageCache;
|
||||
import com.nanri.aiimage.modules.similarasin.util.ExcelCellImageWriter;
|
||||
import com.nanri.aiimage.modules.similarasin.util.SimilarAsinImageEmbedder;
|
||||
import com.nanri.aiimage.modules.similarasin.util.SimilarAsinLogSupport;
|
||||
import com.nanri.aiimage.modules.file.service.LocalFileStorageService;
|
||||
import com.nanri.aiimage.modules.file.service.oss.OssStorageService;
|
||||
import com.nanri.aiimage.modules.task.mapper.FileResultMapper;
|
||||
@@ -137,6 +138,8 @@ public class SimilarAsinTaskService {
|
||||
private static final String CONTENT_TYPE_XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||||
private static final String CONTENT_TYPE_ZIP = "application/zip";
|
||||
private static final int RESULT_ROWS_READ_RETRY_LIMIT = 3;
|
||||
/** Task 19:Python 回传逐行日志采样频率(每 N 行记一行)。 */
|
||||
private static final long PYTHON_INBOUND_LOG_EVERY_N = 20L;
|
||||
private static final long RESULT_ROWS_READ_RETRY_DELAY_MS = 500L;
|
||||
private static final Duration TASK_LOCK_TTL = Duration.ofMinutes(5);
|
||||
private static final long TASK_LOCK_WAIT_MILLIS = 10000L;
|
||||
@@ -1982,21 +1985,25 @@ public class SimilarAsinTaskService {
|
||||
* 打印 Python 端回传给 Java 的每一行 row 关键字段,确认 url(主图)/ urls(同类商品图)/ title / sku
|
||||
* 是否按预期到达。该日志与 SimilarAsinCozeClient 的 coze items diff 日志成对,
|
||||
* 便于排查"Python 回传了什么、Java 又把什么发到 Coze"。
|
||||
* Task 19:改为 DEBUG 级别并按行采样(每 20 行记一行),减少大任务日志量。
|
||||
*/
|
||||
private void logPythonInboundRows(List<SimilarAsinResultRowDto> rows) {
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
log.info("[similar-asin] python inbound start size={}", rows.size());
|
||||
log.debug("[similar-asin] python inbound start size={}", rows.size());
|
||||
for (int i = 0; i < rows.size(); i++) {
|
||||
SimilarAsinResultRowDto row = rows.get(i);
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
if (!SimilarAsinLogSupport.shouldLog(i, PYTHON_INBOUND_LOG_EVERY_N)) {
|
||||
continue;
|
||||
}
|
||||
String url = row.getUrl();
|
||||
List<String> urls = row.getUrls();
|
||||
List<SimilarAsinResultRowDto.AlibabaItem> alibaba = row.getAlibaba();
|
||||
log.info("[similar-asin] python inbound idx={} groupKey={} rowToken={} id={} asin={} country={} title={} sku={} price={} url={} urlsSize={} alibabaSize={} urlsHead={} urlsTail={}",
|
||||
log.debug("[similar-asin] python inbound idx={} groupKey={} rowToken={} id={} asin={} country={} title={} sku={} price={} url={} urlsSize={} alibabaSize={} urlsHead={} urlsTail={}",
|
||||
i,
|
||||
normalize(row.getGroupKey()),
|
||||
normalize(row.getRowToken()),
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.nanri.aiimage.modules.similarasin.util;
|
||||
|
||||
/**
|
||||
* Task 19:Coze 请求/响应及 Python 回传日志的采样与截断工具。
|
||||
* truncate 保证超长正文输出有界(前缀 + 长度 + 后缀),不抛异常、不破坏代理对;
|
||||
* shouldLog 按每 everyN 次采样一次(counter % everyN == 0),计数 0 恒采样。
|
||||
* 两个方法均为纯函数,可在日志点直接内联使用。
|
||||
*/
|
||||
public final class SimilarAsinLogSupport {
|
||||
|
||||
private SimilarAsinLogSupport() {
|
||||
}
|
||||
|
||||
/** 截断前缀保留长度(不含"…"与长度后缀)。 */
|
||||
public static final int TRUNCATE_PREFIX_LENGTH = 2000;
|
||||
|
||||
/**
|
||||
* 正文截断:长度 ≤ maxChars 原样返回;超过保留前 maxChars 字符并附
|
||||
* "…[total=N chars]" 长度后缀。maxChars ≤ 0 视为不截断。
|
||||
* null 返回空串。
|
||||
*/
|
||||
public static String truncate(String value, int maxChars) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
if (maxChars <= 0 || value.length() <= maxChars) {
|
||||
return value;
|
||||
}
|
||||
return value.substring(0, maxChars) + "…[total=" + value.length() + " chars]";
|
||||
}
|
||||
|
||||
/** 便捷重载:使用默认前缀长度 TRUNCATE_PREFIX_LENGTH。 */
|
||||
public static String truncate(String value) {
|
||||
return truncate(value, TRUNCATE_PREFIX_LENGTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* 采样判定:每 everyN 次输出一次(counter % everyN == 0)。
|
||||
* everyN ≤ 0 视为恒采样;counter 为 0 恒采样;计数接近溢出时取模结果仍稳定。
|
||||
*/
|
||||
public static boolean shouldLog(long counter, long everyN) {
|
||||
if (everyN <= 0) {
|
||||
return true;
|
||||
}
|
||||
long normalized = counter >= 0 ? counter : -counter;
|
||||
return normalized % everyN == 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user