feat: playbook执行日志实时流式推送(SSE) + 任务详情显示原始日志

This commit is contained in:
Hermes Agent
2026-05-18 15:08:48 +08:00
parent 6a7f74aac5
commit d58e860059
7 changed files with 197 additions and 44 deletions
+55 -4
View File
@@ -703,12 +703,45 @@ func (s *AnsibleService) runPlaybook(task *models.TaskExecution, playbookPath st
cmd = exec.CommandContext(ctx, "ansible-playbook", args...)
}
var output bytes.Buffer
cmd.Stdout = &output
cmd.Stderr = &output
// 实时写入日志的 Writer
sw := &syncWriter{buf: bytes.NewBuffer(nil)}
cmd.Stdout = sw
cmd.Stderr = sw
// 启动 goroutine 实时搬运日志到 task.Output
done := make(chan struct{})
go func() {
ticker := time.NewTicker(200 * time.Millisecond)
defer ticker.Stop()
var lastLen int
for {
select {
case <-ticker.C:
s.taskLock.Lock()
sw.mu.Lock()
currentLen := sw.buf.Len()
if currentLen > lastLen {
task.Output = sw.buf.String()
lastLen = currentLen
}
sw.mu.Unlock()
s.taskLock.Unlock()
case <-done:
return
}
}
}()
err := cmd.Run()
close(done) // 通知 goroutine 退出
// 最终同步一次完整日志
sw.mu.Lock()
finalOutput := sw.buf.String()
sw.mu.Unlock()
s.taskLock.Lock()
task.Output = finalOutput
task.EndTime = time.Now()
if err != nil {
task.Status = "failed"
@@ -716,7 +749,25 @@ func (s *AnsibleService) runPlaybook(task *models.TaskExecution, playbookPath st
} else {
task.Status = "success"
}
task.Output = output.String()
s.taskLock.Unlock()
}
// syncWriter 线程安全的 Writer
type syncWriter struct {
buf *bytes.Buffer
mu sync.Mutex
}
func (w *syncWriter) Write(p []byte) (n int, err error) {
w.mu.Lock()
defer w.mu.Unlock()
return w.buf.Write(p)
}
func (w *syncWriter) String() string {
w.mu.Lock()
defer w.mu.Unlock()
return w.buf.String()
}
// ListPlaybooks 列出可用Playbooks