telex-server-monitor-sdk
Version:
SDK for Telex Server Monitor - A lightweight agent that collects and reports server CPU metrics to the Telex monitoring platform
121 lines (120 loc) • 4.79 kB
JavaScript
import si from "systeminformation";
import { logger } from "../utils/logger.js";
import { exec } from "child_process";
import { promisify } from "util";
const execAsync = promisify(exec);
class NetworkMonitor {
lastMeasurement = {};
async networkMetrics() {
try {
const metrics = {
timestamp: Date.now(),
bandwidthUsage: { received: 0, sent: 0, lastMeasurement: 0 },
latency: 0,
packetLoss: 0,
interfaceStats: {},
connectionCount: 0,
};
// Get all metrics concurrently
await Promise.all([
this.getInterfaceStats(metrics),
this.getNetworkQuality(metrics),
this.getConnectionCount(metrics),
]);
return { networkMetrics: metrics };
}
catch (error) {
console.error("Error collecting network metrics:", error);
logger.error(`Failed to get all network metrics: ${error.message}`);
throw error;
}
}
async getInterfaceStats(metrics) {
const interfacesData = await si.networkInterfaces();
const stats = await si.networkStats();
// Ensure interfaces is always an array
const interfaces = Array.isArray(interfacesData)
? interfacesData
: [interfacesData];
let earliestLastTime = metrics.timestamp;
for (const stat of stats) {
const iface = interfaces.find((i) => i.iface === stat.iface);
if (!iface)
continue;
const rxBytes = stat.rx_bytes; // received bytes (download)
const txBytes = stat.tx_bytes; // transferred bytes (upload)
const errors = stat.rx_errors + stat.tx_errors;
metrics.interfaceStats[stat.iface] = {
rxBytes,
txBytes,
errors,
};
// Calculate bandwidth
const now = Date.now();
const last = this.lastMeasurement[stat.iface] || {
rx: rxBytes,
tx: txBytes,
time: now,
};
const timeDiff = (now - last.time) / 1000;
if (timeDiff > 0) {
metrics.bandwidthUsage.received += (rxBytes - last.rx) / timeDiff;
metrics.bandwidthUsage.sent += (txBytes - last.tx) / timeDiff;
// Track the earliest last measurement time across all interfaces
earliestLastTime = Math.min(earliestLastTime, last.time);
}
this.lastMeasurement[stat.iface] = {
rx: rxBytes,
tx: txBytes,
time: now,
};
}
// Set the last measurement timestamp for bandwidth
metrics.bandwidthUsage.lastMeasurement =
earliestLastTime === metrics.timestamp ? 0 : earliestLastTime;
}
async getNetworkQuality(metrics) {
try {
// Use ping for precise packet loss and latency measurement
const { stdout } = await execAsync("ping -c 10 -i 0.2 8.8.8.8"); // 10 pings, 0.2s interval
const lines = stdout.split("\n");
// Extract latency (average from ping)
const timeLine = lines.find((line) => line.includes("rtt min/avg/max"));
if (timeLine) {
const match = timeLine.match(/\/(\d+\.\d+)\//);
if (match)
metrics.latency = parseFloat(match[1]);
}
// Extract packet loss
const lossLine = lines.find((line) => line.includes("packet loss"));
if (lossLine) {
const match = lossLine.match(/(\d+\.\d+|\d+)% packet loss/);
if (match)
metrics.packetLoss = parseFloat(match[1]);
}
}
catch (error) {
console.warn("Failed to measure network quality:", error);
metrics.packetLoss = -1; // Indicate failure
metrics.latency = -1;
logger.error(`Failed to measure network quality: ${error.message}`);
}
}
async getConnectionCount(metrics) {
const connections = await si.networkConnections();
metrics.connectionCount = connections.filter((conn) => conn.state === "ESTABLISHED" || conn.state === "LISTENING").length;
}
}
const monitor = new NetworkMonitor();
async function getNetworkMetrics() {
try {
const metrics = await monitor.networkMetrics();
return metrics;
}
catch (error) {
console.error("Monitoring failed:", error);
logger.error(`Failed to get network metrics: ${error.message}`);
throw error;
}
}
export { getNetworkMetrics };