fix(posimai-dev): accurate cpu_pct via 100ms dual-sample diff

Single snapshot returns lifetime average (near 0 on idle systems).
Two samples 100ms apart gives real-time cpu usage per core, then averaged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
posimai 2026-03-31 07:50:16 +09:00
parent c09d5defd3
commit 6c138981a7
1 changed files with 30 additions and 18 deletions

View File

@ -39,24 +39,35 @@ app.get('/api/sessions/:id', (req, res) => {
// ── ヘルス & メトリクス API (/api/health) ────────────────────── // ── ヘルス & メトリクス API (/api/health) ──────────────────────
// Atlas など外部から参照される。CORS ヘッダーを付与して Vercel 上の Atlas からも取得可能にする // Atlas など外部から参照される。CORS ヘッダーを付与して Vercel 上の Atlas からも取得可能にする
function getCpuSample() {
return os.cpus().map((c) => {
const total = Object.values(c.times).reduce((a, b) => a + b, 0);
return { total, idle: c.times.idle };
});
}
app.get('/api/health', (req, res) => { app.get('/api/health', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Origin', '*');
const mem = os.freemem(); const mem = os.freemem();
const total = os.totalmem(); const total = os.totalmem();
const cpus = os.cpus();
// CPU 使用率: 全コアの平均(起動時 idle から差し引く簡易計算) // CPU: 100ms 間隔の2サンプルで実使用率を計算
const cpuUsage = cpus.reduce((sum, c) => { const s1 = getCpuSample();
const t = Object.values(c.times).reduce((a, b) => a + b, 0); setTimeout(() => {
return sum + ((t - c.times.idle) / t) * 100; const s2 = getCpuSample();
}, 0) / cpus.length; const cpuPct = s1.reduce((sum, c1, i) => {
const c2 = s2[i];
const dIdle = c2.idle - c1.idle;
const dTotal = c2.total - c1.total;
return sum + (dTotal > 0 ? (1 - dIdle / dTotal) * 100 : 0);
}, 0) / s1.length;
res.json({ res.json({
ok: true, ok: true,
hostname: os.hostname(), hostname: os.hostname(),
uptime_s: Math.floor(os.uptime()), uptime_s: Math.floor(os.uptime()),
cpu_pct: Math.round(cpuUsage), cpu_pct: Math.round(cpuPct),
mem_used_mb: Math.round((total - mem) / 1024 / 1024), mem_used_mb: Math.round((total - mem) / 1024 / 1024),
mem_total_mb: Math.round(total / 1024 / 1024), mem_total_mb: Math.round(total / 1024 / 1024),
active_sessions: wss.clients ? wss.clients.size : 0, active_sessions: wss.clients ? wss.clients.size : 0,
@ -64,6 +75,7 @@ app.get('/api/health', (req, res) => {
platform: os.platform(), platform: os.platform(),
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
}); });
}, 100);
}); });
// Tailscale証明書を自動検出 // Tailscale証明書を自動検出