Profiler
Overview
A drop-in SSR profiler for SvelteKit. One line in hooks.server.ts, then open /__profiler to see which components, functions, and network calls make a server render slow — with no per-component instrumentation.
On this page
The profiler answers one question: why is this server render slow? It names the slow components, functions, network calls, and allocations — with no per-component instrumentation and nothing to wrap by hand.
It ships as ogygia/profiler, a single SvelteKit handle. It has no dependency on the rest of ogygia, so it works in any SvelteKit app.
The idea
When a page takes seconds to render on the server, the cause is rarely obvious from the outside. It could be a heavy component, a load function awaiting three services one after another, a markdown pass, or the garbage collector. A CPU profiler alone misses the waiting; a request log alone misses the compute.
So the profiler watches the whole Node process during a render and splits the wall clock into everything that happened:
- component SSR and
loadfunctions, - outbound
fetch/httpcalls, tied to the route that made them, - garbage collection and memory growth,
- and time spent waiting rather than computing.
The trick that makes it readable: Svelte compiles every component to a function named after its file. So a V8 sample taken inside a render already knows it is in Cart or ProductGrid. The profiler reads those names straight off the stack — you never wrap a component to measure it.
Install
One line in src/hooks.server.ts, first in the sequence so it times everything below it:
import { sequence } from '@sveltejs/kit/hooks';
import { profiler } from 'ogygia/profiler';
import * as ogygia from 'ogygia/server';
export const handle = sequence(profiler(), ogygia.handle());In production, set a secret so the UI is reachable and gated:
PROFILER_SECRET=some-long-random-stringThen open /__profiler — add ?key=<secret> in production.
What you get
- Always on, near zero cost. Every request is wall-timed and logged, with per-route p50/p95. The CPU is only sampled while you are actively recording.
- On demand. Profile a page by URL, or profile a single request with a header. See Recording.
- A readable report. A wall-clock budget bar (compute vs waiting), an interactive treemap of where the CPU went, sortable component and function tables (with per-call cost and
×Nrepeat counts), a network waterfall, a waiting-by-function breakdown for non-HTTP I/O, memory and GC, and a flame graph. See Reading a report.
What it needs
A Node.js server — adapter-node, vite preview, or most Node hosts. It uses the built-in V8 inspector. On edge runtimes without the inspector (Cloudflare Workers, Deno Deploy) the always-on request log still works, but CPU and heap recording do not; the UI says so plainly. On serverless Node hosts (Lambda, Amplify) recording works, but the instance is ephemeral — download the report as a dump and view it anywhere. See Serverless and edge.
It is production-safe: the UI 404s without the secret, matching is timing-safe, and recorded profiles live only in memory — never on disk.