Fix sysstat timer start and chart layout
This commit is contained in:
@@ -13,8 +13,6 @@ class ServerChartsWidget extends Widget
|
||||
|
||||
protected int|string|array $columnSpan = 'full';
|
||||
|
||||
protected ?string $pollingInterval = '60s';
|
||||
|
||||
public int $refreshKey = 0;
|
||||
|
||||
public string $range = '5m';
|
||||
@@ -201,7 +199,7 @@ class ServerChartsWidget extends Widget
|
||||
{
|
||||
return match ($range) {
|
||||
'5m' => ['minutes' => 5, 'points' => 30, 'resolution' => '10s', 'label_format' => 'H:i:s', 'interval_seconds' => 10],
|
||||
'30m' => ['minutes' => 30, 'points' => 180, 'resolution' => '10s', 'label_format' => 'H:i', 'interval_seconds' => 10],
|
||||
'30m' => ['minutes' => 30, 'points' => 180, 'resolution' => '10s', 'label_format' => 'H:i:s', 'interval_seconds' => 10],
|
||||
'day' => ['minutes' => 1440, 'points' => 24, 'resolution' => '1h', 'label_format' => 'H:00', 'interval_seconds' => 3600],
|
||||
'week' => ['minutes' => 10080, 'points' => 28, 'resolution' => '6h', 'label_format' => 'M d H:00', 'interval_seconds' => 21600],
|
||||
'month' => ['minutes' => 43200, 'points' => 30, 'resolution' => '1d', 'label_format' => 'M d', 'interval_seconds' => 86400],
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace App\Services;
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
use DateTimeZone;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class SysstatMetrics
|
||||
@@ -34,13 +35,18 @@ class SysstatMetrics
|
||||
}
|
||||
}
|
||||
$start = $end->subSeconds(($points - 1) * $intervalSeconds);
|
||||
$samples = $this->readSamples($start, $end);
|
||||
$endBucket = intdiv($end->getTimestamp(), $intervalSeconds);
|
||||
$cacheKey = sprintf('sysstat.history.%d.%d.%s.%d', $points, $intervalSeconds, $labelFormat, $endBucket);
|
||||
$ttl = $this->cacheTtl($intervalSeconds);
|
||||
|
||||
if (empty($samples)) {
|
||||
return [];
|
||||
}
|
||||
return Cache::remember($cacheKey, $ttl, function () use ($start, $end, $points, $intervalSeconds, $labelFormat): array {
|
||||
$samples = $this->readSamples($start, $end, $this->coreOptions());
|
||||
if (empty($samples)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->resample($samples, $start, $points, $intervalSeconds, $labelFormat);
|
||||
return $this->resample($samples, $start, $points, $intervalSeconds, $labelFormat);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,25 +57,29 @@ class SysstatMetrics
|
||||
$timezone = $this->systemTimezone();
|
||||
$end = CarbonImmutable::now($timezone);
|
||||
$start = $end->subMinutes(15);
|
||||
$samples = $this->readSamples($start, $end);
|
||||
$bucket = intdiv($end->getTimestamp(), 10);
|
||||
$cacheKey = sprintf('sysstat.latest.%d', $bucket);
|
||||
|
||||
if (empty($samples)) {
|
||||
return null;
|
||||
}
|
||||
return Cache::remember($cacheKey, now()->addSeconds(10), function () use ($start, $end): ?array {
|
||||
$samples = $this->readSamples($start, $end, $this->coreOptions());
|
||||
if (empty($samples)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$last = end($samples);
|
||||
if (! is_array($last)) {
|
||||
return null;
|
||||
}
|
||||
$last = end($samples);
|
||||
if (! is_array($last)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'load1' => (float) ($last['load1'] ?? 0),
|
||||
'load5' => (float) ($last['load5'] ?? 0),
|
||||
'load15' => (float) ($last['load15'] ?? 0),
|
||||
'iowait' => (float) ($last['iowait'] ?? 0),
|
||||
'memory' => (float) ($last['memory'] ?? 0),
|
||||
'swap' => (float) ($last['swap'] ?? 0),
|
||||
];
|
||||
return [
|
||||
'load1' => (float) ($last['load1'] ?? 0),
|
||||
'load5' => (float) ($last['load5'] ?? 0),
|
||||
'load15' => (float) ($last['load15'] ?? 0),
|
||||
'iowait' => (float) ($last['iowait'] ?? 0),
|
||||
'memory' => (float) ($last['memory'] ?? 0),
|
||||
'swap' => (float) ($last['swap'] ?? 0),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function timezoneName(): string
|
||||
@@ -80,14 +90,16 @@ class SysstatMetrics
|
||||
/**
|
||||
* @return array<int, array{timestamp: int, load1: float, load5: float, load15: float, iowait: float, memory: float, swap: float}>
|
||||
*/
|
||||
private function readSamples(CarbonImmutable $start, CarbonImmutable $end): array
|
||||
private function readSamples(CarbonImmutable $start, CarbonImmutable $end, array $options): array
|
||||
{
|
||||
$samples = [];
|
||||
$current = $start->startOfDay();
|
||||
$lastDay = $end->startOfDay();
|
||||
|
||||
while ($current <= $lastDay) {
|
||||
$file = sprintf('/var/log/sysstat/sa%s', $current->format('d'));
|
||||
$fileLong = sprintf('/var/log/sysstat/sa%s', $current->format('Ymd'));
|
||||
$fileShort = sprintf('/var/log/sysstat/sa%s', $current->format('d'));
|
||||
$file = is_readable($fileLong) ? $fileLong : $fileShort;
|
||||
if (! is_readable($file)) {
|
||||
$current = $current->addDay();
|
||||
|
||||
@@ -96,7 +108,7 @@ class SysstatMetrics
|
||||
|
||||
$dayStart = $current->isSameDay($start) ? $start : $current->startOfDay();
|
||||
$dayEnd = $current->isSameDay($end) ? $end : $current->endOfDay();
|
||||
$statistics = $this->readSadf($file, $dayStart, $dayEnd);
|
||||
$statistics = $this->readSadf($file, $dayStart, $dayEnd, $options);
|
||||
|
||||
foreach ($statistics as $stat) {
|
||||
$parsed = $this->parseSample($stat);
|
||||
@@ -117,20 +129,25 @@ class SysstatMetrics
|
||||
/**
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
private function readSadf(string $file, CarbonImmutable $start, CarbonImmutable $end): array
|
||||
private function readSadf(string $file, CarbonImmutable $start, CarbonImmutable $end, array $options): array
|
||||
{
|
||||
$process = new Process([
|
||||
'sadf',
|
||||
'-j',
|
||||
'-T',
|
||||
$file,
|
||||
'--',
|
||||
'-A',
|
||||
'-s',
|
||||
$start->format('H:i:s'),
|
||||
'-e',
|
||||
$end->format('H:i:s'),
|
||||
]);
|
||||
$args = array_merge(
|
||||
[
|
||||
'sadf',
|
||||
'-j',
|
||||
'-T',
|
||||
$file,
|
||||
'--',
|
||||
],
|
||||
$options,
|
||||
[
|
||||
'-s',
|
||||
$start->format('H:i:s'),
|
||||
'-e',
|
||||
$end->format('H:i:s'),
|
||||
],
|
||||
);
|
||||
$process = new Process($args);
|
||||
$process->run();
|
||||
|
||||
if (! $process->isSuccessful()) {
|
||||
@@ -150,6 +167,23 @@ class SysstatMetrics
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private function coreOptions(): array
|
||||
{
|
||||
return ['-q', '-u', '-r', '-S'];
|
||||
}
|
||||
|
||||
private function cacheTtl(int $intervalSeconds): \DateInterval|\DateTimeInterface|int
|
||||
{
|
||||
if ($intervalSeconds <= 10) {
|
||||
return 10;
|
||||
}
|
||||
|
||||
return max(30, min(300, $intervalSeconds));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $stat
|
||||
* @return array{timestamp: int, load1: float, load5: float, load15: float, iowait: float, memory: float, swap: float}|null
|
||||
@@ -205,6 +239,7 @@ class SysstatMetrics
|
||||
|
||||
$index = 0;
|
||||
$current = null;
|
||||
$first = $samples[0] ?? null;
|
||||
$count = count($samples);
|
||||
|
||||
for ($i = 0; $i < $points; $i++) {
|
||||
@@ -214,11 +249,12 @@ class SysstatMetrics
|
||||
$index++;
|
||||
}
|
||||
|
||||
$sample = $current ?? $first;
|
||||
$labels[] = $bucketTime->format($labelFormat);
|
||||
$loadSeries[] = $current ? round((float) $current['load1'], 3) : 0.0;
|
||||
$ioWaitSeries[] = $current ? round((float) $current['iowait'], 2) : 0.0;
|
||||
$memorySeries[] = $current ? round((float) $current['memory'], 1) : 0.0;
|
||||
$swapSeries[] = $current ? round((float) $current['swap'], 1) : 0.0;
|
||||
$loadSeries[] = $sample ? round((float) $sample['load1'], 3) : 0.0;
|
||||
$ioWaitSeries[] = $sample ? round((float) $sample['iowait'], 2) : 0.0;
|
||||
$memorySeries[] = $sample ? round((float) $sample['memory'], 1) : 0.0;
|
||||
$swapSeries[] = $sample ? round((float) $sample['swap'], 1) : 0.0;
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
@@ -1027,9 +1027,9 @@ configure_sysstat() {
|
||||
fi
|
||||
|
||||
if grep -q '^INTERVAL=' /etc/default/sysstat; then
|
||||
sed -i 's/^INTERVAL=.*/INTERVAL=1/' /etc/default/sysstat
|
||||
sed -i 's/^INTERVAL=.*/INTERVAL=10/' /etc/default/sysstat
|
||||
else
|
||||
echo 'INTERVAL=1' >> /etc/default/sysstat
|
||||
echo 'INTERVAL=10' >> /etc/default/sysstat
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1059,9 +1059,9 @@ configure_sysstat() {
|
||||
fi
|
||||
|
||||
if grep -q '^INTERVAL=' /etc/sysstat/sysstat; then
|
||||
sed -i 's/^INTERVAL=.*/INTERVAL=1/' /etc/sysstat/sysstat
|
||||
sed -i 's/^INTERVAL=.*/INTERVAL=10/' /etc/sysstat/sysstat
|
||||
else
|
||||
echo 'INTERVAL=1' >> /etc/sysstat/sysstat
|
||||
echo 'INTERVAL=10' >> /etc/sysstat/sysstat
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1070,6 +1070,7 @@ configure_sysstat() {
|
||||
cat > /etc/systemd/system/sysstat-collect.timer.d/override.conf <<'EOF'
|
||||
[Timer]
|
||||
OnCalendar=
|
||||
OnActiveSec=10s
|
||||
OnUnitActiveSec=10s
|
||||
AccuracySec=1s
|
||||
Persistent=true
|
||||
|
||||
@@ -995,9 +995,9 @@ configure_sysstat() {
|
||||
fi
|
||||
|
||||
if grep -q '^INTERVAL=' /etc/default/sysstat; then
|
||||
sed -i 's/^INTERVAL=.*/INTERVAL=1/' /etc/default/sysstat
|
||||
sed -i 's/^INTERVAL=.*/INTERVAL=10/' /etc/default/sysstat
|
||||
else
|
||||
echo 'INTERVAL=1' >> /etc/default/sysstat
|
||||
echo 'INTERVAL=10' >> /etc/default/sysstat
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1027,9 +1027,9 @@ configure_sysstat() {
|
||||
fi
|
||||
|
||||
if grep -q '^INTERVAL=' /etc/sysstat/sysstat; then
|
||||
sed -i 's/^INTERVAL=.*/INTERVAL=1/' /etc/sysstat/sysstat
|
||||
sed -i 's/^INTERVAL=.*/INTERVAL=10/' /etc/sysstat/sysstat
|
||||
else
|
||||
echo 'INTERVAL=1' >> /etc/sysstat/sysstat
|
||||
echo 'INTERVAL=10' >> /etc/sysstat/sysstat
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1038,6 +1038,7 @@ configure_sysstat() {
|
||||
cat > /etc/systemd/system/sysstat-collect.timer.d/override.conf <<'EOF'
|
||||
[Timer]
|
||||
OnCalendar=
|
||||
OnActiveSec=10s
|
||||
OnUnitActiveSec=10s
|
||||
AccuracySec=1s
|
||||
Persistent=true
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user