Files
factorio-clusterio/packages/lib/src/system_collectors.ts
T

173 lines
4.8 KiB
TypeScript

import fs from "fs-extra";
import os from "os";
import util from "util";
import { Gauge } from "./prometheus";
import { SystemInfo } from "./data";
function filterCpuModel() {
const model = os.cpus()[0].model;
// Remove irelevant clutter
return model
.replace(/\(R\)/gi, "®") // Intel CPUs use (R) instead of ®
.replace(/\(TM\)/gi, "™") // Intel and AMD CPUs use (TM) instead of ™
.replace(/ CPU /, " ") // Intel CPUs add redundant "CPU" in the model.
.replace(/ @ [0-9]+\.[0-9]+GHz/, "") // Intel CPUs add GHz after the model.
.replace(/ (Dual|Quad|Six|[0-9]+)-Core Processor/, "") // AMD CPUs add cores after the model.
.trim() // AMD CPUs may have trailing spaces in the model.
;
}
const cpuModel = filterCpuModel();
export const systemInfo = new Gauge(
"system_info",
"Static info about the system.",
{
labels: ["kernel", "machine", "cpu_model", "hostname", "node"],
}
);
systemInfo.labels({
"kernel": os.type(),
"machine": os.machine(),
"cpu_model": cpuModel,
"hostname": os.hostname(),
"node": process.version,
}).set(1);
export const systemCpuSecondsTotal = new Gauge(
"system_cpu_seconds_total",
"Total system CPU time spent in seconds.",
{
labels: ["core"],
callback: function(collector) {
const cpus = os.cpus();
for (let i = 0; i < cpus.length; i++) {
const times = cpus[i].times;
collector.labels(String(i)).set(
(times.user + times.idle + times.irq + times.nice + times.sys) / 1000
);
}
},
},
);
export const systemCpuIdleSecondsTotal = new Gauge(
"system_cpu_idle_seconds_total",
"Total system CPU time spent being idle in seconds.",
{
labels: ["core"],
callback: function(collector) {
const cpus = os.cpus();
for (let i = 0; i < cpus.length; i++) {
const times = cpus[i].times;
collector.labels(String(i)).set(
times.idle / 1000
);
}
},
},
);
export const systemMemoryCapacityBytes = new Gauge(
"system_memory_capacity_bytes",
"Total system memory installed",
{
callback: function(collector) {
collector.set(os.totalmem());
},
},
);
export const systemMemoryAvailableBytes = new Gauge(
"system_memory_available_bytes",
"Total system memory available for use",
{
callback: function(collector) {
collector.set(os.freemem());
},
},
);
export const systemDiskCapacityBytes = new Gauge(
"system_disk_capacity_bytes",
"Size of the filesystem of the current working directory Node.js runs on",
{
callback: async function(collector) {
// statfs was added in Node.js v18.15.0 and may not be present.
// TODO: remove this check once minimum supported Node.js >= v18.15.0.
if (!fs.statfs) {
collector.set(0);
return;
}
const statFsAsync = util.promisify(fs.statfs);
const stats = await statFsAsync(".");
collector.set(stats.blocks * stats.bsize);
},
},
);
export const systemDiskAvailableBytes = new Gauge(
"system_disk_available_bytes",
"Available space on the filesystem of the current working directory Node.js runs on",
{
callback: async function(collector) {
// TODO: remove this check once minimum supported Node.js >= v18.15.0.
if (!fs.statfs) {
collector.set(0);
return;
}
const statFsAsync = util.promisify(fs.statfs);
const stats = await statFsAsync(".");
collector.set(stats.bavail * stats.bsize);
},
},
);
function minZip<T>(a: T[], b: T[]): [T, T][] {
const length = Math.min(a.length, b.length);
const result = [];
result.length = length;
for (let i = 0; i < length; i++) {
result[i] = [a[i], b[i]] as [T, T];
}
return result;
}
let previousTotalCpuMs: number[] = [];
let previousIdleCpuMs: number[] = [];
export async function gatherSystemInfo(id: SystemInfo["id"], canRestart: boolean, restartRequired: boolean) {
const cpus = os.cpus();
const currentTotalCpuMs = cpus.map(({ times }) => times.user + times.idle + times.irq + times.nice + times.sys);
const currentIdleCpuMs = cpus.map(({ times }) => times.idle);
const deltaTotalCpuMs = minZip(previousTotalCpuMs, currentTotalCpuMs).map(([prev, curr]) => curr - prev);
const deltaIdleCpuMs = minZip(previousIdleCpuMs, currentIdleCpuMs).map(([prev, curr]) => curr - prev);
const cpuUsage = minZip(deltaTotalCpuMs, deltaIdleCpuMs).map(([total, idle]) => (total - idle) / total);
previousTotalCpuMs = currentTotalCpuMs;
previousIdleCpuMs = currentIdleCpuMs;
let diskCapacity = 0;
let diskAvailable = 0;
if (fs.statfs) { // TODO: remove this check once minimum supported Node.js >= v18.15.0.
const statFsAsync = util.promisify(fs.statfs);
const stats = await statFsAsync(".");
diskCapacity = stats.blocks * stats.bsize;
diskAvailable = stats.bavail * stats.bsize;
}
return new SystemInfo(
id,
os.hostname(),
process.version,
os.type(),
os.machine(),
cpuModel,
cpuUsage,
os.totalmem(),
os.freemem(),
diskCapacity,
diskAvailable,
canRestart,
restartRequired,
Date.now(),
false,
);
}