1.30 k8s pods查询
This commit is contained in:
parent
daeb880bff
commit
2a9b896f3f
@ -123,11 +123,24 @@ public class K8sDeploymentApiController extends BaseController<K8sDeployment, K8
|
|||||||
|
|
||||||
@Operation(
|
@Operation(
|
||||||
summary = "查询Pod日志(引用点模式)",
|
summary = "查询Pod日志(引用点模式)",
|
||||||
description = "基于Kubernetes Dashboard的引用点系统查询日志,支持无重复轮询和前后翻页。\n" +
|
description = "基于Kubernetes Dashboard的引用点系统查询日志,支持无重复轮询和前后翻页。\n\n" +
|
||||||
"初始加载:referenceTimestamp=newest, offsetFrom=-100, offsetTo=1\n" +
|
"**初始加载(获取最新100行)**:\n" +
|
||||||
"轮询刷新:使用上次返回的selection作为参数\n" +
|
"- referenceTimestamp=newest\n" +
|
||||||
"向前翻页:offsetFrom=-200, offsetTo=-100\n" +
|
"- offsetFrom=-100\n" +
|
||||||
"向后翻页:offsetFrom=100, offsetTo=200"
|
"- offsetTo=0\n\n" +
|
||||||
|
"**轮询刷新(获取新日志)**:\n" +
|
||||||
|
"- 使用上次返回的 referenceForNext.referenceTimestamp\n" +
|
||||||
|
"- 使用上次返回的 referenceForNext.offsetFrom (固定为0)\n" +
|
||||||
|
"- 使用上次返回的 referenceForNext.offsetTo (固定为100)\n\n" +
|
||||||
|
"**向前翻页(获取更早的日志)**:\n" +
|
||||||
|
"- 使用上次返回的 referenceForPrevious.referenceTimestamp\n" +
|
||||||
|
"- 使用上次返回的 referenceForPrevious.offsetFrom (固定为-100)\n" +
|
||||||
|
"- 使用上次返回的 referenceForPrevious.offsetTo (固定为0)\n\n" +
|
||||||
|
"**响应字段说明**:\n" +
|
||||||
|
"- referenceForPrevious: 用于向前翻页的选择器\n" +
|
||||||
|
"- referenceForNext: 用于向后翻页/轮询的选择器\n" +
|
||||||
|
"- logs: 当前返回的日志行列表\n" +
|
||||||
|
"- truncated: 是否因边界限制被截断"
|
||||||
)
|
)
|
||||||
@GetMapping("/{deploymentId}/pods/{podName}/logs")
|
@GetMapping("/{deploymentId}/pods/{podName}/logs")
|
||||||
public Response<com.qqchen.deploy.backend.deploy.dto.K8sPodLogsResponse> getPodLogs(
|
public Response<com.qqchen.deploy.backend.deploy.dto.K8sPodLogsResponse> getPodLogs(
|
||||||
|
|||||||
@ -25,9 +25,16 @@ public class K8sPodLogsResponse {
|
|||||||
private String containerName;
|
private String containerName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 日志选择器(用于下次请求)
|
* 用于向前翻页的引用点选择器
|
||||||
|
* 使用此选择器可以获取更早的日志
|
||||||
*/
|
*/
|
||||||
private K8sLogSelection selection;
|
private K8sLogSelection referenceForPrevious;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于向后翻页/轮询的引用点选择器
|
||||||
|
* 使用此选择器可以获取更新的日志(轮询时使用)
|
||||||
|
*/
|
||||||
|
private K8sLogSelection referenceForNext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 日志行列表
|
* 日志行列表
|
||||||
|
|||||||
@ -627,7 +627,7 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration imp
|
|||||||
false, // previous(是否查询上一个容器的日志)
|
false, // previous(是否查询上一个容器的日志)
|
||||||
effectiveSinceSeconds, // sinceSeconds(使用智能默认值)
|
effectiveSinceSeconds, // sinceSeconds(使用智能默认值)
|
||||||
effectiveTail, // tail(使用智能默认值)
|
effectiveTail, // tail(使用智能默认值)
|
||||||
false // timestamps
|
true // timestamps(必须启用,用于引用点系统)
|
||||||
);
|
);
|
||||||
|
|
||||||
int logLength = logs != null ? logs.length() : 0;
|
int logLength = logs != null ? logs.length() : 0;
|
||||||
|
|||||||
@ -151,8 +151,8 @@ public class K8sPodServiceImpl implements IK8sPodService {
|
|||||||
.orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));
|
.orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));
|
||||||
|
|
||||||
// 4. 调用K8s API获取原始日志(获取足够多的日志以支持切片)
|
// 4. 调用K8s API获取原始日志(获取足够多的日志以支持切片)
|
||||||
// 使用较大的tail值确保能够覆盖请求的范围
|
// 使用较大的tail值和缓冲区确保能够覆盖请求的范围
|
||||||
Integer effectiveTail = Math.max(1000, Math.abs(offsetFrom) + Math.abs(offsetTo) + 100);
|
Integer effectiveTail = Math.max(2000, Math.abs(offsetFrom) + Math.abs(offsetTo) + 500);
|
||||||
String rawLogs = k8sServiceIntegration.getPodLogs(
|
String rawLogs = k8sServiceIntegration.getPodLogs(
|
||||||
externalSystem,
|
externalSystem,
|
||||||
namespace.getNamespaceName(),
|
namespace.getNamespaceName(),
|
||||||
@ -180,7 +180,8 @@ public class K8sPodServiceImpl implements IK8sPodService {
|
|||||||
return new com.qqchen.deploy.backend.deploy.dto.K8sPodLogsResponse(
|
return new com.qqchen.deploy.backend.deploy.dto.K8sPodLogsResponse(
|
||||||
podName,
|
podName,
|
||||||
container != null ? container : "default",
|
container != null ? container : "default",
|
||||||
result.getSelection(),
|
result.getReferenceForPrevious(),
|
||||||
|
result.getReferenceForNext(),
|
||||||
result.getLogs(),
|
result.getLogs(),
|
||||||
result.isTruncated()
|
result.isTruncated()
|
||||||
);
|
);
|
||||||
|
|||||||
@ -23,11 +23,11 @@ public class K8sLogParser {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* K8s日志时间戳正则表达式
|
* K8s日志时间戳正则表达式
|
||||||
* 匹配RFC3339格式:2025-12-13T23:27:18.124567890Z
|
* 只匹配RFC3339格式:2025-12-13T23:27:18.124567890Z
|
||||||
* 或常规格式:2025-12-13 23:27:18.124
|
* 这是K8s API返回的标准格式(当timestamps=true时)
|
||||||
*/
|
*/
|
||||||
private static final Pattern TIMESTAMP_PATTERN = Pattern.compile(
|
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<K8sLogLine> lines, K8sLogSelection selection) {
|
public static LogSliceResult selectLogs(List<K8sLogLine> lines, K8sLogSelection selection) {
|
||||||
if (lines == null || lines.isEmpty()) {
|
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());
|
int referenceIndex = findReferenceIndex(lines, selection.getReferenceTimestamp());
|
||||||
if (referenceIndex == -1) {
|
if (referenceIndex == -1) {
|
||||||
log.warn("引用点未找到: {}", selection.getReferenceTimestamp());
|
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()) {
|
if (fromIndex >= toIndex || fromIndex < 0 || toIndex > lines.size()) {
|
||||||
log.warn("无效的切片范围: fromIndex={}, toIndex={}, size={}", fromIndex, 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<K8sLogLine> result = lines.subList(fromIndex, toIndex);
|
List<K8sLogLine> result = lines.subList(fromIndex, toIndex);
|
||||||
|
|
||||||
// 创建新的引用点(使用返回结果的中间行)
|
// 创建两个引用点选择器
|
||||||
// 参考Kubernetes Dashboard: 使用实际返回日志的中间位置
|
// 1. 用于向前翻页:使用返回结果的第一行作为引用点
|
||||||
int resultMiddleIndex = fromIndex + (toIndex - fromIndex) / 2;
|
K8sLogSelection referenceForPrevious = new K8sLogSelection(
|
||||||
K8sLogSelection newSelection = new K8sLogSelection(
|
lines.get(fromIndex).getTimestamp(),
|
||||||
lines.get(resultMiddleIndex).getTimestamp(),
|
-100, // 向前100行
|
||||||
fromIndex - resultMiddleIndex,
|
0 // 到引用点(不包含)
|
||||||
toIndex - resultMiddleIndex
|
|
||||||
);
|
);
|
||||||
|
|
||||||
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 {
|
public static class LogSliceResult {
|
||||||
private final List<K8sLogLine> logs;
|
private final List<K8sLogLine> logs;
|
||||||
private final K8sLogSelection selection;
|
private final K8sLogSelection referenceForPrevious;
|
||||||
|
private final K8sLogSelection referenceForNext;
|
||||||
private final boolean truncated;
|
private final boolean truncated;
|
||||||
|
|
||||||
public LogSliceResult(List<K8sLogLine> logs, K8sLogSelection selection, boolean truncated) {
|
public LogSliceResult(List<K8sLogLine> logs, K8sLogSelection referenceForPrevious,
|
||||||
|
K8sLogSelection referenceForNext, boolean truncated) {
|
||||||
this.logs = logs;
|
this.logs = logs;
|
||||||
this.selection = selection;
|
this.referenceForPrevious = referenceForPrevious;
|
||||||
|
this.referenceForNext = referenceForNext;
|
||||||
this.truncated = truncated;
|
this.truncated = truncated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,8 +212,12 @@ public class K8sLogParser {
|
|||||||
return logs;
|
return logs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public K8sLogSelection getSelection() {
|
public K8sLogSelection getReferenceForPrevious() {
|
||||||
return selection;
|
return referenceForPrevious;
|
||||||
|
}
|
||||||
|
|
||||||
|
public K8sLogSelection getReferenceForNext() {
|
||||||
|
return referenceForNext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTruncated() {
|
public boolean isTruncated() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user