From 26261263a0c6c4a865f1605525a9a7c67f1db339 Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Fri, 22 May 2026 23:43:39 +0800 Subject: [PATCH] fix: use docker ps labels to list services and fetch logs Replace compose-file-path-dependent `docker compose -f ` calls with label-based `docker ps` queries so the ops log viewer works on both public cloud and private deployments regardless of compose file location. Co-Authored-By: Claude Sonnet 4.6 --- .../tenant/service/SystemUpdateService.java | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/tenant-service/src/main/java/com/xuqm/tenant/service/SystemUpdateService.java b/tenant-service/src/main/java/com/xuqm/tenant/service/SystemUpdateService.java index dc3b67b..1c3a9b4 100644 --- a/tenant-service/src/main/java/com/xuqm/tenant/service/SystemUpdateService.java +++ b/tenant-service/src/main/java/com/xuqm/tenant/service/SystemUpdateService.java @@ -60,15 +60,16 @@ public class SystemUpdateService { // ── 公开接口 ──────────────────────────────────────────────────────────────── /** - * 返回当前正在运行的服务名列表(docker compose ps --filter status=running)。 - * 结果已过滤为 ALLOWED_LOG_SERVICES 白名单内的服务,防止枚举非预期服务。 + * 返回当前正在运行的服务名列表。 + * 使用 docker ps --format "{{.Label \"com.docker.compose.service\"}}" 枚举运行中容器的服务标签, + * 不依赖 compose 文件路径,公有云/私有云均可用。 + * 结果已过滤为 ALLOWED_LOG_SERVICES 白名单内的服务。 */ public List getRunningServices() { - String composeFile = deployRoot + "/docker-compose.yml"; try { Process p = new ProcessBuilder( - "docker", "compose", "-f", composeFile, - "ps", "--services", "--filter", "status=running" + "docker", "ps", + "--format", "{{.Label \"com.docker.compose.service\"}}" ).redirectErrorStream(true).start(); String out = new String(p.getInputStream().readAllBytes(), StandardCharsets.UTF_8).trim(); p.waitFor(); @@ -76,6 +77,7 @@ public class SystemUpdateService { return Arrays.stream(out.split("\n")) .map(String::trim) .filter(s -> !s.isEmpty() && ALLOWED_LOG_SERVICES.contains(s)) + .distinct() .collect(Collectors.toList()); } catch (Exception e) { log.error("failed to list running services", e); @@ -85,23 +87,36 @@ public class SystemUpdateService { /** * 获取指定服务最近 N 行日志(上限 1000)。 - * service 名称校验白名单,防止注入。 + * 通过 docker ps 标签找到容器 ID 后使用 docker logs,不依赖 compose 文件路径。 */ public String getServiceLogs(String service, int lines) { if (!ALLOWED_LOG_SERVICES.contains(service)) { throw new IllegalArgumentException("不允许查看此服务的日志: " + service); } int safeLines = Math.min(Math.max(lines, 10), 1000); - String composeFile = deployRoot + "/docker-compose.yml"; try { - Process p = new ProcessBuilder( - "docker", "compose", "-f", composeFile, - "logs", "--tail", String.valueOf(safeLines), "--no-color", service + // 找到对应服务的容器 ID(可能有多个,取第一个) + Process psProc = new ProcessBuilder( + "docker", "ps", + "--filter", "label=com.docker.compose.service=" + service, + "--format", "{{.ID}}" ).redirectErrorStream(true).start(); - String out = new String(p.getInputStream().readAllBytes(), StandardCharsets.UTF_8); - int exitCode = p.waitFor(); + String containerId = new String(psProc.getInputStream().readAllBytes(), StandardCharsets.UTF_8).trim(); + psProc.waitFor(); + + if (containerId.isEmpty()) { + return "(服务 " + service + " 当前没有运行中的容器)"; + } + // 取第一行(如有多个容器) + String firstId = containerId.split("\n")[0].trim(); + + Process logsProc = new ProcessBuilder( + "docker", "logs", "--tail", String.valueOf(safeLines), "--timestamps", firstId + ).redirectErrorStream(true).start(); + String out = new String(logsProc.getInputStream().readAllBytes(), StandardCharsets.UTF_8); + int exitCode = logsProc.waitFor(); if (exitCode != 0 && out.isBlank()) { - throw new RuntimeException("docker compose logs 返回非零退出码: " + exitCode); + throw new RuntimeException("docker logs 返回非零退出码: " + exitCode); } return out; } catch (IllegalArgumentException e) {