55 lines
2 KiB
TypeScript
55 lines
2 KiB
TypeScript
|
|
import type { Client } from 'ssh2'
|
||
|
|
import { execCommand, toFixedNum } from './common.js'
|
||
|
|
|
||
|
|
function parseCpuLine(cpuLine: string): { total: number; idle: number } | undefined {
|
||
|
|
const parts = cpuLine.trim().split(/\s+/).slice(1).map(Number)
|
||
|
|
if (parts.length < 4 || parts.some((n) => !Number.isFinite(n))) return undefined
|
||
|
|
const idle = parts[3] + (parts[4] ?? 0)
|
||
|
|
const total = parts.reduce((sum, n) => sum + n, 0)
|
||
|
|
return { total, idle }
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function collectCpuMetrics(
|
||
|
|
client: Client,
|
||
|
|
): Promise<{ percent: number | null; cores: number | null; load: [number, number, number] | null }> {
|
||
|
|
let percent: number | null = null
|
||
|
|
let cores: number | null = null
|
||
|
|
let load: [number, number, number] | null = null
|
||
|
|
|
||
|
|
try {
|
||
|
|
const work = (async () => {
|
||
|
|
const first = await execCommand(client, "grep '^cpu ' /proc/stat")
|
||
|
|
await new Promise((r) => setTimeout(r, 500))
|
||
|
|
const [second, loadOut, coresOut] = await Promise.all([
|
||
|
|
execCommand(client, "grep '^cpu ' /proc/stat"),
|
||
|
|
execCommand(client, 'cat /proc/loadavg'),
|
||
|
|
execCommand(client, 'nproc 2>/dev/null || grep -c ^processor /proc/cpuinfo'),
|
||
|
|
])
|
||
|
|
|
||
|
|
const a = parseCpuLine(first.stdout)
|
||
|
|
const b = parseCpuLine(second.stdout)
|
||
|
|
if (a && b) {
|
||
|
|
const totalDiff = b.total - a.total
|
||
|
|
const idleDiff = b.idle - a.idle
|
||
|
|
if (totalDiff > 0) {
|
||
|
|
percent = toFixedNum(Math.min(100, Math.max(0, ((totalDiff - idleDiff) / totalDiff) * 100)))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const loadParts = loadOut.stdout.trim().split(/\s+/).slice(0, 3).map(Number)
|
||
|
|
if (loadParts.length === 3 && loadParts.every(Number.isFinite)) {
|
||
|
|
load = loadParts as [number, number, number]
|
||
|
|
}
|
||
|
|
|
||
|
|
const coreCount = Number(coresOut.stdout.trim())
|
||
|
|
cores = Number.isFinite(coreCount) ? coreCount : null
|
||
|
|
})()
|
||
|
|
|
||
|
|
await Promise.race([work, new Promise((_, reject) => setTimeout(() => reject(new Error('cpu metrics timeout')), 25000))])
|
||
|
|
} catch {
|
||
|
|
// best-effort; leave nulls
|
||
|
|
}
|
||
|
|
|
||
|
|
return { percent, cores, load }
|
||
|
|
}
|