From 1c1d2e7aaa2e57268dda253e8c58dada670a1e8e Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 7 Jan 2026 15:01:46 +0000 Subject: [PATCH] feat: add event loop block notifications and env flag Add colored console warnings when the event loop is blocked and wire a feature flag to enable/disable notifications- Introduce notifyEventLoopBlocked() in eventLoopMonitor.server.ts to log a colored warning with blocked and async type. - Call notifyEventLoopBlocked() when an event-loop stall is detected. - Add EVENT_LOOP_MONITOR_NOTIFY_ENABLED to env schema with a default of "0" so notifications are off by default. This makes it easier to spot long event-loop stalls during development or when notifications are explicitly enabled. --- apps/webapp/app/env.server.ts | 1 + apps/webapp/app/eventLoopMonitor.server.ts | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/apps/webapp/app/env.server.ts b/apps/webapp/app/env.server.ts index 1cc0db0bf0..095abd45ca 100644 --- a/apps/webapp/app/env.server.ts +++ b/apps/webapp/app/env.server.ts @@ -1260,6 +1260,7 @@ const EnvironmentSchema = z EVENT_LOOP_MONITOR_THRESHOLD_MS: z.coerce.number().int().default(100), EVENT_LOOP_MONITOR_UTILIZATION_INTERVAL_MS: z.coerce.number().int().default(1000), EVENT_LOOP_MONITOR_UTILIZATION_SAMPLE_RATE: z.coerce.number().default(0.05), + EVENT_LOOP_MONITOR_NOTIFY_ENABLED: z.string().default("0"), VERY_SLOW_QUERY_THRESHOLD_MS: z.coerce.number().int().optional(), diff --git a/apps/webapp/app/eventLoopMonitor.server.ts b/apps/webapp/app/eventLoopMonitor.server.ts index b86ea3d31a..0a1b33690b 100644 --- a/apps/webapp/app/eventLoopMonitor.server.ts +++ b/apps/webapp/app/eventLoopMonitor.server.ts @@ -9,6 +9,23 @@ import { signalsEmitter } from "./services/signals.server"; const THRESHOLD_NS = env.EVENT_LOOP_MONITOR_THRESHOLD_MS * 1e6; +// ANSI color codes for terminal output +const RED = "\x1b[31m"; +const YELLOW = "\x1b[33m"; +const RESET = "\x1b[0m"; + +function notifyEventLoopBlocked(timeMs: number, asyncType: string): void { + if (env.EVENT_LOOP_MONITOR_NOTIFY_ENABLED !== "1") { + return; + } + + console.warn( + `${RED}⚠️ Event loop blocked${RESET} for ${YELLOW}${timeMs.toFixed( + 1 + )}ms${RESET} (${asyncType})` + ); +} + const cache = new Map(); function init(asyncId: number, type: string, triggerAsyncId: number, resource: any) { @@ -66,6 +83,8 @@ function after(asyncId: number) { ); newSpan.end(); + + notifyEventLoopBlocked(time, cached.type); } }