From 2a9b896f3fa4378ab9d1afe237416213971557f6 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Sun, 14 Dec 2025 00:25:33 +0800 Subject: [PATCH] =?UTF-8?q?1.30=20k8s=20pods=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/K8sDeploymentApiController.java | 23 ++++++-- .../deploy/dto/K8sPodLogsResponse.java | 11 +++- .../impl/K8sServiceIntegrationImpl.java | 2 +- .../service/impl/K8sPodServiceImpl.java | 7 +-- .../backend/deploy/utils/K8sLogParser.java | 54 ++++++++++++------- 5 files changed, 67 insertions(+), 30 deletions(-) diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/K8sDeploymentApiController.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/K8sDeploymentApiController.java index bbdd256b..a47f699d 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/K8sDeploymentApiController.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/K8sDeploymentApiController.java @@ -123,11 +123,24 @@ public class K8sDeploymentApiController extends BaseController getPodLogs( diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/K8sPodLogsResponse.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/K8sPodLogsResponse.java index f1f68097..cbaae906 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/K8sPodLogsResponse.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/K8sPodLogsResponse.java @@ -25,9 +25,16 @@ public class K8sPodLogsResponse { private String containerName; /** - * 日志选择器(用于下次请求) + * 用于向前翻页的引用点选择器 + * 使用此选择器可以获取更早的日志 */ - private K8sLogSelection selection; + private K8sLogSelection referenceForPrevious; + + /** + * 用于向后翻页/轮询的引用点选择器 + * 使用此选择器可以获取更新的日志(轮询时使用) + */ + private K8sLogSelection referenceForNext; /** * 日志行列表 diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/impl/K8sServiceIntegrationImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/impl/K8sServiceIntegrationImpl.java index 4494a8e9..ea567ea2 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/impl/K8sServiceIntegrationImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/impl/K8sServiceIntegrationImpl.java @@ -627,7 +627,7 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp false, // previous(是否查询上一个容器的日志) effectiveSinceSeconds, // sinceSeconds(使用智能默认值) effectiveTail, // tail(使用智能默认值) - false // timestamps + true // timestamps(必须启用,用于引用点系统) ); int logLength = logs != null ? logs.length() : 0; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/K8sPodServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/K8sPodServiceImpl.java index 848bd0af..6ca604f5 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/K8sPodServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/K8sPodServiceImpl.java @@ -151,8 +151,8 @@ public class K8sPodServiceImpl implements IK8sPodService { .orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND)); // 4. 调用K8s API获取原始日志(获取足够多的日志以支持切片) - // 使用较大的tail值确保能够覆盖请求的范围 - Integer effectiveTail = Math.max(1000, Math.abs(offsetFrom) + Math.abs(offsetTo) + 100); + // 使用较大的tail值和缓冲区确保能够覆盖请求的范围 + Integer effectiveTail = Math.max(2000, Math.abs(offsetFrom) + Math.abs(offsetTo) + 500); String rawLogs = k8sServiceIntegration.getPodLogs( externalSystem, namespace.getNamespaceName(), @@ -180,7 +180,8 @@ public class K8sPodServiceImpl implements IK8sPodService { return new com.qqchen.deploy.backend.deploy.dto.K8sPodLogsResponse( podName, container != null ? container : "default", - result.getSelection(), + result.getReferenceForPrevious(), + result.getReferenceForNext(), result.getLogs(), result.isTruncated() ); diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/utils/K8sLogParser.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/utils/K8sLogParser.java index b2aae406..2291d919 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/utils/K8sLogParser.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/utils/K8sLogParser.java @@ -23,11 +23,11 @@ public class K8sLogParser { /** * K8s日志时间戳正则表达式 - * 匹配RFC3339格式:2025-12-13T23:27:18.124567890Z - * 或常规格式:2025-12-13 23:27:18.124 + * 只匹配RFC3339格式:2025-12-13T23:27:18.124567890Z + * 这是K8s API返回的标准格式(当timestamps=true时) */ private static final Pattern TIMESTAMP_PATTERN = Pattern.compile( - "^(\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}\\.\\d+Z?)\\s" + "^(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d+Z)\\s" ); /** @@ -131,14 +131,16 @@ public class K8sLogParser { */ public static LogSliceResult selectLogs(List lines, K8sLogSelection selection) { if (lines == null || lines.isEmpty()) { - return new LogSliceResult(Collections.emptyList(), selection, false); + K8sLogSelection emptySelection = new K8sLogSelection("newest", -100, 0); + return new LogSliceResult(Collections.emptyList(), emptySelection, emptySelection, false); } // 查找引用点索引 int referenceIndex = findReferenceIndex(lines, selection.getReferenceTimestamp()); if (referenceIndex == -1) { log.warn("引用点未找到: {}", selection.getReferenceTimestamp()); - return new LogSliceResult(Collections.emptyList(), selection, false); + K8sLogSelection emptySelection = new K8sLogSelection("newest", -100, 0); + return new LogSliceResult(Collections.emptyList(), emptySelection, emptySelection, false); } // 计算切片范围 @@ -164,22 +166,29 @@ public class K8sLogParser { // 确保范围有效 if (fromIndex >= toIndex || fromIndex < 0 || toIndex > lines.size()) { log.warn("无效的切片范围: fromIndex={}, toIndex={}, size={}", fromIndex, toIndex, lines.size()); - return new LogSliceResult(Collections.emptyList(), selection, false); + K8sLogSelection emptySelection = new K8sLogSelection("newest", -100, 0); + return new LogSliceResult(Collections.emptyList(), emptySelection, emptySelection, false); } // 切片 List result = lines.subList(fromIndex, toIndex); - // 创建新的引用点(使用返回结果的中间行) - // 参考Kubernetes Dashboard: 使用实际返回日志的中间位置 - int resultMiddleIndex = fromIndex + (toIndex - fromIndex) / 2; - K8sLogSelection newSelection = new K8sLogSelection( - lines.get(resultMiddleIndex).getTimestamp(), - fromIndex - resultMiddleIndex, - toIndex - resultMiddleIndex + // 创建两个引用点选择器 + // 1. 用于向前翻页:使用返回结果的第一行作为引用点 + K8sLogSelection referenceForPrevious = new K8sLogSelection( + lines.get(fromIndex).getTimestamp(), + -100, // 向前100行 + 0 // 到引用点(不包含) ); - return new LogSliceResult(new ArrayList<>(result), newSelection, truncated); + // 2. 用于向后翻页/轮询:使用返回结果的最后一行作为引用点 + K8sLogSelection referenceForNext = new K8sLogSelection( + lines.get(toIndex - 1).getTimestamp(), + 1, // 从引用点的下一行开始(不包含引用点,避免重复) + 101 // 向后100行 + ); + + return new LogSliceResult(new ArrayList<>(result), referenceForPrevious, referenceForNext, truncated); } /** @@ -187,12 +196,15 @@ public class K8sLogParser { */ public static class LogSliceResult { private final List logs; - private final K8sLogSelection selection; + private final K8sLogSelection referenceForPrevious; + private final K8sLogSelection referenceForNext; private final boolean truncated; - public LogSliceResult(List logs, K8sLogSelection selection, boolean truncated) { + public LogSliceResult(List logs, K8sLogSelection referenceForPrevious, + K8sLogSelection referenceForNext, boolean truncated) { this.logs = logs; - this.selection = selection; + this.referenceForPrevious = referenceForPrevious; + this.referenceForNext = referenceForNext; this.truncated = truncated; } @@ -200,8 +212,12 @@ public class K8sLogParser { return logs; } - public K8sLogSelection getSelection() { - return selection; + public K8sLogSelection getReferenceForPrevious() { + return referenceForPrevious; + } + + public K8sLogSelection getReferenceForNext() { + return referenceForNext; } public boolean isTruncated() {