diff --git a/docs/cli/gateway.md b/docs/cli/gateway.md index 882957bd1c0..5756d2cf76a 100644 --- a/docs/cli/gateway.md +++ b/docs/cli/gateway.md @@ -112,7 +112,7 @@ Inline `--password` can be exposed in local process listings. Prefer `--password ### Startup profiling - Set `OPENCLAW_GATEWAY_STARTUP_TRACE=1` to log phase timings during Gateway startup, including per-phase `eventLoopMax` delay and plugin lookup-table timings for installed-index, manifest registry, startup planning, and owner-map work. -- Set `OPENCLAW_DIAGNOSTICS=1` with `OPENCLAW_DIAGNOSTICS_TIMELINE_PATH=` to write a best-effort JSONL startup diagnostics timeline for external QA harnesses. Add `OPENCLAW_DIAGNOSTICS_EVENT_LOOP=1` to include event-loop samples. +- Set `OPENCLAW_DIAGNOSTICS=timeline` with `OPENCLAW_DIAGNOSTICS_TIMELINE_PATH=` to write a best-effort JSONL startup diagnostics timeline for external QA harnesses. Add `OPENCLAW_DIAGNOSTICS_EVENT_LOOP=1` to include event-loop samples. - Run `pnpm test:startup:gateway -- --runs 5 --warmup 1` to benchmark Gateway startup. The benchmark records first process output, `/healthz`, `/readyz`, startup trace timings, event-loop delay, and plugin lookup-table timing details. ## Query a running Gateway diff --git a/docs/diagnostics/flags.md b/docs/diagnostics/flags.md index 6cafb0efead..50dd6955f23 100644 --- a/docs/diagnostics/flags.md +++ b/docs/diagnostics/flags.md @@ -50,6 +50,28 @@ Disable all flags: OPENCLAW_DIAGNOSTICS=0 ``` +## Timeline artifacts + +The `timeline` flag writes structured startup and runtime timing events for +external QA harnesses: + +```bash +OPENCLAW_DIAGNOSTICS=timeline \ +OPENCLAW_DIAGNOSTICS_TIMELINE_PATH=/tmp/openclaw-timeline.jsonl \ +openclaw gateway run +``` + +`OPENCLAW_DIAGNOSTICS=1`, `OPENCLAW_DIAGNOSTICS=all`, and +`OPENCLAW_DIAGNOSTICS=*` also enable the timeline because they enable every +diagnostics flag. Prefer `timeline` when you only want the JSONL timing +artifact. + +Timeline records use the `openclaw.diagnostics.v1` envelope. Events can include +process ids, phase names, span names, durations, plugin ids, dependency counts, +event-loop delay samples, provider operation names, child-process exit state, +and startup error names/messages. Treat timeline files as local diagnostics +artifacts; review them before sharing outside your machine. + ## Where logs go Flags emit logs into the standard diagnostics log file. By default: diff --git a/src/infra/diagnostics-timeline.test.ts b/src/infra/diagnostics-timeline.test.ts index 5a34ef073ef..96a8a9346c4 100644 --- a/src/infra/diagnostics-timeline.test.ts +++ b/src/infra/diagnostics-timeline.test.ts @@ -17,7 +17,7 @@ async function createTimelineEnv() { tempDirs.push(dir); return { env: { - OPENCLAW_DIAGNOSTICS: "1", + OPENCLAW_DIAGNOSTICS: "timeline", OPENCLAW_DIAGNOSTICS_RUN_ID: "run-1", OPENCLAW_DIAGNOSTICS_ENV: "env-1", OPENCLAW_DIAGNOSTICS_TIMELINE_PATH: join(dir, "nested", "timeline.jsonl"), @@ -43,6 +43,15 @@ describe("diagnostics timeline", () => { const { env } = await createTimelineEnv(); expect(isDiagnosticsTimelineEnabled(env)).toBe(true); + expect(isDiagnosticsTimelineEnabled({ ...env, OPENCLAW_DIAGNOSTICS: "1" })).toBe(true); + expect(isDiagnosticsTimelineEnabled({ ...env, OPENCLAW_DIAGNOSTICS: "all" })).toBe(true); + expect(isDiagnosticsTimelineEnabled({ ...env, OPENCLAW_DIAGNOSTICS: "*" })).toBe(true); + expect( + isDiagnosticsTimelineEnabled({ ...env, OPENCLAW_DIAGNOSTICS: "diagnostics.timeline" }), + ).toBe(true); + expect(isDiagnosticsTimelineEnabled({ ...env, OPENCLAW_DIAGNOSTICS: "telegram.http" })).toBe( + false, + ); expect(isDiagnosticsTimelineEnabled({ ...env, OPENCLAW_DIAGNOSTICS: "0" })).toBe(false); expect(isDiagnosticsTimelineEnabled({ ...env, OPENCLAW_DIAGNOSTICS_TIMELINE_PATH: "" })).toBe( false, diff --git a/src/infra/diagnostics-timeline.ts b/src/infra/diagnostics-timeline.ts index 9c2371d1b46..168b37c7100 100644 --- a/src/infra/diagnostics-timeline.ts +++ b/src/infra/diagnostics-timeline.ts @@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto"; import { appendFileSync, mkdirSync } from "node:fs"; import { dirname } from "node:path"; import { performance } from "node:perf_hooks"; -import { isTruthyEnvValue } from "./env.js"; +import { isDiagnosticFlagEnabled } from "./diagnostic-flags.js"; export const OPENCLAW_DIAGNOSTICS_TIMELINE_SCHEMA_VERSION = "openclaw.diagnostics.v1"; @@ -56,7 +56,8 @@ const createdTimelineDirs = new Set(); export function isDiagnosticsTimelineEnabled(env: NodeJS.ProcessEnv = process.env): boolean { return ( - isTruthyEnvValue(env.OPENCLAW_DIAGNOSTICS) && + (isDiagnosticFlagEnabled("timeline", undefined, env) || + isDiagnosticFlagEnabled("diagnostics.timeline", undefined, env)) && typeof env.OPENCLAW_DIAGNOSTICS_TIMELINE_PATH === "string" && env.OPENCLAW_DIAGNOSTICS_TIMELINE_PATH.trim().length > 0 );