Monitor: 1-second CPU/mem/net updates via fast endpoint
Split slow cached monitor endpoint from the live stats. monitor_fast.php reads /proc/stat, /proc/meminfo, ZFS arcstats, and /proc/net/dev directly — no cache wrapper, 87ms response. Docker/vm/swap pulled from last full cache so mem card stays complete. Full monitor poll stays at 2s for everything else (GPU, containers, storage, etc).
This commit is contained in:
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once dirname(__DIR__) . '/include/config.php';
|
||||||
|
require_once dirname(__DIR__) . '/include/common.php';
|
||||||
|
|
||||||
|
// Pull slow fields (docker/vm/top_procs/swap) from last full cache — stale is fine,
|
||||||
|
// these don't change on a per-second basis. 600s window so the card stays populated
|
||||||
|
// even if the full cache writer is temporarily behind.
|
||||||
|
$_full = vv_cache_read('monitor', 600);
|
||||||
|
$_dockerKb = $_full['mem']['docker_kb'] ?? 0;
|
||||||
|
$_vmKb = $_full['mem']['vm_kb'] ?? 0;
|
||||||
|
$_topProcs = $_full['mem']['top_procs'] ?? [];
|
||||||
|
$_swapTotal = $_full['mem']['swap_total_kb'] ?? 0;
|
||||||
|
$_swapUsed = $_full['mem']['swap_used_kb'] ?? 0;
|
||||||
|
|
||||||
|
// Fast memory: /proc/meminfo + ZFS ARC (no docker stats, no GQL)
|
||||||
|
$_memRaw = [];
|
||||||
|
foreach (file('/proc/meminfo') ?: [] as $_l) {
|
||||||
|
if (preg_match('/^(\w+):\s+(\d+)/', $_l, $_m)) $_memRaw[$_m[1]] = (int)$_m[2];
|
||||||
|
}
|
||||||
|
$_totalKb = $_memRaw['MemTotal'] ?? 0;
|
||||||
|
$_freeKb = $_memRaw['MemAvailable'] ?? 0;
|
||||||
|
$_arcKb = 0;
|
||||||
|
foreach (@file('/proc/spl/kstat/zfs/arcstats') ?: [] as $_l) {
|
||||||
|
if (preg_match('/^size\s+\d+\s+(\d+)/', $_l, $_m)) { $_arcKb = (int)($_m[1] / 1024); break; }
|
||||||
|
}
|
||||||
|
if (!$_swapTotal) {
|
||||||
|
$_swapTotal = $_memRaw['SwapTotal'] ?? 0;
|
||||||
|
$_swapUsed = max(0, ($_memRaw['SwapTotal'] ?? 0) - ($_memRaw['SwapFree'] ?? 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'cpu' => vv_cpu_per_core(),
|
||||||
|
'mem' => [
|
||||||
|
'total_kb' => $_totalKb,
|
||||||
|
'free_kb' => $_freeKb,
|
||||||
|
'used_kb' => max(0, $_totalKb - $_freeKb),
|
||||||
|
'arc_kb' => $_arcKb,
|
||||||
|
'docker_kb' => $_dockerKb,
|
||||||
|
'vm_kb' => $_vmKb,
|
||||||
|
'system_kb' => max(0, $_totalKb - $_freeKb - $_arcKb - $_dockerKb - $_vmKb),
|
||||||
|
'swap_total_kb' => $_swapTotal,
|
||||||
|
'swap_used_kb' => $_swapUsed,
|
||||||
|
'top_procs' => $_topProcs,
|
||||||
|
],
|
||||||
|
'net' => vv_network_stats(),
|
||||||
|
'ts' => time(),
|
||||||
|
]);
|
||||||
@@ -898,62 +898,7 @@ function vvPollMonitor() {
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// ── CPU ─────────────────────────────────────────────────────────────────
|
// ── CPU title (core count + temp — watchdog data only comes from full poll) ──
|
||||||
const cpu = d.cpu ?? {};
|
|
||||||
document.getElementById('vv-cpu-body').innerHTML = vvRenderCpu(cpu);
|
|
||||||
|
|
||||||
vvCpuHistory.push(cpu.overall ?? 0);
|
|
||||||
if (vvCpuHistory.length > VV_HIST_MAX) vvCpuHistory.shift();
|
|
||||||
vvDrawChart(document.getElementById('vv-cpu-canvas'), vvCpuHistory, '#4caf50', 'rgba(76,175,80,0.18)', true);
|
|
||||||
|
|
||||||
// ── Memory ──────────────────────────────────────────────────────────────
|
|
||||||
const mem = d.mem ?? {};
|
|
||||||
document.getElementById('vv-memory-body').innerHTML = vvRenderMemory(mem);
|
|
||||||
|
|
||||||
// ── Network ─────────────────────────────────────────────────────────────
|
|
||||||
const net = d.net ?? {};
|
|
||||||
if (net.available) {
|
|
||||||
const rx = net.rx_bps ?? 0;
|
|
||||||
const tx = net.tx_bps ?? 0;
|
|
||||||
const linkMbps = net.speed_mbps ?? 0;
|
|
||||||
const linkLabel = linkMbps >= 1000 ? (linkMbps / 1000) + ' Gb/s' : linkMbps ? linkMbps + ' Mb/s' : '—';
|
|
||||||
const linkBps = linkMbps * 1e6;
|
|
||||||
|
|
||||||
vvNetRxHistory.push(rx);
|
|
||||||
vvNetTxHistory.push(tx);
|
|
||||||
if (vvNetRxHistory.length > VV_HIST_MAX) vvNetRxHistory.shift();
|
|
||||||
if (vvNetTxHistory.length > VV_HIST_MAX) vvNetTxHistory.shift();
|
|
||||||
|
|
||||||
const maxSeen = Math.max(...vvNetRxHistory, ...vvNetTxHistory, 1);
|
|
||||||
const maxBps = maxSeen * 1.25; // auto-scale with 25% headroom
|
|
||||||
const peakRx = Math.max(...vvNetRxHistory, 0); // window peak (last 2 min)
|
|
||||||
const peakTx = Math.max(...vvNetTxHistory, 0);
|
|
||||||
|
|
||||||
const ipRows = [
|
|
||||||
net.local_ip ? `<div><span style="color:#555;font-size:9px;">LAN </span>${net.local_ip}</div>` : '',
|
|
||||||
net.ext_ip ? `<div><span style="color:#555;font-size:9px;">EXT </span>${net.ext_ip}</div>` : '',
|
|
||||||
net.ts_ip ? `<div><span style="color:#555;font-size:9px;">TS </span>${net.ts_ip}</div>` : '',
|
|
||||||
].filter(Boolean).join('');
|
|
||||||
|
|
||||||
document.getElementById('vv-network-body').innerHTML =
|
|
||||||
`<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:8px;">
|
|
||||||
<div>
|
|
||||||
<div style="font-size:12px;color:#888;margin-bottom:4px;">${net.iface} · ${linkLabel}</div>
|
|
||||||
<div style="display:flex;gap:16px;font-size:13px;font-weight:600;">
|
|
||||||
<span><span style="color:#4caf50;font-size:9px;margin-right:4px;">━ IN (RX)</span><span style="color:#4caf50;">${vvFmtBps(rx)}</span><span style="color:#444;font-size:9px;font-weight:400;margin-left:4px;">peak ${vvFmtBps(peakRx)}</span></span>
|
|
||||||
<span><span style="color:#ff9800;font-size:9px;margin-right:4px;">━ OUT (TX)</span><span style="color:#ff9800;">${vvFmtBps(tx)}</span><span style="color:#444;font-size:9px;font-weight:400;margin-left:4px;">peak ${vvFmtBps(peakTx)}</span></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="text-align:right;font-size:11px;color:#aaa;line-height:1.6;">${ipRows}</div>
|
|
||||||
</div>
|
|
||||||
<canvas id="vv-net-canvas" style="width:100%;height:110px;display:block;"></canvas>`;
|
|
||||||
|
|
||||||
vvDrawNetChart(document.getElementById('vv-net-canvas'), vvNetRxHistory, vvNetTxHistory, maxBps);
|
|
||||||
} else {
|
|
||||||
document.getElementById('vv-network-body').innerHTML = '<p style="color:#555;font-style:italic">No network interface detected</p>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── CPU title (core count + temp to its right) ────────────────────────────
|
|
||||||
const _cpuTitleEl = document.getElementById('vv-cpu-title');
|
const _cpuTitleEl = document.getElementById('vv-cpu-title');
|
||||||
if (_cpuTitleEl) {
|
if (_cpuTitleEl) {
|
||||||
const _cpuTemp = d.watchdog?.stability?.cpu_temp ?? null;
|
const _cpuTemp = d.watchdog?.stability?.cpu_temp ?? null;
|
||||||
@@ -1634,6 +1579,69 @@ function vvPollMonitor() {
|
|||||||
vvPollMonitor();
|
vvPollMonitor();
|
||||||
setInterval(vvPollMonitor, 2000);
|
setInterval(vvPollMonitor, 2000);
|
||||||
|
|
||||||
|
// ── Fast poll: CPU, memory, network — 1-second live updates ──────────────────
|
||||||
|
function vvPollFast() {
|
||||||
|
fetch('/plugins/varaverk/api/monitor_fast.php')
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(d => {
|
||||||
|
// CPU
|
||||||
|
const cpu = d.cpu ?? {};
|
||||||
|
document.getElementById('vv-cpu-body').innerHTML = vvRenderCpu(cpu);
|
||||||
|
vvCpuHistory.push(cpu.overall ?? 0);
|
||||||
|
if (vvCpuHistory.length > VV_HIST_MAX) vvCpuHistory.shift();
|
||||||
|
vvDrawChart(document.getElementById('vv-cpu-canvas'), vvCpuHistory, '#4caf50', 'rgba(76,175,80,0.18)', true);
|
||||||
|
|
||||||
|
// Memory
|
||||||
|
document.getElementById('vv-memory-body').innerHTML = vvRenderMemory(d.mem ?? {});
|
||||||
|
|
||||||
|
// Network
|
||||||
|
const net = d.net ?? {};
|
||||||
|
if (net.available) {
|
||||||
|
const rx = net.rx_bps ?? 0;
|
||||||
|
const tx = net.tx_bps ?? 0;
|
||||||
|
const linkMbps = net.speed_mbps ?? 0;
|
||||||
|
const linkLabel = linkMbps >= 1000 ? (linkMbps / 1000) + ' Gb/s' : linkMbps ? linkMbps + ' Mb/s' : '—';
|
||||||
|
|
||||||
|
vvNetRxHistory.push(rx);
|
||||||
|
vvNetTxHistory.push(tx);
|
||||||
|
if (vvNetRxHistory.length > VV_HIST_MAX) vvNetRxHistory.shift();
|
||||||
|
if (vvNetTxHistory.length > VV_HIST_MAX) vvNetTxHistory.shift();
|
||||||
|
|
||||||
|
const maxSeen = Math.max(...vvNetRxHistory, ...vvNetTxHistory, 1);
|
||||||
|
const maxBps = maxSeen * 1.25;
|
||||||
|
const peakRx = Math.max(...vvNetRxHistory, 0);
|
||||||
|
const peakTx = Math.max(...vvNetTxHistory, 0);
|
||||||
|
|
||||||
|
const ipRows = [
|
||||||
|
net.local_ip ? `<div><span style="color:#555;font-size:9px;">LAN </span>${net.local_ip}</div>` : '',
|
||||||
|
net.ext_ip ? `<div><span style="color:#555;font-size:9px;">EXT </span>${net.ext_ip}</div>` : '',
|
||||||
|
net.ts_ip ? `<div><span style="color:#555;font-size:9px;">TS </span>${net.ts_ip}</div>` : '',
|
||||||
|
].filter(Boolean).join('');
|
||||||
|
|
||||||
|
document.getElementById('vv-network-body').innerHTML =
|
||||||
|
`<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:8px;">
|
||||||
|
<div>
|
||||||
|
<div style="font-size:12px;color:#888;margin-bottom:4px;">${net.iface} · ${linkLabel}</div>
|
||||||
|
<div style="display:flex;gap:16px;font-size:13px;font-weight:600;">
|
||||||
|
<span><span style="color:#4caf50;font-size:9px;margin-right:4px;">━ IN (RX)</span><span style="color:#4caf50;">${vvFmtBps(rx)}</span><span style="color:#444;font-size:9px;font-weight:400;margin-left:4px;">peak ${vvFmtBps(peakRx)}</span></span>
|
||||||
|
<span><span style="color:#ff9800;font-size:9px;margin-right:4px;">━ OUT (TX)</span><span style="color:#ff9800;">${vvFmtBps(tx)}</span><span style="color:#444;font-size:9px;font-weight:400;margin-left:4px;">peak ${vvFmtBps(peakTx)}</span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="text-align:right;font-size:11px;color:#aaa;line-height:1.6;">${ipRows}</div>
|
||||||
|
</div>
|
||||||
|
<canvas id="vv-net-canvas" style="width:100%;height:110px;display:block;"></canvas>`;
|
||||||
|
|
||||||
|
vvDrawNetChart(document.getElementById('vv-net-canvas'), vvNetRxHistory, vvNetTxHistory, maxBps);
|
||||||
|
} else {
|
||||||
|
document.getElementById('vv-network-body').innerHTML = '<p style="color:#555;font-style:italic">No network interface detected</p>';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
vvPollFast();
|
||||||
|
setInterval(vvPollFast, 1000);
|
||||||
|
|
||||||
// Pin pools card width to CPU card width across rows
|
// Pin pools card width to CPU card width across rows
|
||||||
function vvSyncCardWidths() {
|
function vvSyncCardWidths() {
|
||||||
const cpu = document.getElementById('vv-cpu');
|
const cpu = document.getElementById('vv-cpu');
|
||||||
|
|||||||
Reference in New Issue
Block a user