跳转至

诊断导出

OpenClaw 可以为缺陷报告构建本地诊断 .zip:已脱敏的 Gateway 状态、健康状态、日志、配置结构,以及最近的无载荷稳定性事件。

在审查之前,请将诊断包视为机密信息。载荷和凭据在设计上会被脱敏,但诊断包仍会汇总本地 Gateway 日志和主机级运行时状态。

快速开始

openclaw gateway diagnostics export

打印已写入的 zip 路径。选择输出路径:

openclaw gateway diagnostics export --output openclaw-diagnostics.zip

用于自动化:

openclaw gateway diagnostics export --json

聊天命令

所有者可以在任何对话中运行 /diagnostics [note],请求一次本地 Gateway 导出,作为一份可复制粘贴的支持报告:

  1. 发送 /diagnostics,可选附带简短备注(/diagnostics bad tool choice)。
  2. OpenClaw 会发送前言,并要求一次明确的执行审批;该审批会运行 openclaw gateway diagnostics export --json。不要通过全允许规则批准诊断。
  3. 批准后,OpenClaw 会回复本地包路径、清单摘要、隐私说明以及相关会话 ID。

在群聊中,所有者仍可以运行 /diagnostics,但 OpenClaw 会将导出结果、审批提示以及 Codex 会话/线程明细私下发送给所有者。群组只会看到简短状态通知:审批待处理、已确认私有投递、投递待处理或投递已抑制。投递待处理不会触发另一次私有发送。如果不存在私有所有者路由,该命令会要求所有者从 DM 中运行它。

当活动会话使用原生 OpenAI Codex 框架时,同一执行审批还涵盖 OpenClaw 所知道的 Codex 线程的 OpenAI 反馈上传。该上传独立于本地 Gateway zip,并且仅发生在 Codex 框架会话中。审批提示会说明批准也会发送 Codex 反馈,但不会列出 Codex 会话或线程 ID。批准后,回复会列出通道、OpenClaw 会话 ID、Codex 线程 ID,以及已发送到 OpenAI 的线程的本地恢复命令。拒绝或忽略审批会跳过导出、Codex 反馈上传以及 Codex ID 列表。

这使得 Codex 调试循环更短:在某个通道中注意到异常行为,运行 /diagnostics,批准一次,分享报告,然后如果你想自行检查线程,就在本地运行打印出的 codex resume <thread-id> 命令。参见 Codex 框架。

导出内容

  • summary.md:面向支持人员的人类可读概览。
  • diagnostics.json:配置、日志、状态、健康状态和稳定性数据的机器可读摘要。
  • manifest.json:导出元数据和文件列表。
  • 已脱敏的配置结构和非机密配置详情。
  • 已脱敏的日志摘要以及最近已脱敏的日志行。
  • 尽力而为的 Gateway 状态和健康快照。
  • stability/latest.json:最新已持久化的稳定性包(如可用)。

即使 Gateway 不健康,导出仍然有用:如果状态/健康请求失败,本地日志、配置结构和最新稳定性包仍会在可用时收集。

隐私模型

保留:子系统名称、插件 ID、提供商 ID、通道 ID、已配置模式、状态码、持续时间、字节数、队列状态、内存读数、已脱敏日志元数据、已脱敏操作消息、配置结构以及非机密功能设置。

省略或脱敏:聊天文本、提示词、指令、webhook 主体、工具输出、凭据、API 密钥、令牌、cookie、机密值、原始请求/响应主体、账户 ID、消息 ID、原始会话 ID、主机名以及本地用户名。

当日志消息看起来像用户、聊天、提示词或工具载荷文本时,导出仅保留“某条消息已被省略”及其字节数。

WebSocket 断开连接日志

已连接的 webchat 和已认证用户断开连接会在默认 info 级别文件日志中包含 durationMs(以毫秒为单位的连接生命周期)。cause 字段包含 Gateway 记录的关闭原因(如已知);否则省略。heartbeat-timeout 记录 Gateway 的未收到 pong 判定。它不能证明 ping 已到达远程对等方,或对等方导致了传输故障。

心跳超时记录还会在终止前捕获以下事实:

  • pingWriteState:未观察到写入回调时为 pending,本地写入完成后为 completed,写入错误后为 failed。pending 不能证明 ping 未发送;completed 不能证明对等方已接收。
  • lastPongAgeMs:自上次观察到 pong 以来单调递增的已流逝毫秒数;未观察到 pong 时省略。
  • bufferedBytes:超时决策时的本地 WebSocket 聚合缓冲量,而非单个 ping 的投递状态。

命令通道诊断

当排队任务在等待至少其警告阈值(通常为 2 秒)后启动时,会发出 [diagnostic] lane wait exceeded。waitedMs 测量该等待时间;queueAhead 和 activeAhead 描述入队时的竞争情况,而 activeNow 和 queueBehind 描述任务启动时的通道状态。

该警告和 lane task error 都会在调用方提供时包含入队时的 taskKind、sessionKey、runId 和 requesterSessionKey。这些是结构化日志字段,并出现在同一控制台行中,因此无需单独查询注册表即可归因子代理队列竞争。嵌入式运行会提供 spawn、turn 或 cron 作为任务类型;其会话键标识等待或失败的会话,其请求方键在存在时是生成它的父级。排队的定时手动 cron 运行会提供其已接受的运行 ID。未知字段会被省略。身份是诊断溯源信息,而非权限,并且不会改变准入、排序或警告阈值。

稳定性记录器

启用诊断时,Gateway 默认会记录一个有界、无载荷的稳定性流。它捕获操作事实,而非内容。

现有的诊断心跳调试日志包含 nextWakeAtMs,即 Gateway 调度器中最早的待处理唤醒时间,以毫秒为单位的 Unix 时间戳表示(如果没有待处理唤醒,则为 none)。逾期的诊断心跳在休眠后只运行一次;调度器不会重放错过的 tick。

同一心跳还会在事件循环或 CPU 看起来饱和时采样存活状态,发出 diagnostic.liveness.warning 事件,其中包含事件循环延迟、事件循环利用率、CPU 核心比率、活动/等待/排队会话数、当前启动/运行时阶段(如果已知)、最近的阶段跨度以及有界工作标签。当工作处于等待或排队状态、活动工作与持续的事件循环延迟重叠,或 Gateway 报告至少 60 秒的持续性能下降时,这些事件会成为 Gateway warn 级别日志行;否则它们以 debug 级别记录。即使没有正在运行的受跟踪工作,Gateway 的持续性能下降也可能触发警告。其他空闲存活状态采样仍保持为诊断事件,不会升级为警告。

启动阶段会发出 diagnostic.phase.completed 事件,包含墙钟时间和整个进程的 CPU 计时,包括 worker 和原生线程。阶段 CPU 可能包含该阶段之外的并发工作;它不是独占归因。阶段和存活状态事件中的 cpuCoreRatio 以核心当量衡量,可能超过 1。参见 CPU 压力和事件循环延迟。

启用诊断后,持续时间至少一秒的 sessions.patch 和 sessions.patchMany 调用会添加一条 info 级别的 slow session patch 文件日志记录。其 elapsedMs、phaseDurationsMs 和 phaseCounts 用于区分生命周期准入、快照读取、目录准备、投影、提交、运行时确认、副作用和响应工作。记录在可用时会继承请求的诊断跟踪,并包含固定的阶段名称和数量,而不是补丁值或会话键。重复访问阶段会计入数量和总计。并行和嵌套阶段可能重叠,因此它们的总计既不是请求时间的独占分解,也不是 CPU 测量值。

会话协作读取会向感兴趣的诊断监听器发出排队的 diagnostic.phase.completed 事件。session.members.list 和 session.members.listEvidence 分别区分 profiles、evidence 和 projection 等待;session.discussion.info 和 session.discussion.open 报告 provider 时间,包括远程 provider 请求。阶段名称使用方法作为前缀,不包含会话键或响应数据。成员资格证据使用现有的投影 worker 通道,因此完整转录读取不会阻塞它。

启用诊断和警告日志后,持续时间至少一秒的 sessions.create 调用会发出 slow session create。其 elapsedMs 和 phaseDurationsMs 用于区分请求准备、准入、目标发现、worktree 准备、行快照和投影、转录初始化、writer 准入、提交、发布、初始轮次分发以及响应工作。这些是包括等待在内的经过时间,具有固定的阶段名称,不包含会话键或请求值。worktree 准备仅测量响应前所需的工作;已经延迟到初始轮次的供应工作仍属于该轮次的生命周期。

两条相关的 info 级别记录有助于归因缓慢的 worktree 清理: slow managed worktree removal 区分分配准入、回调工作和最终结算,回调内部包含准备、快照、checkout 移除和主体最终化的计时;slow Git ref mutation 区分目录解析、队列等待和排队工作。两者都需要诊断和 info 级别日志,仅在一次持续时间至少一秒的操作结算后发出,并且每个运行时 isolate 每分钟有各自固定的 60 条记录预算,并带有 omittedObservations 计数。它们保留固定的标量字段和现有跟踪,不添加私有路径或新标识。其经过时间区间可以嵌套在 worktreeCleanup 内部,并包含异步等待;它们不是 CPU 或单个子命令计时。字段和缺失记录限制参见 缓慢的 worktree 清理。

SQLite 会话写入警告也会区分 queueWaitMs、writerExecutionMs 和 completionDelayMs。它们分别测量 writer 开始前的时间、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 并不能证明工作在主线程上运行。

停滞的嵌入式运行诊断会在最后一次 bridge 进度看起来是终态(例如原始响应项或响应完成事件)但 Gateway 仍认为嵌入式运行处于活动状态时,标记 terminalProgressStale=true。

检查实时记录器:

openclaw gateway stability
openclaw gateway stability --type payload.large
openclaw gateway stability --json

在致命退出、关闭超时或重启启动失败后,检查最新持久化的 bundle:

openclaw gateway stability --bundle latest

从最新持久化的 bundle 创建诊断 zip:

openclaw gateway stability --bundle latest --export

当事件存在时,持久化的 bundle 位于 ~/.openclaw/logs/stability/ 下。

CPU 配置文件

具有 operator.admin 权限的操作员可以请求 Gateway 主 JavaScript isolate 的一个内存中的配置文件:

openclaw gateway call diagnostics.cpuProfile --params '{}' --timeout 30000 --json

该仅限 Node 的 RPC 请求以 10 ms 间隔采样五秒。它不会打开调试器端口,也不会发送进程信号。调用方断开连接或 Gateway 关闭会取消采集并运行分析器清理。重叠请求会失败,而不是排队。配置文件不会写入磁盘,也不会包含在诊断导出中。

结果包含 V8 CPU 配置文件格式的 profile、requestedDurationMs、actualDurationMs、startBlockedMs、samplingIntervalMicros、redactedNodeCount,以及 sampleLossCount: null,因为 V8 不暴露显式的丢失采样计数。完整结果限制为 1 MiB;更大的配置文件会失败,而不会截断节点或采样。OpenClaw 包内的代码位置使用 openclaw: 路径;Node 内置位置使用 node: 路径。外部路径、eval 标签以及其他无法识别的名称会被脱敏。在已识别位置处的有界代码符号名称会被保留;其语法并不能证明某个计算出的名称是公开的。共享配置文件之前请先审查它。图边、原生带符号脚本 ID 和采样顺序保持完整。V8 可能按时间戳顺序之外的顺序输出采样,因此保留带符号的时间差,以便配置文件查看器重建时间戳并对采样排序。

当事件循环被阻塞时,采样可能超过请求的间隔。响应限制不会限制该延迟期间 V8 的内部分配。配置文件采样描述的是该 isolate,而不是所有进程线程,也不是精确的按函数 CPU 核算。

同步启动 CPU 配置文件会扫描 V8 堆以构建其代码映射。在大型 Gateway 上,每次采集都可能在常规采样开始之前阻塞主事件循环数秒(在 3 GB 堆上已观察到约 3.5 秒)。保持 inspector 域启用或从 Worker 发送请求,都无法避免该主 isolate 工作。startBlockedMs 使用单调时钟测量同步启动调用,排除 Promise 等待、设置、采样时间以及停止/清理。在分析单个采集时,用它来归因采集导致的停顿;它不能从聚合事件循环最大值或百分位数中减去。该测量报告成本;它不会阻止或中断原生停顿。

该 RPC 会拒绝已知的活动 inspector 监听器、配置文件标志、覆盖率收集或任何活动的 Node 跟踪,包括非 CPU 类别。在请求配置文件之前停止跟踪,并在采集期间不要启用跟踪:V8 可能在此 RPC 清理结果之前,将原始配置文件块发送到现有跟踪写入器。该 RPC 无法发现任意的第三方进程内 inspector 会话;不要将其与另一个调试器、分析器、跟踪器或覆盖率所有者一起运行。不可用响应会指明原因以及清理是否失败。如果清理状态仍不确定,则拒绝后续采集;该 RPC 永远不会自动重启 Gateway。

完整堆快照

具有 operator.admin 权限的操作员可以显式采集 Gateway 的主 V8 isolate,包括请求之前分配的对象:

openclaw gateway call diagnostics.heapSnapshot --params '{"reason":"retention baseline"}' --timeout 180000 --json

该仅限 Node 的 RPC 只接受一个可选的 reason(最多 256 个字符),并记录在采集前的警告中。无需配置开关。它以仅所有者可访问的文件权限写入 <state>/diagnostics/heap-<timestamp>.heapsnapshot,并返回 path、sizeBytes、heapUsedBefore、heapUsedAfter(字节)和 elapsedMs(原生采集墙钟时间)。快照内容不会通过 WebSocket 传输,也不会进入诊断导出。Worker isolate 被排除。

在空闲窗口期采集快照。 3 GB 堆快照可能阻塞主线程数十秒。采集期间 V8 可能需要大约两倍于堆的内存;足够的内存和磁盘空间仍由操作员负责。该 RPC 会拒绝超过 6 GiB 的堆、重叠采集,以及原生尝试结束后 60 秒内的另一次采集。这些准入保护不会施加硬性时长、输出大小或内存限制:同步的 writeHeapSnapshot() 一旦启动,就无法被超时、断开连接或关闭中断。客户端超时并不意味着采集已停止;重试前请检查主机目录。失败的采集会在可能时删除部分文件;cleanupFailed 报告删除是否失败。

快照是未脱敏的,可能包含凭据、提示词和私密消息。将它们保留在主机上,单独审查任何传输,并在分析后手动删除。成功的快照会一直保留到被删除;没有自动快照收集或保留任务。

在同一进程中采集两个点,然后从源代码检出中比较它们:

node scripts/heap-snapshot-diff.mjs before.heapsnapshot after.heapsnapshot

该工具按构造器/类报告保留字节数,并按支配者报告最大变化(支配者是所有强根路径经过的对象)。它以流式方式读取输入并顺序分析快照,但仍需要与对象图成比例的内存;请在具有足够内存的独立分析主机上运行大型差异。弱边和快捷边被排除。类总计将同一类的嵌套实例只计算一次;不同类之间的总计可能重叠。对象 ID 只在同一 isolate/进程内匹配。使用 Chrome DevTools 查看交互式保留路径和 V8 特定的弱引用/ephemeron 语义;该脚本是强边图摘要。--json 生成机器可读输出。同样将差异输出视为敏感信息:它包含未脱敏的堆名称。

采样堆配置文件

具有 operator.admin 权限的操作员可以在不采集整个堆快照的情况下,对 Gateway 的主 JavaScript isolate 中的分配进行采样:

openclaw gateway call diagnostics.heapProfile --params '{}' --timeout 30000 --json
openclaw gateway call diagnostics.heapProfile --params '{"durationMs":10000,"samplingIntervalBytes":32768}' --timeout 45000 --json
openclaw gateway call diagnostics.heapProfile --params '{"includeObjectsCollectedByMajorGC":true,"includeObjectsCollectedByMinorGC":true}' --timeout 30000 --json

仅限 Node 的 RPC 默认持续时间为五秒,平均采样间隔为 32 KiB。durationMs 和 samplingIntervalBytes 必须为正整数。超过 30 秒的持续时间会被 限制为 30 秒;低于 4 KiB 的间隔会被限制为 4 KiB。更小的间隔 会采集更多样本,但会带来更高的 CPU 和内存开销。请选择比请求的采集时长更长的 CLI 超时时间。关键内存警告指向此 RPC; 内存压力不会自动启动采集。

可选布尔值 includeObjectsCollectedByMajorGC 和 includeObjectsCollectedByMinorGC 默认均为 false。同时启用两者可保留 采集窗口内已被回收对象的样本,并将临时分配波动 归因到相应位置。保留已回收样本可能会增加分析器的内存使用。

结果包含实际经过的 durationMs、samplingIntervalBytes、 includeObjectsCollectedByMajorGC、includeObjectsCollectedByMinorGC、 heapUsedBefore、heapUsedAfter、rssBefore、rssAfter(所有内存值均以 字节为单位)、redactedNodeCount、unattributedSampleCount、unattributedSampleBytes 以及 truncated。如果存在,profile 包含经过脱敏处理的 V8 采样树 和样本。每个节点的 selfSize 是该调用点处的估算分配字节数;将其后代节点相加可得到包含 字节数。样本通过 nodeId 关联到节点。

V8 可能会采样在构建其自身 profile 期间发生的分配,此时调用点可能已被转换到返回的树中。没有匹配树节点的样本会被省略,并报告在 unattributedSampleCount 和 unattributedSampleBytes 中;原生树大小保持不变。当省略此类引用,或由大小受限的摘要替换树时,truncated 为 true。

完整结果的上限为 1 MiB。当树和样本超过该上限时, truncated 为 true,并且 summary 会替换 profile。摘要条目会合并 相同的调用栈,按叶子优先顺序最多保留八帧,并包含 selfBytes、包含式 totalBytes 以及 count(这些位置被采样的分配数量, 不是精确的对象数量)。条目按 totalBytes 排序, 然后按 selfBytes 排序;排名较低的条目会被省略以符合上限。包含式总计 在不同调用者之间会重叠,因此不要将它们相加。应从较大的 selfBytes 开始,并检查调用栈以识别执行分配的代码。

采样比整个堆快照开销更低,但仍然是近似值。V8 的 默认采样模式会排除在采集结束前已被回收的对象;启用两个 采集标志可包含这些样本。两种模式都不是对 每次分配的精确清单,也不包含采集前已分配的对象。 原生分配、外部缓冲区、其他 isolate 以及其他进程线程 不会被归因,因此采样字节数不一定能解释完整的 RSS 变化。

堆采集和 CPU 采集共享同一个 inspector 所有者:重叠的调用会失败,而不会 排队。两者都使用上述相同的脱敏、运行时冲突检查、取消 和清理规则。不会打开监听器,也不会写入文件。 事件循环停顿可能会延长采集持续时间,并且响应上限不会限制 V8 的内部采样内存。共享前请检查保留的代码符号名称。

实用选项

openclaw gateway diagnostics export \
  --output openclaw-diagnostics.zip \
  --log-lines 5000 \
  --log-bytes 1000000
标志 默认值 描述
--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 关闭 跳过持久化稳定性 bundle 查找。
--json 关闭 打印机器可读的导出元数据。

禁用诊断

诊断默认启用。要禁用稳定性记录器和 诊断事件采集:

{
  diagnostics: {
    enabled: false,
  },
}

禁用诊断会减少 bug 报告详情;它不会影响正常的 Gateway 日志记录。

内存压力事件会记录 RSS、堆、阈值和增长事实 (rss_threshold、heap_threshold、rss_growth),而不会执行 文件系统扫描或写入 OOM 前快照。

在 Node 上,持久数据库工作线程会在已完成操作后,当其已使用堆自上次空闲回收以来增长 32 MiB 时进行垃圾回收。SQLite、 history、transcript 和 reclamation 工作线程请求 512 MiB 的 V8 老年代 限制;显式的进程级 --max-old-space-size 会覆盖 Node 的工作线程 资源限制。这些限制不涵盖原生分配或传输的缓冲区。 内存诊断会按脚本和线程 ID 报告每个被采样的直接工作线程, 包括其堆和外部内存。任务工作线程还会从其 isolate 内部发布 ArrayBuffer 字节数;ArrayBuffer 已包含在外部 内存中,因此不要将这些值相加。其他直接工作线程使用原生堆 统计信息,并且 ArrayBuffer 字节数不可用。嵌套工作线程位于 父注册表的覆盖范围之外。

workerHeapSampledCount 和 workerArrayBuffersSampledCount 显示相对于 workerCount 的覆盖率。对于直接 worker,workerMemoryCoverage 为 complete、partial 或 unavailable。在首次采样之前或采样过期之后,缺失的字节会被省略,而不是报告为零;workerMemoryMissing 标识待处理、过期或不可用的 worker。采样在 60 秒后过期。采样从不等待繁忙的 worker,并且每个传输最多保持一个未决请求。如果 worker 无法处理端口消息,原生 V8 中断仍会刷新其堆和外部计数器;ArrayBuffer 覆盖率在 worker 响应之前保持不可用。内存压力警告包含这些计数器以及外部内存限制注意事项;Node 不会为外部/原生分配提供 worker 限制。

严重内存压力会通过其现有清理所有者回收空闲 worker,包括在诊断事件收集被禁用时。活动操作保留其托管,并且使用结束后恢复通常的 30 分钟数据库保留窗口。存储数据、数据库架构或更新程序均无变化。

当任务池在五分钟内重新创建已因空闲回收的 Worker 时,它会保持一个替换 Worker 处于热备状态,持续五分钟无活动。其他槽位保留其正常空闲超时。Node Code Mode 同样最多保留一个已完成的 Worker 五分钟,仅当其运行时入口和堆限制匹配时才复用。热备任务 worker 仍会就地收集已释放的负载;严重压力、取消、轮换和关闭保留其现有清理路径。无需配置设置。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw