Add Server stats (CPU, RAM, Disk, System) to admin Infra page
First row shows host-level stats from /proc: CPU model, cores, load average with charge %, RAM total/used/available with usage %, disk total/used/free with usage %, hostname, OS and uptime. All color-coded green <70%, yellow <90%, red >=90%. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -664,8 +664,10 @@ class AdminController extends AbstractController
|
||||
$pgbouncerInfo = $this->getPgBouncerInfo($databaseUrl);
|
||||
|
||||
$phpContainers = $this->getPhpContainerInfo();
|
||||
$server = $this->getServerInfo();
|
||||
|
||||
return $this->render('admin/infra.html.twig', [
|
||||
'server' => $server,
|
||||
'redis_global' => $redisGlobal,
|
||||
'redis_dbs' => [$redisMessenger, $redisSession, $redisCache],
|
||||
'postgres' => $pgInfo,
|
||||
@@ -674,6 +676,84 @@ class AdminController extends AbstractController
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function getServerInfo(): array
|
||||
{
|
||||
$info = [];
|
||||
|
||||
// CPU
|
||||
$info['cpu_model'] = '?';
|
||||
$info['cpu_cores'] = 1;
|
||||
if (is_readable('/proc/cpuinfo')) {
|
||||
$cpuinfo = file_get_contents('/proc/cpuinfo');
|
||||
$info['cpu_cores'] = substr_count($cpuinfo, 'processor') ?: 1;
|
||||
if (preg_match('/model name\s*:\s*(.+)/i', $cpuinfo, $m)) {
|
||||
$info['cpu_model'] = trim($m[1]);
|
||||
}
|
||||
}
|
||||
|
||||
$load = sys_getloadavg();
|
||||
$info['load_1m'] = $load ? round($load[0], 2) : '?';
|
||||
$info['load_5m'] = $load ? round($load[1], 2) : '?';
|
||||
$info['load_15m'] = $load ? round($load[2], 2) : '?';
|
||||
$info['load_percent'] = $load ? round($load[0] / $info['cpu_cores'] * 100, 1) : '?';
|
||||
|
||||
// RAM from /proc/meminfo (host-level)
|
||||
$info['ram_total'] = '?';
|
||||
$info['ram_used'] = '?';
|
||||
$info['ram_free'] = '?';
|
||||
$info['ram_usage_percent'] = '?';
|
||||
$info['ram_available'] = '?';
|
||||
if (is_readable('/proc/meminfo')) {
|
||||
$meminfo = file_get_contents('/proc/meminfo');
|
||||
$values = [];
|
||||
foreach (['MemTotal', 'MemFree', 'MemAvailable', 'Buffers', 'Cached'] as $key) {
|
||||
if (preg_match("/{$key}:\s+(\d+)\s+kB/", $meminfo, $m)) {
|
||||
$values[$key] = (int) $m[1] * 1024;
|
||||
}
|
||||
}
|
||||
if (isset($values['MemTotal'])) {
|
||||
$total = $values['MemTotal'];
|
||||
$available = $values['MemAvailable'] ?? ($values['MemFree'] + ($values['Buffers'] ?? 0) + ($values['Cached'] ?? 0));
|
||||
$used = $total - $available;
|
||||
$info['ram_total'] = $this->formatBytes($total);
|
||||
$info['ram_used'] = $this->formatBytes($used);
|
||||
$info['ram_free'] = $this->formatBytes($available);
|
||||
$info['ram_usage_percent'] = round($used / $total * 100, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Disk
|
||||
$path = '/app';
|
||||
$diskTotal = @disk_total_space($path);
|
||||
$diskFree = @disk_free_space($path);
|
||||
if (false !== $diskTotal && false !== $diskFree) {
|
||||
$diskUsed = $diskTotal - $diskFree;
|
||||
$info['disk_total'] = $this->formatBytes((int) $diskTotal);
|
||||
$info['disk_used'] = $this->formatBytes((int) $diskUsed);
|
||||
$info['disk_free'] = $this->formatBytes((int) $diskFree);
|
||||
$info['disk_usage_percent'] = round($diskUsed / $diskTotal * 100, 1);
|
||||
} else {
|
||||
$info['disk_total'] = '?';
|
||||
$info['disk_used'] = '?';
|
||||
$info['disk_free'] = '?';
|
||||
$info['disk_usage_percent'] = '?';
|
||||
}
|
||||
|
||||
// Uptime & hostname
|
||||
if (is_readable('/proc/uptime')) {
|
||||
$info['uptime'] = $this->formatSeconds((int) explode(' ', file_get_contents('/proc/uptime'))[0]);
|
||||
} else {
|
||||
$info['uptime'] = '?';
|
||||
}
|
||||
$info['hostname'] = gethostname() ?: '?';
|
||||
$info['os'] = php_uname('s').' '.php_uname('r');
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array<string, mixed>>
|
||||
*/
|
||||
|
||||
@@ -5,6 +5,61 @@
|
||||
{% block body %}
|
||||
<h1 class="text-3xl font-black uppercase tracking-tighter italic heading-page mb-8">Infrastructure</h1>
|
||||
|
||||
{# Server #}
|
||||
<h2 class="text-xl font-black uppercase tracking-tighter italic heading-page mb-4">Serveur</h2>
|
||||
<div class="flex flex-wrap gap-6 mb-8">
|
||||
{# CPU #}
|
||||
<div class="flex-1 min-w-[280px]">
|
||||
<div class="admin-card">
|
||||
<h2 class="text-sm font-black uppercase tracking-widest mb-4">CPU</h2>
|
||||
<div class="flex flex-col gap-0">
|
||||
{{ _self.row('Modele', server.cpu_model) }}
|
||||
{{ _self.row('Coeurs', server.cpu_cores) }}
|
||||
{{ _self.row('Load 1m / 5m / 15m', server.load_1m ~ ' / ' ~ server.load_5m ~ ' / ' ~ server.load_15m) }}
|
||||
{{ _self.row_color('Charge CPU', server.load_percent != '?' ? server.load_percent ~ '%' : '?', server.load_percent != '?' and server.load_percent < 70 ? 'green' : (server.load_percent == '?' ? 'gray' : (server.load_percent < 90 ? 'yellow' : 'red'))) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# RAM #}
|
||||
<div class="flex-1 min-w-[280px]">
|
||||
<div class="admin-card">
|
||||
<h2 class="text-sm font-black uppercase tracking-widest mb-4">RAM</h2>
|
||||
<div class="flex flex-col gap-0">
|
||||
{{ _self.row('Total', server.ram_total) }}
|
||||
{{ _self.row('Utilisee', server.ram_used) }}
|
||||
{{ _self.row('Disponible', server.ram_free) }}
|
||||
{{ _self.row_color('Utilisation', server.ram_usage_percent != '?' ? server.ram_usage_percent ~ '%' : '?', server.ram_usage_percent != '?' and server.ram_usage_percent < 70 ? 'green' : (server.ram_usage_percent == '?' ? 'gray' : (server.ram_usage_percent < 90 ? 'yellow' : 'red'))) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Disk #}
|
||||
<div class="flex-1 min-w-[280px]">
|
||||
<div class="admin-card">
|
||||
<h2 class="text-sm font-black uppercase tracking-widest mb-4">Disque</h2>
|
||||
<div class="flex flex-col gap-0">
|
||||
{{ _self.row('Total', server.disk_total) }}
|
||||
{{ _self.row('Utilise', server.disk_used) }}
|
||||
{{ _self.row('Libre', server.disk_free) }}
|
||||
{{ _self.row_color('Utilisation', server.disk_usage_percent != '?' ? server.disk_usage_percent ~ '%' : '?', server.disk_usage_percent != '?' and server.disk_usage_percent < 70 ? 'green' : (server.disk_usage_percent == '?' ? 'gray' : (server.disk_usage_percent < 90 ? 'yellow' : 'red'))) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# System #}
|
||||
<div class="flex-1 min-w-[280px]">
|
||||
<div class="admin-card">
|
||||
<h2 class="text-sm font-black uppercase tracking-widest mb-4">Systeme</h2>
|
||||
<div class="flex flex-col gap-0">
|
||||
{{ _self.row('Hostname', server.hostname) }}
|
||||
{{ _self.row('OS', server.os) }}
|
||||
{{ _self.row('Uptime', server.uptime) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# PHP Containers #}
|
||||
<h2 class="text-xl font-black uppercase tracking-tighter italic heading-page mb-4">PHP</h2>
|
||||
<div class="flex flex-wrap gap-6 mb-8">
|
||||
|
||||
Reference in New Issue
Block a user