You've probably seen this: Kubernetes probes are green, /health returns 200, CPU isn't on fire - and users are timing out.
Process is up. Just not doing anything useful.
Classic event loop freeze. Something sync and expensive (or a dumb busy loop) grabs the thread, and suddenly timers, HTTP, DB callbacks - all of it waits. Including your health endpoint, because that also needs the loop.
monitorEventLoopDelay() is fine for dashboards. It won't yell at you while the process is stuck, and it definitely won't write a log line from outside the frozen loop.
That's the part that annoyed me enough to build this.
I made @js-ak/watchdog - tiny N-API addon. The monitor runs in C++ on its own thread and writes JSON lines to stderr/file even while the JS event loop is stuck. Your freeze / recovered handlers only run after the loop unblocks. Optional RSS/CPU, optional stack sample.
npm install @js-ak/watchdog
const watchdog = require("@js-ak/watchdog");
watchdog.on("freeze", (e) => console.log(e.event, e.duration_ms));
watchdog.on("recovered", (e) => console.log("back after", e.duration_ms, "ms"));
watchdog.start({
freezeThresholdMs: 1000,
logTarget: "stderr", // or "file" | "both"
source: "payments-api",
});
Real freezes usually aren't while (true). More like giant JSON.parse, a cursed regex, sync crypto/compress, or some dependency doing sync I/O on a hot path. Stuff you only notice after people complain.
Prebuilds for the usual platforms (Node 22+). MIT.
Repo: https://github.com/JS-AK/watchdog
Curious how other people catch loop stalls in prod - and what you'd actually want in the freeze payload. Roast the API if it's wrong.
Top comments (0)