浏览代码

fix:指定Kiro-cli日志路径,避免多会话日志覆盖

Jerry Liang 1 月之前
父节点
当前提交
e2f09bbfe1
共有 1 个文件被更改,包括 65 次插入 和 0 次删除
  1. 65 0
      service/utils/acp/acp.go

+ 65 - 0
service/utils/acp/acp.go

@@ -382,6 +382,47 @@ func fillEnvIfAbsent(env []string, key, value string) []string {
 	return append(env, prefix+value)
 }
 
+// envHasKey 判断 env("KEY=VALUE" 形式的切片)中是否已存在指定 key。
+func envHasKey(env []string, key string) bool {
+	prefix := key + "="
+	for _, e := range env {
+		if strings.HasPrefix(e, prefix) {
+			return true
+		}
+	}
+	return false
+}
+
+// ensureDirWritableByRunAsUser 确保日志目录可被降权用户写入。
+// 主服务常以 root 运行,创建的目录属主为 root;子进程降权到 runAsUser 后会写不进去,
+// 导致 kiro-cli 日志被静默丢弃。这里将目录(含其直接父目录)chown 到 runAsUser 的 uid/gid。
+// runAsUser 为空表示不降权(与主进程同用户),无需调整。所有失败仅记录告警,不致命。
+func ensureDirWritableByRunAsUser(dir string, runAsUser string) {
+	runAsUser = strings.TrimSpace(runAsUser)
+	if runAsUser == "" {
+		return
+	}
+	u, err := user.Lookup(runAsUser)
+	if err != nil {
+		zap.L().Warn("[ACP] 查找降权用户失败,日志目录属主未调整", zap.String("user", runAsUser), zap.Error(err))
+		return
+	}
+	uid, err1 := strconv.Atoi(u.Uid)
+	gid, err2 := strconv.Atoi(u.Gid)
+	if err1 != nil || err2 != nil {
+		zap.L().Warn("[ACP] 解析降权用户 uid/gid 失败,日志目录属主未调整",
+			zap.String("user", runAsUser), zap.String("uid", u.Uid), zap.String("gid", u.Gid))
+		return
+	}
+	// 同时调整父目录(如 /tmp/kiro-log)与目标目录(如 /tmp/kiro-log/logs)。
+	for _, d := range []string{filepath.Dir(dir), dir} {
+		if err := os.Chown(d, uid, gid); err != nil {
+			zap.L().Warn("[ACP] 调整日志目录属主失败(降权用户可能无法写入日志)",
+				zap.String("dir", d), zap.String("user", runAsUser), zap.Error(err))
+		}
+	}
+}
+
 // overrideEnv 用 kv 覆盖 env 中已存在的同名变量,不存在则追加。
 func overrideEnv(env []string, kv map[string]string) []string {
 	out := make([]string, 0, len(env)+len(kv))
@@ -420,6 +461,30 @@ func NewClient(cfg ClientConfig) (*Client, error) {
 	// 设置环境变量
 	cmd.Env = append(os.Environ(), cfg.Env...)
 
+	// A 方案:为每次启动的子进程分配带启动时间戳的独立日志文件,避免覆盖历史日志。
+	// 仅在开启了 kiro-cli debug 日志(env 含 KIRO_LOG_LEVEL)、且调用方未显式指定
+	// KIRO_CHAT_LOG_FILE 时生效。KIRO_CHAT_LOG_FILE 是 kiro-cli acp 官方支持的日志路径变量。
+	if envHasKey(cmd.Env, "KIRO_LOG_LEVEL") && !envHasKey(cmd.Env, "KIRO_CHAT_LOG_FILE") {
+		logDir := "/tmp/kiro-log/logs"
+		if err := os.MkdirAll(logDir, 0o777); err != nil {
+			zap.L().Warn("[ACP] 创建日志目录失败,沿用 kiro-cli 默认日志路径",
+				zap.String("dir", logDir), zap.Error(err))
+		} else {
+			// 主服务常以 root 运行,创建出的目录默认属主为 root;而子进程会被
+			// ApplyRunAsUser 降权到 cfg.RunAsUser,若目录不可写日志将被静默丢弃。
+			// 因此这里把目录属主/权限调整到降权用户可写。
+			ensureDirWritableByRunAsUser(logDir, cfg.RunAsUser)
+
+			logPath := filepath.Join(logDir, fmt.Sprintf("kiro-chat-%s.log", time.Now().Format("20060102T150405.000")))
+			cmd.Env = append(cmd.Env, "KIRO_CHAT_LOG_FILE="+logPath)
+			// 顺带关闭 ANSI 颜色码,便于后续直接阅读/grep 日志。
+			if !envHasKey(cmd.Env, "KIRO_LOG_NO_COLOR") {
+				cmd.Env = append(cmd.Env, "KIRO_LOG_NO_COLOR=1")
+			}
+			zap.L().Info("[ACP] 本次子进程日志文件", zap.String("logFile", logPath))
+		}
+	}
+
 	// 若配置了降权用户,则以该用户身份拉起子进程
 	if err := ApplyRunAsUser(cmd, cfg.RunAsUser); err != nil {
 		return nil, err