package dev.lions.unionflow.server.service; import dev.lions.unionflow.server.api.dto.logs.response.SystemMetricsResponse; import dev.lions.unionflow.server.entity.Membre; import dev.lions.unionflow.server.repository.MembreRepository; import io.agroal.api.AgroalDataSource; import io.quarkus.runtime.StartupEvent; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; import jakarta.inject.Inject; import lombok.extern.slf4j.Slf4j; import org.eclipse.microprofile.config.inject.ConfigProperty; import javax.sql.DataSource; import java.io.File; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.OperatingSystemMXBean; import java.sql.Connection; import java.sql.SQLException; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.concurrent.atomic.AtomicLong; /** * Service pour récupérer les métriques système réelles */ @Slf4j @ApplicationScoped public class SystemMetricsService { @Inject MembreRepository membreRepository; @Inject DataSource dataSource; @ConfigProperty(name = "quarkus.application.name") String applicationName; @ConfigProperty(name = "quarkus.application.version") String applicationVersion; @ConfigProperty(name = "quarkus.oidc.auth-server-url", defaultValue = "http://localhost:8180/realms/unionflow") String authServerUrl; // Compteurs pour les métriques private final AtomicLong apiRequestsCount = new AtomicLong(0); private final AtomicLong apiRequestsLastHour = new AtomicLong(0); private final AtomicLong apiRequestsToday = new AtomicLong(0); private long startTimeMillis; private LocalDateTime startTime; /** * Initialisation au démarrage */ void onStart(@Observes StartupEvent event) { startTimeMillis = System.currentTimeMillis(); startTime = LocalDateTime.now(); log.info("SystemMetricsService initialized at {}", startTime); } /** * Récupérer toutes les métriques système */ public SystemMetricsResponse getSystemMetrics() { log.debug("Collecting system metrics..."); return SystemMetricsResponse.builder() // Métriques CPU .cpuUsagePercent(getCpuUsage()) .availableProcessors(Runtime.getRuntime().availableProcessors()) .systemLoadAverage(getSystemLoadAverage()) // Métriques mémoire .totalMemoryBytes(getTotalMemory()) .usedMemoryBytes(getUsedMemory()) .freeMemoryBytes(getFreeMemory()) .maxMemoryBytes(getMaxMemory()) .memoryUsagePercent(getMemoryUsagePercent()) .totalMemoryFormatted(SystemMetricsResponse.formatBytes(getTotalMemory())) .usedMemoryFormatted(SystemMetricsResponse.formatBytes(getUsedMemory())) .freeMemoryFormatted(SystemMetricsResponse.formatBytes(getFreeMemory())) // Métriques disque .totalDiskBytes(getTotalDiskSpace()) .usedDiskBytes(getUsedDiskSpace()) .freeDiskBytes(getFreeDiskSpace()) .diskUsagePercent(getDiskUsagePercent()) .totalDiskFormatted(SystemMetricsResponse.formatBytes(getTotalDiskSpace())) .usedDiskFormatted(SystemMetricsResponse.formatBytes(getUsedDiskSpace())) .freeDiskFormatted(SystemMetricsResponse.formatBytes(getFreeDiskSpace())) // Métriques utilisateurs .activeUsersCount(getActiveUsersCount()) .totalUsersCount(getTotalUsersCount()) .activeSessionsCount(getActiveSessionsCount()) .failedLoginAttempts24h(getFailedLoginAttempts()) // Métriques API .apiRequestsLastHour(apiRequestsLastHour.get()) .apiRequestsToday(apiRequestsToday.get()) .averageResponseTimeMs(getAverageResponseTime()) .totalRequestsCount(apiRequestsCount.get()) // Métriques base de données .dbConnectionPoolSize(getDbConnectionPoolSize()) .dbActiveConnections(getDbActiveConnections()) .dbIdleConnections(getDbIdleConnections()) .dbHealthy(isDatabaseHealthy()) // Métriques erreurs et logs (simulées pour l'instant, à implémenter avec vrai système de logs) .criticalErrorsCount(0) .warningsCount(0) .infoLogsCount(0) .debugLogsCount(0) .totalLogsCount(0L) // Métriques réseau (simulées, nécessiterait monitoring avancé) .networkBytesReceivedPerSec(0.0) .networkBytesSentPerSec(0.0) .networkInFormatted("0 B/s") .networkOutFormatted("0 B/s") // Métriques système .systemStatus(getSystemStatus()) .uptimeMillis(getUptimeMillis()) .uptimeFormatted(SystemMetricsResponse.formatUptime(getUptimeMillis())) .startTime(startTime) .currentTime(LocalDateTime.now()) .javaVersion(System.getProperty("java.version")) .quarkusVersion(getQuarkusVersion()) .applicationVersion(applicationVersion) // Métriques maintenance (à implémenter avec vrai système de backup) .lastBackup(null) .nextScheduledMaintenance(null) .lastMaintenance(null) // URLs .apiBaseUrl(getApiBaseUrl()) .authServerUrl(authServerUrl) .cdnUrl(null) // Cache (à implémenter) .totalCacheSizeBytes(0L) .totalCacheSizeFormatted("0 B") .totalCacheEntries(0) .build(); } // ==================== MÉTHODES DE CALCUL DES MÉTRIQUES ==================== /** * CPU Usage (estimation basée sur la charge système) */ private Double getCpuUsage() { OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean(); double loadAvg = osBean.getSystemLoadAverage(); int processors = osBean.getAvailableProcessors(); if (loadAvg < 0) { return 0.0; // Non disponible sur certains OS } // Calcul approximatif : (load average / nb processeurs) * 100 return Math.min(100.0, (loadAvg / processors) * 100.0); } /** * System Load Average */ private Double getSystemLoadAverage() { return ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage(); } /** * Mémoire totale */ private Long getTotalMemory() { return Runtime.getRuntime().totalMemory(); } /** * Mémoire utilisée */ private Long getUsedMemory() { Runtime runtime = Runtime.getRuntime(); return runtime.totalMemory() - runtime.freeMemory(); } /** * Mémoire libre */ private Long getFreeMemory() { return Runtime.getRuntime().freeMemory(); } /** * Mémoire maximale */ private Long getMaxMemory() { return Runtime.getRuntime().maxMemory(); } /** * Pourcentage mémoire utilisée */ private Double getMemoryUsagePercent() { Runtime runtime = Runtime.getRuntime(); long used = runtime.totalMemory() - runtime.freeMemory(); long max = runtime.maxMemory(); return (used * 100.0) / max; } /** * Espace disque total */ private Long getTotalDiskSpace() { File root = new File("/"); return root.getTotalSpace(); } /** * Espace disque utilisé */ private Long getUsedDiskSpace() { File root = new File("/"); return root.getTotalSpace() - root.getFreeSpace(); } /** * Espace disque libre */ private Long getFreeDiskSpace() { File root = new File("/"); return root.getFreeSpace(); } /** * Pourcentage disque utilisé */ private Double getDiskUsagePercent() { File root = new File("/"); long total = root.getTotalSpace(); long free = root.getFreeSpace(); if (total == 0) return 0.0; return ((total - free) * 100.0) / total; } /** * Nombre d'utilisateurs actifs (avec sessions actives) */ private Integer getActiveUsersCount() { // TODO: Implémenter avec vrai système de sessions // Pour l'instant, compte les membres actifs try { return (int) membreRepository.count("actif = true"); } catch (Exception e) { log.error("Error getting active users count", e); return 0; } } /** * Nombre total d'utilisateurs */ private Integer getTotalUsersCount() { try { return (int) membreRepository.count(); } catch (Exception e) { log.error("Error getting total users count", e); return 0; } } /** * Nombre de sessions actives */ private Integer getActiveSessionsCount() { // TODO: Implémenter avec vrai système de sessions Keycloak return 0; } /** * Tentatives de login échouées (24h) */ private Integer getFailedLoginAttempts() { // TODO: Implémenter avec vrai système d'audit return 0; } /** * Temps de réponse moyen API */ private Double getAverageResponseTime() { // TODO: Implémenter avec vrai système de métriques return 0.0; } /** * Taille du pool de connexions DB */ private Integer getDbConnectionPoolSize() { if (dataSource instanceof AgroalDataSource agroalDataSource) { return agroalDataSource.getConfiguration().connectionPoolConfiguration().maxSize(); } return 0; } /** * Connexions DB actives */ private Integer getDbActiveConnections() { if (dataSource instanceof AgroalDataSource agroalDataSource) { return (int) agroalDataSource.getMetrics().activeCount(); } return 0; } /** * Connexions DB en attente */ private Integer getDbIdleConnections() { if (dataSource instanceof AgroalDataSource agroalDataSource) { return (int) agroalDataSource.getMetrics().availableCount(); } return 0; } /** * État santé base de données */ private Boolean isDatabaseHealthy() { try (Connection conn = dataSource.getConnection()) { return conn.isValid(5); // 5 secondes timeout } catch (SQLException e) { log.error("Database health check failed", e); return false; } } /** * Statut système */ private String getSystemStatus() { // TODO: Implémenter logique plus sophistiquée return "OPERATIONAL"; } /** * Uptime en millisecondes */ private Long getUptimeMillis() { return System.currentTimeMillis() - startTimeMillis; } /** * Version Quarkus */ private String getQuarkusVersion() { return io.quarkus.runtime.annotations.QuarkusMain.class.getPackage().getImplementationVersion(); } /** * URL base API */ private String getApiBaseUrl() { // TODO: Récupérer depuis configuration return "http://localhost:8085"; } /** * Incrémenter le compteur de requêtes API */ public void incrementApiRequestCount() { apiRequestsCount.incrementAndGet(); apiRequestsLastHour.incrementAndGet(); apiRequestsToday.incrementAndGet(); } }