7.1 KiB
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 中的 createHourlyJsonlWriter;LOG_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_BYTES、UNIDESK_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_FILE 和 UNIDESK_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 终态回报的任务做超时处理。超时任务转为 failed,result 中保留 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 这类控制面页面出现 core_proxy、GET /api/microservices/code-queue/proxy/api/tasks/overview、POST /api/microservices/code-queue/proxy/api/tasks/<id>/read 等超过 1s 的慢操作时,应保留优化前后的性能面板证据,并同时记录 live API 耗时、容器内存、/health 存储摘要和是否仍通过 PostgreSQL/append-only archive 重建历史数据。短 TTL cache、warmup 或页面内存缓存只能作为重复请求抖动保护,性能证据必须证明数据库索引/聚合、分页和渐进式披露本身已把核心路径降到目标内,不能用长缓存遮蔽慢 SQL 或全量 JSON 物化。
当最近失败请求集中出现 frontend core_proxy 502,路径为 /api/microservices/code-queue/proxy/... 的 overview、trace 或 summary,且 k3s/k8s Pod 仍在运行时,必须区分“Kubernetes API service proxy 不可达”“Code Queue 进程不可达”和“Code Queue event loop 被热路径同步工作饿死”。排障顺序是同时查看 /api/frontend-performance、/api/performance、k3sctl-adapter /api/control-plane、Kubernetes Pod /live、/health、overview/trace-step curl、kubectl top pod 或 Docker stats、容器 RestartCount/OOMKilled 和 Code Queue 日志;如果 Pod 内 /health 也超时,应优先检查实时 output 发布、archive 读取、transcript 构建、统计计算、启动维护、历史 OA backfill 和远程 Provider 准备/SSH 子进程是否阻塞 event loop,而不是先调整 frontend 渲染或代理超时。Code Queue 默认不得在启动时自动执行历史 OA backfill 或通知表索引维护;显式 backfill 必须作为运维动作记录,并在运行期间并发证明 /live、/health 与 /api/tasks/overview 仍快速返回。涉及 D601 等远程 Provider 时,还要检查 runCodeQueueSsh/开发容器准备是否仍存在同步子进程、无 timeout 的 SSH、无上限 stdout/stderr 或 stale TUN 重建等待;修复后必须在远程准备探针运行期间并发证明 Pod /health 与 /api/tasks/overview 仍快速返回。
Code Queue task 明明产出最终回复却反复 retry_wait 时,应优先用任务详情里的 latest attempt 字段核查 terminalStatus、transportClosedBeforeTerminal、appServerExitCode、finalResponseChars、judge.raw._safetyOverride 和 attempt output。OpenCode 远程任务中,opencode completed status=completed exit=0 加当前 attempt 非空 assistant 输出应对应 terminalStatus=completed、transportClosedBeforeTerminal=false;如果因为缺少 step_finish 事件仍触发 _safetyOverride=terminal_not_completed,说明协议终态归一化有回归。相反,当前 attempt 没有最终 assistant response 时即使 tool/read/bash 证据完整,也必须 retry,不能用旧 task.finalResponse 或 reasoning/tool evidence 代替可见最终回复。