diff --git a/public/perf.js b/public/perf.js index 2052cefd..ca0c8ab6 100644 --- a/public/perf.js +++ b/public/perf.js @@ -18,16 +18,33 @@ Promise.resolve(window.apiPerf ? window.apiPerf() : null) ]); + // Also fetch health telemetry + const health = await fetch('/api/health').then(r => r.json()).catch(() => null); + let html = ''; // Server overview html += `
${server.totalRequests}
Total Requests
${server.avgMs}ms
Avg Response
-
${Math.round(server.uptime / 60)}m
Uptime
+
${health ? health.uptimeHuman : Math.round(server.uptime / 60) + 'm'}
Uptime
${server.slowQueries.length}
Slow (>100ms)
`; + // System health (memory, event loop, WS) + if (health) { + const m = health.memory, el = health.eventLoop; + const elColor = el.p95Ms > 500 ? '#ef4444' : el.p95Ms > 100 ? '#f59e0b' : '#22c55e'; + const memColor = m.heapUsed > m.heapTotal * 0.85 ? '#ef4444' : m.heapUsed > m.heapTotal * 0.7 ? '#f59e0b' : '#22c55e'; + html += `

System Health

+
${m.heapUsed}MB
Heap Used / ${m.heapTotal}MB
+
${m.rss}MB
RSS
+
${el.p95Ms}ms
Event Loop p95
+
${el.maxLagMs}ms
EL Max Lag
+
${el.currentLagMs}ms
EL Current
+
${health.websocket.clients}
WS Clients
+
`; + // Cache stats if (server.cache) { const c = server.cache; @@ -37,6 +54,8 @@
${c.hits}
Server Hits
${c.misses}
Server Misses
${c.hitRate}%
Server Hit Rate
+
${c.staleHits || 0}
Stale Hits (SWR)
+
${c.recomputes || 0}
Recomputes
${clientCache}
Client Entries
`; if (client) { diff --git a/server.js b/server.js index 7545c0fb..47f3743e 100644 --- a/server.js +++ b/server.js @@ -185,7 +185,7 @@ app.get('/api/perf', (req, res) => { avgMs: perfStats.requests ? Math.round(perfStats.totalMs / perfStats.requests * 10) / 10 : 0, endpoints: Object.fromEntries(sorted), slowQueries: perfStats.slowQueries.slice(-20), - cache: { size: cache.size, hits: cache.hits, misses: cache.misses, hitRate: cache.hits + cache.misses > 0 ? Math.round(cache.hits / (cache.hits + cache.misses) * 1000) / 10 : 0 }, + cache: { size: cache.size, hits: cache.hits, misses: cache.misses, staleHits: cache.staleHits, recomputes: cache.recomputes, hitRate: cache.hits + cache.misses > 0 ? Math.round(cache.hits / (cache.hits + cache.misses) * 1000) / 10 : 0 }, packetStore: pktStore.getStats(), }); });