@foxglove/ws-protocol-examples
Version:
Foxglove WebSocket protocol examples
163 lines • 6.57 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const ws_protocol_1 = require("@foxglove/ws-protocol");
const commander_1 = require("commander");
const debug_1 = tslib_1.__importDefault(require("debug"));
const os_1 = tslib_1.__importDefault(require("os"));
const ws_1 = require("ws");
const boxen_1 = tslib_1.__importDefault(require("../boxen"));
const setupSigintHandler_1 = require("./util/setupSigintHandler");
const log = (0, debug_1.default)("foxglove:sysmon");
debug_1.default.enable("foxglove:*");
// eslint-disable-next-line @typescript-eslint/promise-function-async
function delay(durationSec) {
return new Promise((resolve) => setTimeout(resolve, durationSec * 1000));
}
function getStats(prevStats) {
let cpuTotal = 0;
let idleTotal = 0;
const cpus = [];
os_1.default.cpus().forEach((cpu, i) => {
const total = cpu.times.idle + cpu.times.user + cpu.times.nice + cpu.times.sys + cpu.times.irq;
let usage = 0;
const prevTimes = prevStats?.cpus[i]?.times;
if (prevTimes) {
const prevTotal = prevTimes.idle + prevTimes.user + prevTimes.nice + prevTimes.sys + prevTimes.irq;
cpuTotal += total - prevTotal;
idleTotal += cpu.times.idle - prevTimes.idle;
usage = 1 - (cpu.times.idle - prevTimes.idle) / (total - prevTotal);
}
cpus.push({ ...cpu, usage });
});
const networkInterfaces = [];
for (const [name, ifaces] of Object.entries(os_1.default.networkInterfaces())) {
if (ifaces) {
networkInterfaces.push(...ifaces.map((iface) => ({ name, ...iface })));
}
}
return {
hostname: os_1.default.hostname(),
platform: os_1.default.platform(),
type: os_1.default.type(),
arch: os_1.default.arch(),
version: os_1.default.version(),
release: os_1.default.release(),
endianness: os_1.default.endianness(),
uptime: os_1.default.uptime(),
freemem: os_1.default.freemem(),
totalmem: os_1.default.totalmem(),
cpus,
total_cpu_usage: 1 - idleTotal / cpuTotal,
loadavg: os_1.default.loadavg(),
networkInterfaces,
};
}
async function main() {
const server = new ws_protocol_1.FoxgloveServer({ name: "sysmon" });
const port = 8765;
const ws = new ws_1.WebSocketServer({
port,
handleProtocols: (protocols) => server.handleProtocols(protocols),
});
(0, setupSigintHandler_1.setupSigintHandler)(log, ws);
ws.on("listening", () => {
void (0, boxen_1.default)(`📡 Server listening on localhost:${port}. To see data, visit:\n` +
`https://app.foxglove.dev/~/view?ds=foxglove-websocket&ds.url=ws://localhost:${port}/`, { borderStyle: "round", padding: 1 }).then(log);
});
ws.on("connection", (conn, req) => {
const name = `${req.socket.remoteAddress}:${req.socket.remotePort}`;
server.handleConnection(conn, name);
});
const ch1 = server.addChannel({
topic: "system_stats",
encoding: "json",
schemaName: "Stats",
schema: JSON.stringify({
type: "object",
properties: {
hostname: { type: "string" },
platform: { type: "string" },
type: { type: "string" },
arch: { type: "string" },
version: { type: "string" },
release: { type: "string" },
endianness: { type: "string" },
uptime: { type: "number" },
freemem: { type: "number" },
totalmem: { type: "number" },
cpus: {
type: "array",
items: {
type: "object",
properties: {
model: { type: "string" },
speed: { type: "number" },
usage: { type: "number" },
times: {
type: "object",
properties: {
user: { type: "number" },
nice: { type: "number" },
sys: { type: "number" },
idle: { type: "number" },
irq: { type: "number" },
},
},
},
},
},
total_cpu_usage: { type: "number" },
loadavg: { type: "array", items: { type: "number" } },
networkInterfaces: {
type: "array",
items: {
type: "object",
properties: {
name: { type: "string" },
family: { type: "string" },
address: { type: "string" },
netmask: { type: "string" },
mac: { type: "string" },
internal: { type: "boolean" },
cidr: { type: "string" },
scopeid: { type: "number" },
},
},
},
},
}),
});
const textEncoder = new TextEncoder();
const INTERVAL_SEC = 0.5;
let controller;
server.on("subscribe", (_chanId) => {
log("starting monitor");
if (controller) {
controller.abort();
throw new Error("already running");
}
controller = new AbortController();
void (async function (signal) {
let lastStats;
while (!signal.aborted) {
const now = BigInt(Date.now()) * 1000000n;
lastStats = getStats(lastStats);
server.sendMessage(ch1, now, textEncoder.encode(JSON.stringify(lastStats)));
await delay(INTERVAL_SEC);
}
})(controller.signal);
});
server.on("unsubscribe", (_chanId) => {
log("stopping monitor");
controller?.abort();
controller = undefined;
});
server.on("error", (err) => {
log("server error: %o", err);
});
}
exports.default = new commander_1.Command("sysmon")
.description("publish CPU, memory, and network info")
.action(main);
//# sourceMappingURL=sysmon.js.map