# 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/503/504，路径为 `/api/microservices/code-queue/proxy/...` 的 overview、trace 或 summary，且 k3s/k8s Pod 仍在运行时，必须先运行 `bun scripts/cli.ts microservice diagnostics code-queue`，区分 provider-gateway online、WebSocket HTTP tunnel、k3sctl-adapter、Kubernetes API service proxy 和目标 Service 五段状态。provider tunnel 类失败必须记录响应 body/headers 中的 `requestId`、`stage`、`failureReason`、`x-unidesk-request-id` 和 `x-unidesk-tunnel-error`；如需主动验证错误结构，运行 `bun scripts/cli.ts microservice tunnel-self-test code-queue`，该自测应返回预期失败但 `ok=true` 的诊断结果。随后再继续判断“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 代替可见最终回复。
