OpenClaw Gateway 诊断导出(Diagnostics Export)完全指南:构建安全、可分享的 Bug 报告包
本文基于 docs/gateway/diagnostics.md 展开,系统讲解 OpenClaw Gateway 的诊断导出机制:如何用一行命令生成脱敏后的本地 .zip 诊断包、如何在聊天中通过 /diagnostics 命令快速触发导出、导出包内包含哪些文件与字段、隐私与红action模型如何运作,以及稳定性记录器(Stability Recorder)如何为崩溃、重启、内存压力等疑难问题保留现场证据。读完本文,你将能够在 30 秒内产出一份既包含足够技术细节、又不会泄露任何敏感内容的可分享支持报告。
为什么需要诊断导出
OpenClaw Gateway 是一个长期运行的守护进程,承载着渠道连接、会话管理、Agent 执行、WebSocket 传输与 SQLite 写入等大量异步工作。当它出现崩溃、反复重启、内存压力或超大负载时,单靠口头描述往往难以定位问题。OpenClaw 为此内置了一套诊断导出工具:它可以把 Gateway 的状态、健康快照、日志摘要、配置形态以及最近一段无负载(payload-free)的稳定性事件打包成一个本地 .zip 文件,直接用于 Bug 报告或支持请求。
需要特别强调的是,诊断包的处理原则是"像对待机密文件一样对待诊断包":负载(payload)与凭据在设计上会被红action,但包内仍然汇总了本地 Gateway 日志与主机级运行时状态,因此在分享前应自行审阅。
快速开始:一分钟导出诊断包
基础导出
在任意终端中运行:
openclaw gateway diagnostics export
命令会打印写出的 zip 路径。默认输出位置为 $OPENCLAW_STATE_DIR/logs/support/openclaw-diagnostics-<timestamp>-<pid>.zip,其中 <timestamp> 使用诊断文件名时间戳格式,<pid> 是执行导出进程的进程号。
指定输出路径
openclaw gateway diagnostics export --output openclaw-diagnostics.zip
--output 既可以是具体的 zip 文件路径,也可以是一个目录(此时文件会被写入该目录)。
面向自动化
openclaw gateway diagnostics export --json
--json 模式会输出机器可读的导出元数据(如 zip 路径、manifest 摘要等),适合 CI、脚本或 Agent 后续解析。
聊天内导出:/diagnostics 命令
对于不方便打开终端的使用场景,会话所有者(Owner)可以在任意对话中直接发送 /diagnostics 命令,OpenClaw 会把它加工成一份可直接复制粘贴的支持报告。完整流程如下:
- 发送
/diagnostics,可附带一句简短说明,例如/diagnostics bad tool choice。该说明会成为导出记录的上下文备注。 - OpenClaw 先发送一段前置说明(preamble),然后请求一次显式的 exec 审批,审批通过后执行
openclaw gateway diagnostics export --json。官方明确建议:不要用 allow-all 规则来批准诊断导出,应逐次审阅。 - 审批通过后,OpenClaw 回复本地 bundle 路径、manifest 摘要、隐私说明以及相关的 session id。
群聊中的行为
在群聊里,Owner 依然可以运行 /diagnostics,但导出结果、审批提示以及 Codex 会话/线程明细都会私密发送给 Owner。群内成员只能看到简短的状态提示:审批中(approval pending)、私密投递确认(private delivery confirmed)、投递中(delivery pending)或投递被抑制(delivery suppressed)。需要注意:
- "投递中"状态不会触发另一次私密发送,避免重复骚扰 Owner;
- 如果不存在可用的私密 Owner 路由,命令会提示 Owner 改从 DM(私聊)运行。
Codex Harness 会话的特殊行为
当当前活动会话使用原生 OpenAI Codex harness 时,同一次 exec 审批还会同时覆盖一次 OpenAI 反馈上传(针对 OpenClaw 已知的 Codex 线程)。这一点与原文档一致,值得注意的细节包括:
- 该上传与本地 Gateway zip 是两回事,且仅发生在 Codex harness 会话中;
- 审批提示会明确说明"批准同时也意味着发送 Codex 反馈",但不会列出 Codex 会话或线程 id;
- 审批完成后,回复会列出已发送给 OpenAI 的渠道、OpenClaw 会话 id、Codex 线程 id,以及这些线程的本地恢复命令(
codex resume <thread-id>); - 拒绝或忽略审批会一并跳过导出、Codex 反馈上传和 Codex id 列表。
由此形成的调试闭环非常短:在渠道中观察到异常行为 → 运行 /diagnostics → 一次性批准 → 分享报告 → 如想亲自检查线程,本地执行回复中打印的 codex resume <thread-id> 即可。线程检查的完整说明见 Codex harness 命令文档。
导出包内包含什么
导出的 zip 包由以下几部分组成:
| 文件/内容 | 说明 |
|---|---|
summary.md |
面向支持人员的人类可读概览 |
diagnostics.json |
机器可读的配置、日志、状态、健康与稳定性数据摘要 |
manifest.json |
导出元数据与文件清单 |
| 脱敏配置形态 | 配置结构与非敏感配置细节 |
| 脱敏日志摘要 | 最近的日志行(已红action) |
| 尽力而为的状态/健康快照 | Gateway status 与 health 的即时抓取 |
stability/latest.json |
最近一次持久化的稳定性 bundle(如存在) |
关键在于:即使 Gateway 处于不健康状态,导出仍然有价值。如果 status/health 请求失败,本地日志、配置形态和最近的稳定性 bundle 仍会在可用时被收集进包内——这保证了"Gateway 已经崩了"这种最需要诊断的场景也能拿到尽可能多的现场证据。
隐私模型:保留什么、红action什么
保留的数据(Kept)
- 子系统名称、插件 id、provider id、渠道 id、已配置的模式
- 状态码、耗时、字节数、队列状态、内存读数
- 脱敏后的日志元数据、红action后的运维消息
- 配置形态与非敏感的功能开关设置
省略或红action的数据(Omitted / Redacted)
- 聊天文本、提示词、指令、webhook 请求体、工具输出
- 凭据、API key、token、cookie、密钥值
- 原始请求/响应体、账号 id、消息 id、原始会话 id
- 主机名、本地用户名
特别地:当一条日志消息看起来像是用户、聊天、提示词或工具负载文本时,导出只会保留"该消息被省略"这一事实及其字节数,绝不保留内容本身。
从源码实现来看,这一红action模型贯穿于稳定性记录与导出的底层。在 src/logging/diagnostic-stability-bundle.ts 中可以看到:
- 错误消息在写入前会经过
redactSensitiveText(message, { mode: "tools" })处理(见readErrorMessage,L181-L196); - 持久化 bundle 中的
host.hostname恒为常量REDACTED_HOSTNAME(即"<redacted-hostname>",L31),读取与写入两侧都强制覆盖; - 会话文件路径会被消毒:
agents/<agent>/sessions/<session>.jsonl这类路径会被规范化为agents/<agent>/sessions/<session>.jsonl的脱敏形式(见sanitizeSessionEvidencePath,L780-L802); reason字段只允许匹配^[A-Za-z0-9_.:-]{1,120}$的安全码,其余一律归一为"unknown"(normalizeReason,L155-L157)。
这些"写入即脱敏"的约束,从实现层面保证了即使 bundle 被意外分享,敏感内容也无法通过构造畸形数据绕过。
WebSocket 断开日志
对于 webchat 与已认证用户连接,默认的 info 级文件日志中会记录断开事件,包含:
durationMs:连接生命周期(毫秒)。cause:Gateway 记录的关闭原因(当已知时),否则省略。其中heartbeat-timeout表示 Gateway 做出了"错过 pong"的判定。需要清醒认识的是:它不能证明 ping 确实到达了对端,也不能证明是对方造成了传输故障。
当发生 heartbeat-timeout 时,记录还会在终止前捕获以下事实:
pingWriteState:pending(未观察到写回调)、completed(本地写完成)或failed(写入出错)。同样地,pending不代表 ping 一定没发出,completed也不代表对端已收到。lastPongAgeMs:距离最后一次观察到的 pong 所经过的单调时钟毫秒数;若从未观察到 pong 则省略。bufferedBytes:超时判定时刻的本地 WebSocket 缓冲总量——它是聚合缓冲状态,而非某个单独 ping 的投递状态。
这些字段的设计意图非常明确:把"本地能观测到的事实"与"无法观测的网络对端状态"严格区分开,避免诊断者被误导。
稳定性记录器(Stability Recorder)
环形缓冲区的有界记录流
当诊断功能启用时(默认启用),Gateway 会记录一条有界、无负载的稳定性事件流,只捕获运维事实,不捕获内容。从 src/logging/diagnostic-stability.ts 的源码可以看到其实现骨架:
- 采用环形缓冲区(ring buffer)设计,默认容量
1000条事件(DEFAULT_DIAGNOSTIC_STABILITY_CAPACITY = 1000,L10); - 默认快照上限
50条(DEFAULT_DIAGNOSTIC_STABILITY_LIMIT = 50),最大可达容量上限1000; - 每条事件记录
DiagnosticStabilityEventRecord都是严格脱敏的结构化字段(L20-L100):包含seq(序号)、ts(时间戳)、type(事件类型),以及可选的channel、pluginId、durationMs、requestBytes、responseBytes、costUsd、queueDepth、memory、usage等——全部是度量与标识,不含任何聊天内容。
该事件流包含了 Gateway 在运行中产生的一大批诊断事件类型(如 payload.large、diagnostic.memory.pressure 等)。对支持与调优场景,可以通过 --type 精确过滤查看。
实时检查记录器
openclaw gateway stability
openclaw gateway stability --type payload.large
openclaw gateway stability --json
对应的 CLI 参数在 docs/cli/gateway.md 中有完整定义:
--limit <limit>:最多返回最近的事件数,默认25,最大1000;--type <type>:按诊断事件类型过滤,例如payload.large或diagnostic.memory.pressure;--since-seq <seq>:只返回某个诊断序号之后的事件。
检查最新的持久化 bundle
在致命退出(fatal exit)、关闭超时(shutdown timeout)或重启启动失败(restart startup failure)之后,可以检查最新持久化的 bundle:
openclaw gateway stability --bundle latest
--bundle latest(或裸 --bundle)会选取状态目录下最新的 bundle;也可以直接传一个 bundle JSON 路径。--limit、--type、--since-seq 同样适用于 bundle 输出。
从最新 bundle 直接生成诊断 zip
openclaw gateway stability --bundle latest --export
这会基于最新持久化 bundle 直接产出一个可分享的支持诊断 zip(可配合 --output 指定输出路径)。
持久化 bundle 的存放位置为 ~/.openclaw/logs/stability/(当有事件时),文件名形如 openclaw-stability-<timestamp>-<pid>-<reason>.json。从 src/logging/diagnostic-stability-bundle.ts 可以看到这一机制的实现细节:
- bundle 版本号
DIAGNOSTIC_STABILITY_BUNDLE_VERSION = 1(L23),读取时对不支持的版本直接拒绝; - 单文件上限
MAX_DIAGNOSTIC_STABILITY_BUNDLE_BYTES = 5 * 1024 * 1024(5 MB,L26),防止超大 bundle 被误读; - 默认保留最近
20个 bundle,写入新 bundle 时会自动裁剪更旧的(pruneOldBundles,L872-L903); - bundle 通过
replaceFileAtomicSync原子写入,目录权限0o700、文件权限0o600,确保即使包含敏感过程信息也不被其他用户读取; - 通过
installDiagnosticStabilityFatalHook(L982-L992)注册致命错误钩子,使 fatal exit 时自动落盘 bundle。
心跳采样:事件循环与 CPU 压力告警
同一个心跳机制还会在事件循环或 CPU 看似饱和时采样存活状态(liveness),发出 diagnostic.liveness.warning 事件,携带:
- 事件循环延迟(event-loop delay)与事件循环利用率(event-loop utilization)
- CPU 核心占比(CPU-core ratio)
- 活跃/等待/排队中的会话数(active/waiting/queued session counts)
- 当前启动/运行阶段(当已知时)与最近的阶段耗时区间
- 有界的工作标签(bounded work labels)
这些事件何时升级为 Gateway 的 warn 级日志行,有一套明确的判定规则:
- 有工作处于等待或排队状态时;
- 活跃工作与持续性的事件循环延迟重叠时;
- Gateway 报告至少 60 秒的持续劣化时(即使没有跟踪到活跃工作,持续劣化也可能告警)。
其余空闲时的 liveness 采样仅作为诊断事件保留,不升级为告警。
启动阶段计时
启动阶段会发出 diagnostic.phase.completed 事件,携带墙钟时间(wall-clock)与整个进程的 CPU 计时,包括 worker 线程与原生线程。需要注意两点:
- 阶段 CPU 可能包含该阶段之外的并发工作,它不是排他性归因;
- 阶段与 liveness 事件中的
cpuCoreRatio以核心当量为单位,可以大于1(一个核完全占满为1,并行工作可超过)。详细解释见 Gateway 健康检查文档中的 CPU 压力与事件循环延迟。
慢会话补丁(slow session patch)诊断
当诊断启用时,任何持续至少 1 秒的 sessions.patch 与 sessions.patchMany 调用都会在文件日志中追加一条 info 级的 slow session patch 记录,包含:
elapsedMs:总耗时;phaseDurationsMs与phaseCounts:区分生命周期准入(lifecycle admission)、快照读取、目录准备、投影(projection)、提交、运行时确认、副作用(effects)与响应工作。
这些记录继承请求的诊断 trace(当可用时),并且只包含固定的阶段名称与数字,绝不包含补丁值或会话键。重复访问同一阶段会计入次数与总量;并行与嵌套阶段可以重叠,因此它们的总量既不是请求时间的排他性分解,也不是 CPU 测量值。
SQLite 会话写入警告的三段式耗时
SQLite 会话写入警告会进一步拆分:
queueWaitMs:从请求入队到 writer 开始执行的时间;writerExecutionMs:在 writer 通道内的工作与等待时间;completionDelayMs:writer 执行完毕后到调用方恢复的时间。
需要注意,writer 执行时间不是 SQLite 事务锁的持有时间——原生事务锁的等待与持有告警是独立记录的。在进入 writer 之前就被拒绝的写入会省略这三个字段。这个拆分只出现在文件日志中,不会进入 Gateway RPC 的 Prometheus 聚合直方图。
这些警告还包含 writer 的 pid、Node threadId 与 isMainThread;回收回调还会记录 reclamationKind,以及在创建了 Worker 时捕获的 workerThreadId。在同一进程生命周期内,可以把警告中的 pid/workerThreadId 与 agent-database-open 警告中的 pid/threadId 匹配,从而识别出被等待的 Worker——这建立的是关联关系,不是 CPU 归因或 Worker 生命周期的分解;缺失 Worker id 也不能证明工作运行在主线程上。
停滞的内嵌运行标记
停滞的内嵌运行(embedded run)诊断会在以下情况标记 terminalProgressStale=true:最后一次 bridge 进度看起来已经终结(例如原始响应项或响应完成事件),但 Gateway 仍认为内嵌运行处于活跃状态。这是识别"进度卡死但进程未结束"类问题的重要信号。
实用选项:完整参数表
组合使用限制日志规模的典型命令:
openclaw gateway diagnostics export \
--output openclaw-diagnostics.zip \
--log-lines 5000 \
--log-bytes 1000000
完整参数表如下(与 docs/cli/gateway.md 一致):
| Flag | 默认值 | 说明 |
|---|---|---|
--output <path> |
$OPENCLAW_STATE_DIR/logs/support/openclaw-diagnostics-<timestamp>-<pid>.zip |
写入指定的 zip 路径(或目录) |
--log-lines <count> |
5000 |
包含的最大脱敏日志行数 |
--log-bytes <bytes> |
1000000 |
检查的最大日志字节数 |
--url <url> |
- | 用于状态/健康快照的 Gateway WebSocket URL |
--token <token> |
- | 用于状态/健康快照的 Gateway token |
--password <password> |
- | 用于状态/健康快照的 Gateway 密码 |
--timeout <ms> |
3000 |
状态/健康快照超时(毫秒) |
--no-stability-bundle |
off | 跳过持久化稳定性 bundle 查找 |
--json |
off | 输出机器可读的导出元数据 |
其中 --url/--token/--password 用于在 CLI 与 Gateway 分离部署(例如远程 Gateway)时,让导出命令能够连接到 Gateway 抓取 status/health 快照;--timeout 控制这一抓取的最长等待时间。--log-lines 与 --log-bytes 是一对配合使用的护栏:前者限制写入 zip 的日志行数,后者限制扫描日志文件时读取的最大字节量,两者共同防止超大日志拖垮导出过程。
禁用诊断
诊断功能默认开启。如需关闭稳定性记录器与诊断事件收集,在配置文件中设置:
{
diagnostics: {
enabled: false,
},
}
需要明确的是:禁用诊断只降低 Bug 报告的信息丰富度,不影响正常的 Gateway 日志。普通日志照常输出,只是不再收集稳定性事件流与诊断事件。
另外,内存压力事件会记录 RSS、堆、阈值与增长事实(rss_threshold、heap_threshold、rss_growth),但不会执行文件系统扫描,也不会在 OOM 前写入快照——这是有意的设计取舍,避免在内存紧张的临界状态下再引入额外的 IO 开销。相关实现可见 src/logging/diagnostic-stability-bundle.ts 中 evidence.memoryPressure 的结构(L70-L82)。
典型排查工作流
综合以上机制,一个面向崩溃/重启/内存压力问题的标准排查流程可以这样组织:
- 现场直接导出:在问题可复现时,立即运行
openclaw gateway diagnostics export --json,拿到完整现场包。 - 检查稳定性记录:运行
openclaw gateway stability --type diagnostic.memory.pressure(或按需换成其他类型),查看最近的内存压力/负载事件,确认是否存在rss_growth、queueDepth增长等先兆。 - 利用崩溃现场:如果 Gateway 已 fatal exit,直接
openclaw gateway stability --bundle latest读取崩溃瞬间自动落盘的 bundle,再--export转为可分享 zip。 - 核对健康与日志:结合 Gateway 健康检查 中的事件循环/CPU 压力指标,以及 日志 中的慢会话补丁、SQLite 写入警告字段,交叉定位瓶颈。
- 分享前自检:打开
summary.md与manifest.json,确认没有意外的原始会话 id 或主机名后再外发。
相关文档导航
- Gateway 健康检查(Health Checks) — 事件循环延迟、CPU 核心占比等指标的权威定义
- Gateway CLI —
gateway diagnostics export与gateway stability的完整参数说明 - Gateway 协议:RPC 方法族 —
sessions.patch等底层 RPC 的协议背景 - 日志(Logging) — 文件日志、级别与脱敏的整体设计
- OpenTelemetry 导出 — 与本地 zip 导出相互独立的另一条流式诊断通道,用于把诊断数据实时投递到 Collector
- Codex harness 命令 —
/diagnostics在 Codex harness 会话中的线程检查与反馈上传细节
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00