From 6498f84bc8c16445507a01f12e267605de044ed1 Mon Sep 17 00:00:00 2001 From: DariusIII Date: Mon, 27 Oct 2025 08:28:42 +0100 Subject: [PATCH] Add missing SystemMetricsService --- app/Services/SystemMetricsService.php | 250 ++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 app/Services/SystemMetricsService.php diff --git a/app/Services/SystemMetricsService.php b/app/Services/SystemMetricsService.php new file mode 100644 index 000000000..3e85367d4 --- /dev/null +++ b/app/Services/SystemMetricsService.php @@ -0,0 +1,250 @@ +getCpuUsage(); + $ramUsage = $this->getRamUsage(); + + $now = now(); + + // Store CPU metric + SystemMetric::create([ + 'metric_type' => 'cpu', + 'value' => $cpuUsage, + 'recorded_at' => $now, + ]); + + // Store RAM metric (percentage) + SystemMetric::create([ + 'metric_type' => 'ram', + 'value' => $ramUsage['percentage'], + 'recorded_at' => $now, + ]); + } + + /** + * Get historical metrics grouped by hour for the last N hours + */ + public function getHourlyMetrics(string $type, int $hours = 24): array + { + $startDate = now()->subHours($hours); + + $metrics = SystemMetric::where('metric_type', $type) + ->where('recorded_at', '>=', $startDate) + ->orderBy('recorded_at') + ->get(); + + // Group by hour and calculate average + $hourlyData = []; + foreach ($metrics as $metric) { + $hourKey = $metric->recorded_at->format('Y-m-d H:00'); + $hourLabel = $metric->recorded_at->format('H:i'); + + if (! isset($hourlyData[$hourKey])) { + $hourlyData[$hourKey] = [ + 'time' => $hourLabel, + 'values' => [], + ]; + } + $hourlyData[$hourKey]['values'][] = $metric->value; + } + + // Calculate averages + $result = []; + foreach ($hourlyData as $data) { + $result[] = [ + 'time' => $data['time'], + 'value' => round(array_sum($data['values']) / count($data['values']), 2), + ]; + } + + // Fill missing hours with null or last known value + return $this->fillMissingHours($result, $hours); + } + + /** + * Get historical metrics grouped by day for the last N days + */ + public function getDailyMetrics(string $type, int $days = 30): array + { + $startDate = now()->subDays($days); + + $metrics = SystemMetric::where('metric_type', $type) + ->where('recorded_at', '>=', $startDate) + ->orderBy('recorded_at') + ->get(); + + // Group by day and calculate average + $dailyData = []; + foreach ($metrics as $metric) { + $dayKey = $metric->recorded_at->format('Y-m-d'); + $dayLabel = $metric->recorded_at->format('M d'); + + if (! isset($dailyData[$dayKey])) { + $dailyData[$dayKey] = [ + 'time' => $dayLabel, + 'values' => [], + ]; + } + $dailyData[$dayKey]['values'][] = $metric->value; + } + + // Calculate averages + $result = []; + foreach ($dailyData as $data) { + $result[] = [ + 'time' => $data['time'], + 'value' => round(array_sum($data['values']) / count($data['values']), 2), + ]; + } + + return $result; + } + + /** + * Fill missing hours with previous value or 0 + */ + protected function fillMissingHours(array $data, int $hours): array + { + if (empty($data)) { + // Generate empty data points for all hours + $result = []; + for ($i = $hours - 1; $i >= 0; $i--) { + $time = now()->subHours($i); + $result[] = [ + 'time' => $time->format('H:i'), + 'value' => 0, + ]; + } + + return $result; + } + + return $data; + } + + /** + * Get current CPU usage percentage + */ + protected function getCpuUsage(): float + { + try { + if (PHP_OS_FAMILY === 'Windows') { + $output = shell_exec('wmic cpu get loadpercentage'); + if ($output) { + preg_match('/\d+/', $output, $matches); + + return (float) ($matches[0] ?? 0); + } + } else { + // Linux - use load average + $load = sys_getloadavg(); + if ($load !== false) { + $cpuCount = $this->getCpuCount(); + + return round(($load[0] / $cpuCount) * 100, 2); + } + } + } catch (\Exception $e) { + Log::warning('Could not get CPU usage: '.$e->getMessage()); + } + + return 0; + } + + /** + * Get number of CPU cores + */ + protected function getCpuCount(): int + { + try { + if (PHP_OS_FAMILY === 'Windows') { + $output = shell_exec('wmic cpu get NumberOfLogicalProcessors'); + if ($output) { + preg_match('/\d+/', $output, $matches); + + return (int) ($matches[0] ?? 1); + } + } else { + $cpuinfo = file_get_contents('/proc/cpuinfo'); + preg_match_all('/^processor/m', $cpuinfo, $matches); + + return count($matches[0]) ?: 1; + } + } catch (\Exception $e) { + return 1; + } + + return 1; + } + + /** + * Get RAM usage information + */ + protected function getRamUsage(): array + { + try { + if (PHP_OS_FAMILY === 'Windows') { + $output = shell_exec('wmic OS get FreePhysicalMemory,TotalVisibleMemorySize /Value'); + if ($output) { + preg_match('/FreePhysicalMemory=(\d+)/', $output, $free); + preg_match('/TotalVisibleMemorySize=(\d+)/', $output, $total); + + if (isset($free[1]) && isset($total[1])) { + $freeKb = (float) $free[1]; + $totalKb = (float) $total[1]; + $usedKb = $totalKb - $freeKb; + + return [ + 'used' => round($usedKb / 1024 / 1024, 2), + 'total' => round($totalKb / 1024 / 1024, 2), + 'percentage' => round(($usedKb / $totalKb) * 100, 2), + ]; + } + } + } else { + $meminfo = file_get_contents('/proc/meminfo'); + preg_match('/MemTotal:\s+(\d+)/', $meminfo, $total); + preg_match('/MemAvailable:\s+(\d+)/', $meminfo, $available); + + if (isset($total[1]) && isset($available[1])) { + $totalKb = (float) $total[1]; + $availableKb = (float) $available[1]; + $usedKb = $totalKb - $availableKb; + + return [ + 'used' => round($usedKb / 1024 / 1024, 2), + 'total' => round($totalKb / 1024 / 1024, 2), + 'percentage' => round(($usedKb / $totalKb) * 100, 2), + ]; + } + } + } catch (\Exception $e) { + Log::warning('Could not get RAM usage: '.$e->getMessage()); + } + + return [ + 'used' => 0, + 'total' => 0, + 'percentage' => 0, + ]; + } + + /** + * Clean up old metrics (keep last 60 days) + */ + public function cleanupOldMetrics(int $daysToKeep = 60): int + { + return SystemMetric::cleanupOldMetrics($daysToKeep); + } +}