# 任务:Realtime Logs 数据流优化 (缓冲与回放) [已完成] ## 目标 解决前端 `Realtime Logs` 面板在页面刷新、重新连接或初始加载延迟时丢失日志的问题。 目标是确保 "First-hand" 服务端日志能够可靠地流向前端,不依赖 NATS 的临时性。 ## 实施方案 ### 1. 后端:增强 `SyncStateCommand` (Orchestrator) 我们需要修改 `handle_sync_state` 逻辑,使其在发送状态快照的同时,**也能读取当前的临时日志文件**,并将历史日志作为事件发送给前端。 * **修改 `workflow.rs` -> `handle_sync_state`**: * 调用 `log_manager.read_current_logs(req_id)` (需要新增此方法,非破坏性读取)。 * 读取到的日志内容可能是巨大的字符串。为了不阻塞 NATS 消息,可以分块发送,或者作为 `WorkflowStateSnapshot` 的一部分发送(如果大小允许)。 * **方案选择**: 发送一个新的事件类型 `WorkflowLogHistory` 或者复用 `TaskLog`(批量发送)。 * 鉴于前端 `handleEvent` 处理 `TaskLog` 是追加式的,我们可以循环发送 `TaskLog` 事件。 * **更优方案**: 在 `WorkflowStateSnapshot` 结构体中增加 `logs: Vec` 字段。这样前端在恢复快照时一次性填入。 ### 2. 定义数据结构变更 * **`common-contracts/src/messages.rs`**: * 修改 `WorkflowStateSnapshot`,增加 `logs: Vec`。 ### 3. 完善 `LogBufferManager` * **`logging.rs`**: * 新增 `read_current_logs(&self, request_id: &str) -> Result>`。 * 读取文件,按行分割,返回 `Vec`。 ### 4. 前端适配 * **`useWorkflowStore.ts`**: * 在 `handleEvent` -> `WorkflowStateSnapshot` 分支中,处理 `event.payload.logs`。 * 将这些日志合并到 `state.logs` (Global Logs) 或者解析后分发到 `state.tasks` (如果日志格式包含 Task ID)。 * 目前日志格式为 `[ISO Time] [Level] Message`,不一定包含 Task ID,所以主要作为 Global Logs 展示。 ### 5. 流程梳理 1. **前端启动/刷新**: * 调用 `SSE /events/{id}`。 * API Gateway 收到连接,订阅 NATS,并发送 `SyncStateCommand` 给 Orchestrator。 2. **Orchestrator**: * 收到 `SyncStateCommand`。 * 生成 DAG 快照。 * **读取 `temp_logs/{id}.log`**。 * 构建 `WorkflowStateSnapshot` (包含 logs)。 * 发布到 NATS。 3. **前端接收**: * 收到 Snapshot。 * 恢复 DAG 状态。 * 恢复 Logs 面板内容。 4. **后续实时日志**: * Orchestrator 继续运行,Tracing Layer 写入文件。 * **关键点**: 我们之前删除了 `publish_log`。现在需要恢复**实时推送**能力,但不是手动调用。 * **方案**: `FileRequestLogLayer` 除了写文件,还应该有一个机制将日志推送到 NATS 吗? * **回答**: 是的。之前的重构把推送删了,导致前端**收不到实时更新**了。 * **修正**: `FileRequestLogLayer` 应该同时负责: 1. 写文件 (持久化缓冲)。 2. 推送到 NATS (实时展示)。 * **技术难点**: Layer 是同步的,NATS 是异步的。 * **解决**: 使用 `tokio::sync::broadcast` 或 `mpsc` 通道。Layer 将日志发送到通道,有一个后台 Task 负责接收通道消息并推送到 NATS。 ## 修正后的后端任务列表 1. **恢复实时推送通道**: * 在 `AppState` 中增加一个 `log_broadcast_tx` (sender)。 * `FileRequestLogLayer` 持有这个 sender。 * 在 `main.rs` 启动一个后台任务,监听 receiver,将日志封装为 `WorkflowEvent::TaskLog` 并推送到 NATS。 2. **实现历史回放 (Snapshot)**: * 修改 `WorkflowStateSnapshot` 增加 `logs` 字段。 * `LogBufferManager` 增加读取方法。 * `handle_sync_state` 填充 logs。 ## 前端任务列表 1. 更新 `WorkflowStateSnapshot` 类型定义。 2. 在 Store 中处理 Snapshot 携带的日志。 这个方案兼顾了实时性和可靠性(断线重连)。