Files
pikasTech-unidesk/docs/reference/observability.md
T
Codex b36d7f94d7 feat: harden code queue runtime UX
- add queue merge controls and no-cache overview loading\n- improve runtime probes, judge retries, and task trace rendering\n- extend CLI/E2E/docs coverage for code queue and user services
2026-05-14 02:20:48 +00:00

6.8 KiB
Raw Blame History

UniDesk Observability Reference

UniDesk 的可观测性优先级高于静默成功。CLI、服务日志、Docker 日志和数据库状态都必须能通过短命令查询。

CLI Logs

异步 job 的 stdout 和 stderr 位于 .state/jobs/job status 会返回有限尾部,避免输出爆炸,同时保留完整日志文件路径便于继续排查。

Service Logs

服务日志位于 logs/{YYYYMMDD}/,每次 server start 都生成新的本地时间戳前缀。新写入的 UniDesk JSONL 日志必须按小时切片:logs/{YYYYMMDD}/{startStamp}_{YYYYMMDD}_{HH}_{service}.jsonl,一天一个目录,禁止长期追加到单个巨大 JSONL。所有 UniDesk Bun 服务(backend-core、frontend、provider-gateway、Code Queue、project-manager、baidu-netdisk 以及后续新增服务)必须复用 src/components/shared/src/rotating-jsonl.ts 中的 createHourlyJsonlWriterLOG_FILE 只作为推导 logs 根目录、启动前缀和 service 后缀的 base path,不得直接 appendFileSync(LOG_FILE, ...) 长期追加。database 通过 PostgreSQL logging collector 写入同一日期目录。

日志保留默认按日志族限制为 1GiB:服务写入或 Code Queue 导出日志时必须扫描同一 service 后缀的历史文件,超过上限后自动删除最旧切片;当前活跃切片不能被保留清理删除。全局上限由 UNIDESK_LOG_RETENTION_BYTES 控制,服务级上限使用 UNIDESK_<SERVICE>_LOG_MAX_BYTES(如 UNIDESK_FRONTEND_LOG_MAX_BYTESUNIDESK_PROVIDER_GATEWAY_LOG_MAX_BYTES),历史兼容变量只允许作为过渡入口。Codex app-server 的 logs_*.sqlite 仅作为 Codex 上游运行时的短暂缓冲,Code Queue 必须周期性导出为同样按小时切片的 codex-app-server JSONL,并删除/压缩已导出的 SQLite 行,避免 logs_2.sqlite 成为长期大文件。

新增或迁移服务的长期规范:Dockerfile 必须把 src/components/shared 复制到与仓库相同的相对路径,TypeScript 配置必须能解析 shared 引用,Compose 必须传入 LOG_FILEUNIDESK_LOG_RETENTION_BYTES;如果服务需要在内存中暴露 /logs,可以继续维护有限 recentLogs,但落盘只能通过统一 hourly writer。业务归档日志(例如 Code Queue task output archive)可以保留 append-only 文件,但不得复用 UniDesk service JSONL 命名族,也不得替代 /logs 的结构化服务日志。

bun scripts/cli.ts check 必须包含日志轮转门禁:核心 Bun 服务源码不得直接向 LOG_FILE append,且必须引用统一 hourly writer。新增服务如果进入主 Compose,也要纳入该门禁的 checked file 列表。

Log Access

bun scripts/cli.ts server logs 同时读取文件日志和 Docker logs 尾部。文件日志是服务崩溃时的第一现场,Docker logs 是容器启动失败和 stdout/stderr 的辅助来源。

Task Liveness

backend-core 必须把 queued、dispatched、running 视为待处理任务,并通过 TASK_PENDING_TIMEOUT_MS 对长时间没有 provider 终态回报的任务做超时处理。超时任务转为 failedresult 中保留 timeout、previousStatus 和 previousResult 摘要,避免 态势总览 的待处理数量长期卡住且无法解释。

Performance Metrics

backend-core 必须提供 /api/performance,返回滚动窗口内的 HTTP 组件请求统计、最近失败请求、内部操作统计、最近慢操作、进程内存、PGDATA 用量和 Code Queue PostgreSQL 存储摘要。组件统计必须包含请求数、失败数、失败率、平均延迟和 P95,内部操作统计必须包含服务名、操作名、次数、平均延迟和 P95;失败和慢操作记录必须保留时间、状态、耗时、路径或细节,避免只给汇总数字而无法定位。

frontend Bun server 必须提供同源 /api/frontend-performance,记录 webui 静态资源、登录/session、API 代理和 frontend->core 代理操作耗时。浏览器中的 运行总览 / 性能面板 必须把 frontend 与 backend-core 指标合并展示为 Bwebui 曲线、组件汇总、最近失败请求、内部操作汇总和最近慢操作;完整性能 JSON 只能通过显式 查看原始JSON 打开。

性能优化必须先用这些指标锁定慢操作名称、路径、耗时和代理层级,再改后端查询或前后端通信策略;不得只凭主观体感改 UI。Code Queue 这类控制面页面出现 code_queue_direct_proxycore_proxyGET /api/tasks/overviewPOST /api/tasks/<id>/read 等超过 1s 的慢操作时,应保留优化前后的性能面板证据,并同时记录 live API 耗时、容器内存、/health 存储摘要和是否仍通过 PostgreSQL/append-only archive 重建历史数据。短 TTL cache、warmup 或页面内存缓存只能作为重复请求抖动保护,性能证据必须证明数据库索引/聚合、分页和渐进式披露本身已把核心路径降到目标内,不能用长缓存遮蔽慢 SQL 或全量 JSON 物化。

当最近失败请求集中出现 frontend webui_api_proxy 502,路径为 /api/code-queue-direct/... 的 overview、trace 或 summary,且 code-queue-backend 容器仍在运行时,必须区分“上游进程不可达”和“上游 event loop 被热路径同步工作饿死”。排障顺序是同时查看 /api/frontend-performance/api/performance、容器直连 /health/overview/trace-step curl、docker stats、容器 RestartCount/OOMKilled 和 Code Queue 日志;如果容器直连 /health 也超时,应优先检查实时 output/SSE 发布、archive 读取、transcript 构建、统计计算和远程 Provider 准备/SSH 子进程是否在 active output 追加或任务入队启动时阻塞 event loop,而不是先调整 frontend 渲染或代理超时。涉及 D601 等远程 Provider 时,还要检查 runCodeQueueSsh/开发容器准备是否仍存在同步子进程、无 timeout 的 SSH、无上限 stdout/stderr 或 stale TUN 重建等待;修复后必须在远程准备探针运行期间并发证明容器直连 /health/api/tasks/overview 仍快速返回。

Code Queue task 明明产出最终回复却反复 retry_wait 时,应优先用任务详情里的 latest attempt 字段核查 terminalStatustransportClosedBeforeTerminalappServerExitCodefinalResponseCharsjudge.raw._safetyOverride 和 attempt output。OpenCode 远程任务中,opencode completed status=completed exit=0 加当前 attempt 非空 assistant 输出应对应 terminalStatus=completedtransportClosedBeforeTerminal=false;如果因为缺少 step_finish 事件仍触发 _safetyOverride=terminal_not_completed,说明协议终态归一化有回归。相反,当前 attempt 没有最终 assistant response 时即使 tool/read/bash 证据完整,也必须 retry,不能用旧 task.finalResponse 或 reasoning/tool evidence 代替可见最终回复。