import { cpus } from "node:os"; import { NS_runtime } from "@x/types/env.runtime"; import { NS_websockets } from "@x/ff-dl/ws"; const DEBUG_MEM = () => (NS_runtime.ENV_VAR_DB?.DEBUG_MEM || process.env.DEBUG_MEM) === "1"; const STATS_CHANNEL = "server-stats"; const STATS_INTERVAL_MS = 2000; let cpu_timer: ReturnType | undefined; let previous_cpu = process.cpuUsage(); let previous_time = process.hrtime.bigint(); const OS = () => process.platform; const mb = (n: number) => Number((n / 1024 / 1024).toFixed(2)); function get_server_stats() { const now = process.hrtime.bigint(); const elapsed_us = Number(now - previous_time) / 1000; const cpu = process.cpuUsage(previous_cpu); const cpu_time_us = cpu.user + cpu.system; // Percentage of one CPU core used by this process. const process_cpu_percent = elapsed_us > 0 ? (cpu_time_us / elapsed_us) * 100 : 0; // Percentage of total machine CPU capacity. const core_count = cpus().length; previous_cpu = process.cpuUsage(); previous_time = now; const memory = process.memoryUsage(); return { timestamp: Date.now(), OS: OS(), cpu: { process_percent: Number(process_cpu_percent.toFixed(2)), machine_percent: Number((process_cpu_percent / core_count).toFixed(2)), cores: core_count }, memory: { rss_mb: mb(memory.rss), heap_used_mb: mb(memory.heapUsed), heap_total_mb: mb(memory.heapTotal), external_mb: mb(memory.external), array_buffers_mb: mb(memory.arrayBuffers) }, process: { uptime_seconds: Math.floor(process.uptime()), pid: process.pid } }; } function broadcast_server_stats() { const stats = get_server_stats(); const payload = JSON.stringify({ type: "SERVER-STATS", data: stats }); for (const client of NS_websockets.ws_clients.values()) { if (!client.channels.has(STATS_CHANNEL)) continue; try { client.socket.send(payload); } catch (error) { if (!NS_runtime.IS_PROD()) { console.error("WS STATS SEND ERROR:", error); } } } } function start_cpu_timer() { if (cpu_timer) return; // Establish a fresh baseline before the first sample. previous_cpu = process.cpuUsage(); previous_time = process.hrtime.bigint(); if (DEBUG_MEM()) { cpu_timer = setInterval(broadcast_server_stats, STATS_INTERVAL_MS); } } function stop_cpu_timer() { if (!cpu_timer) return; clearInterval(cpu_timer); cpu_timer = undefined; } function has_stats_subscribers() { for (const client of NS_websockets.ws_clients.values()) { if (client.channels.has(STATS_CHANNEL)) { return true; } } return false; } function sync_cpu_timer() { if (has_stats_subscribers()) { start_cpu_timer(); } else { stop_cpu_timer(); } } export const NS_server_stats = { get: get_server_stats, start: start_cpu_timer, stop: stop_cpu_timer, sync: sync_cpu_timer };