Watchdog & profiler
Two opt-in diagnostics — continuous resource health with local incident detection, and JS Self-Profiling flame graphs around a slow operation.
Both are off by default, for different reasons: the watchdog samples continuously, and the profiler needs a response header you have to set.
Watchdog#
Continuous resource sampling that turns into local incidents — the SDK decides something went wrong on the device, rather than shipping raw samples for a backend to sift.
await ScaleBun.init({ clientKey, apiBaseUrl, features: { watchdog: true }, watchdog: { thresholds: { lagP95Ms: 400 } },});
ScaleBun.watchdog.stats();Incident types#
| Type | What it means |
|---|---|
cpu_spike | Sustained critical compute pressure, or a high event-loop-lag p95. |
main_thread_stall | A single heartbeat gap at or above the threshold. |
jank_burst | N long frames inside a window. |
frozen_frame | One frame at or above the frozen threshold. |
memory_surge | Heap growth rate sustained across two trend intervals. |
memory_leak_suspicion | Monotonic growth across N samples and a minimum total. |
low_memory_pressure | Used heap over the limit ratio. |
Each incident carries a tier — pressure, lag-proxy, loaf, longtask or
memory — recording which signal produced it. Browsers differ wildly here, and
the tier is what lets the dashboard grade a cross-browser claim honestly instead
of pretending Chromium's Compute Pressure and a lag proxy are the same evidence.
Thresholds#
| Threshold | Default |
|---|---|
pressureSustainMs | 6000 |
lagP95Ms | 500 |
mainThreadStallMs | 500 |
jankBurstCount / jankWindowMs | 5 in 10000 |
frozenFrameMs | 700 |
memSurgeBytesPerSec | 6 MiB/s |
leakSamples / leakMinBytes | 10 samples, 50 MiB |
heapUsedRatio | 0.9 |
cooldownMs | 60000 per incident type |
Sampling and caps#
cooldownMs is per incident type, so a noisy CPU signal cannot mask a
memory one. Above that sits a session-wide cap of 10 incidents per 10 minutes;
anything dropped by the cap is counted and surfaced on the health rollup rather
than silently discarded.
| Option | Default |
|---|---|
sampleRate | 1 — deterministic per session |
heartbeatMs | 1000, floored at 250 |
rollupMs | 30000 — one health rollup per window |
memorySampleMs | 20000 |
Profiler#
Sampled call-stack traces via the W3C JS Self-Profiling API, shipped as a performance item and rendered as a flame graph.
await ScaleBun.init({ clientKey, apiBaseUrl, features: { profiler: true } });
if (ScaleBun.profiler.available()) { ScaleBun.profiler.start(10); // sample interval in ms, default 10 await slowOperation(); await ScaleBun.profiler.stop(); // ships the trace}Use it around a known-slow operation rather than leaving it running. Profiling has a real CPU cost, which is why it is opt-in and why the API is a window rather than a mode.
Next#
Performance & network — the always-on RUM lane.
Troubleshooting — diagnostics and counters.