Startup trace (single-file launch performance log for bug reports)
A dedicated log file — startup.log — that captures the timeline of the most recent app launch in one self-contained, human-readable file. It exists so a user reporting "the app feels slow to start" can attach exactly one file to a bug report, and any agent (or outside AI like ChatGPT) reading that pasted file can immediately tell which startup phase was slow without seeing the rest of the codebase.
What it is
A dedicated log file — startup.log — that captures the timeline of the most recent app launch in one self-contained, human-readable file. It exists so a user reporting "the app feels slow to start" can attach exactly one file to a bug report, and any agent (or outside AI like ChatGPT) reading that pasted file can immediately tell which startup phase was slow without seeing the rest of the codebase.
The file lives next to the regular logs:
- Windows:
%APPDATA%/omniscio/logs/startup.log - macOS:
~/Library/Application Support/omniscio/logs/startup.log - Linux:
~/.config/omniscio/logs/startup.log
Each launch overwrites the file. The previous five launches are preserved as startup.log.1 (most recent) through startup.log.5 (oldest).
It is separate from main.log on purpose: main.log is a rolling firehose of every log line during app runtime, useful for debugging behavior during use; startup.log is a pinpoint trace of the launch timeline only, so a user pasting it into a chat with no other context still gets a useful answer.
Where to find it
Settings → Diagnostics — the Startup Trace card with its Reveal Startup Log button.
Reading it from an E2E test
In E2E the app's <userData> is the per-test temp directory, and the fixture deletes that directory
at teardown — so this file is written and then destroyed on every single test. Read it BEFORE
teardown, or it is gone: tests/perf/boot-trace.spec.ts does exactly that and prints the per-phase
breakdown, which is how the ~9.2s migration replay on every test was found on 2026-09-28. That spec
lives in the default-off perf tier, so a normal run does not pay for it.
How it behaves
How to use it
To attach it to a bug report
- Open Settings → Diagnostics. You'll see a card titled Startup Trace with a single button: Reveal Startup Log.
- Click it. Omniscio opens your file manager with
startup.loghighlighted. - Drag the file into the bug report (or paste its contents into a chat).
The file is plain text, ~30–80 lines, opens in any editor. The very top has a self-describing header so an LLM reading a pasted copy with no other context understands what each section means.
To compare against a previous launch
After you click Reveal Startup Log you'll see the rotated backups in the same folder (startup.log.1, startup.log.2, etc.). Open startup.log.1 to see the previous launch. Anything older was rotated out the next time you opened the app.
What you'll see in the file
Four sections, in order:
═══════════════════════════════════════════════════════════════
Omniscio STARTUP TRACE
…self-describing header (version, platform, mode, instance)…
═══════════════════════════════════════════════════════════════
────────────────────────────────────────────────
MAIN PROCESS PHASES (relative to app.whenReady)
────────────────────────────────────────────────
+0ms app.whenReady
+12ms setupLogging done [+12ms phase]
+45ms db init [+33ms phase]
+78ms IPC handlers registered [+33ms phase]
+412ms createWindow returned [+334ms phase]
…
────────────────────────────────────────────────
RENDERER PHASES (relative to mainTsxStart)
────────────────────────────────────────────────
+0ms mainTsxStart
+24ms ipcModuleLoad
+512ms jsLoaded
+520ms bootstrapStart
+820ms bootstrapEnd
+820ms hydrateEnd (visible)
────────────────────────────────────────────────
SLOWEST 5 PHASES (main process)
────────────────────────────────────────────────
1. IPC handlers registered → createWindow returned 334ms
2. setupLogging done → db init 33ms
…
────────────────────────────────────────────────
SUMMARY
────────────────────────────────────────────────
app.whenReady → last-main-mark: 412ms
htmlParsed → renderer-hydrated: 820ms
Renderer report received: 2026-05-08T18:42:11.123Z
The "Slowest 5 Phases" section is the most useful for triage — it sorts phases by gap-from-previous-mark so you can immediately see where time was spent.
How it works
Recorder lives in /src/main/services/startup-trace.ts. It's a small in-memory state machine with three public functions:
initStartupTrace()— called once from /src/main/index.ts right after thestartupT0timestamp. Rotatesstartup.log→startup.log.1→ … →startup.log.5(drops the oldest), then writes the self-describing header banner.recordStartupMark(label, elapsedMs)— called from insidestartupMark()inindex.tsat every existing[perf:startup]instrumentation site. Writes one line to disk per mark with elapsed-since-launch and gap-since-previous-mark.recordRendererTimings(timings)— called by the IPC handlerSTARTUP_TRACE_REPORT_RENDERERafter the renderer has finished its initial bootstrap. Appends the Renderer Phases section, the Slowest 5 Phases summary, and the final Summary block, then marks the trace flushed (idempotent — a renderer reload is a no-op).
The renderer side lives in /src/renderer/src/App.tsx. Inside the Electron-only branch of the initial Promise.all([fetchSettings(), fetchProjects(), fetchSessions(), fetchDividers()]).then(), it reads window.__perfTimings (populated by inline scripts in index.html for htmlParsed, ipcModuleLoad, mainTsxStart, jsLoaded) plus its own bootstrapStart / bootstrapEnd / hydrateEnd measurements, and posts everything in one IPC call to STARTUP_TRACE_REPORT_RENDERER. The handler validates with Zod (startupTraceReportRendererSchema in src/shared/ipc-schemas.ts — caps each field at 5 minutes to prevent runaway values), then forwards to recordRendererTimings.
A 60-second watchdog timer in index.ts calls flushStartupTraceWithoutRenderer() if the renderer never reports — that way a hung renderer still leaves a trace with whatever main-process marks were captured before the hang.
The Reveal Startup Log button in Settings → Diagnostics uses three IPC channels:
STARTUP_TRACE_REPORT_RENDERER— renderer → main, reports__perfTimingsSTARTUP_TRACE_GET_PATH— main returns the absolute path so the renderer can show itSTARTUP_TRACE_REVEAL— main callsshell.showItemInFolder(getStartupLogPath())
All three are registered in /src/main/ipc/log-handlers.ts alongside the existing log/crash-dump handlers.
The recorder's writes are append-only with appendFileSync (synchronous so a crash mid-startup still leaves a partial trace on disk) and every write is wrapped in a try/catch — a failure to write must never block startup itself. If the log directory cannot be created, the trace silently no-ops.
Why a separate file (not just main.log)
main.log is invaluable but the wrong shape for this problem:
- It rotates by size, so a long debug session may have evicted the launch you care about by the time you check.
- It interleaves every log line from every subsystem, making the launch timeline hard to spot.
- It's only meaningful with the rest of the app's context — pasting it into ChatGPT yields wishy-washy answers.
startup.log solves all three: rotation is per-launch (not per-MB), the file contains only launch timings, and the self-describing header makes it interpretable in isolation.
Limitations
- The trace is recorded once per launch. If the user does something inside the app that's slow, the regular
main.logis the right place to look —startup.logwon't have new entries. - The renderer section appears only after the renderer has fully bootstrapped. If the renderer crashes during init, the watchdog flushes the file at the 60-second mark with a
(Renderer timings not received before flush.)note — useful as a diagnostic ("we got to phase X but never made it past renderer hydration"). - The trace shares the same
<userData>/logs/folder asmain.log. The Export Logs button at Settings → Diagnostics → Actions packages everything in that folder plus crash dumps into a single ZIP, so attaching the export ZIP to a bug report includes the startup trace automatically.
Related
- logs-and-debugging.md — the broader logging system (rolling
main.log, in-app Debug Log Viewer, crash dumps, ZIP export). - heap-snapshot-diagnostics.md — the on-demand heap snapshot endpoint for memory bloat investigation; complementary to startup trace (one is launch performance, the other is steady-state memory).
Last verified 2026-10-06