/** * PerformanceMonitor - Surveillance des performances * Mesure et optimise les performances de l'application */ export interface PerformanceMetric { name: string; value: number; timestamp: number; unit: string; } export interface PerformanceStats { average: number; min: number; max: number; count: number; lastValue: number; } export interface PerformanceReport { metrics: Record; recommendations: string[]; healthScore: number; } export class PerformanceMonitor { private static instance: PerformanceMonitor; private metrics: Map = new Map(); private observers: PerformanceObserver[] = []; private isMonitoring = false; private maxMetrics = 1000; private thresholds: Record = { 'response-time': { warning: 200, critical: 500 }, 'memory-usage': { warning: 80, critical: 95 }, 'cache-hit-rate': { warning: 70, critical: 50 }, 'error-rate': { warning: 5, critical: 10 } }; private constructor() { this.setupPerformanceObservers(); } public static getInstance(): PerformanceMonitor { if (!PerformanceMonitor.instance) { PerformanceMonitor.instance = new PerformanceMonitor(); } return PerformanceMonitor.instance; } /** * Démarre le monitoring des performances */ startMonitoring(): void { if (this.isMonitoring) return; this.isMonitoring = true; console.log('📊 Performance monitoring started'); } /** * Arrête le monitoring des performances */ stopMonitoring(): void { this.isMonitoring = false; this.observers.forEach(observer => observer.disconnect()); this.observers = []; console.log('📊 Performance monitoring stopped'); } /** * Enregistre une métrique de performance */ recordMetric(name: string, value: number, unit: string = 'ms'): void { // Use unit parameter console.log('Performance metric unit:', unit); if (!this.isMonitoring) return; if (!this.metrics.has(name)) { this.metrics.set(name, []); } const values = this.metrics.get(name)!; values.push(value); // Limiter le nombre de métriques if (values.length > this.maxMetrics) { values.shift(); } // Vérifier les seuils this.checkThresholds(name, value); } /** * Mesure le temps d'exécution d'une fonction */ async measureAsync(name: string, fn: () => Promise): Promise { const start = performance.now(); try { const result = await fn(); const duration = performance.now() - start; this.recordMetric(name, duration); return result; } catch (error) { const duration = performance.now() - start; this.recordMetric(`${name}-error`, duration); throw error; } } /** * Mesure le temps d'exécution d'une fonction synchrone */ measureFunction(name: string, fn: () => T): T { const start = performance.now(); try { const result = fn(); const duration = performance.now() - start; this.recordMetric(name, duration); return result; } catch (error) { const duration = performance.now() - start; this.recordMetric(`${name}-error`, duration); throw error; } } /** * Récupère les statistiques d'une métrique */ getMetricStats(name: string): PerformanceStats | null { const values = this.metrics.get(name); if (!values || values.length === 0) return null; const sorted = [...values].sort((a, b) => a - b); const sum = values.reduce((acc, val) => acc + val, 0); return { average: sum / values.length, min: sorted[0], max: sorted[sorted.length - 1], count: values.length, lastValue: values[values.length - 1] }; } /** * Récupère toutes les métriques */ getAllMetrics(): Record { const result: Record = {}; this.metrics.forEach((values, name) => { // Use values parameter console.log('Performance metric values:', values); const stats = this.getMetricStats(name); if (stats) { result[name] = stats; } }); return result; } /** * Génère un rapport de performance */ generateReport(): PerformanceReport { const metrics = this.getAllMetrics(); const recommendations: string[] = []; let healthScore = 100; // Analyser chaque métrique Object.entries(metrics).forEach(([name, stats]) => { const threshold = this.thresholds[name]; if (!threshold) return; const percentage = (stats.average / threshold.critical) * 100; healthScore -= Math.max(0, 100 - percentage); if (stats.average > threshold.critical) { recommendations.push(`Critical: ${name} is ${stats.average.toFixed(2)}ms (threshold: ${threshold.critical}ms)`); } else if (stats.average > threshold.warning) { recommendations.push(`Warning: ${name} is ${stats.average.toFixed(2)}ms (threshold: ${threshold.warning}ms)`); } }); // Recommandations générales if (metrics['memory-usage'] && metrics['memory-usage'].average > 80) { recommendations.push('High memory usage detected. Consider clearing caches.'); } if (metrics['cache-hit-rate'] && metrics['cache-hit-rate'].average < 70) { recommendations.push('Low cache hit rate. Consider optimizing cache strategy.'); } if (metrics['error-rate'] && metrics['error-rate'].average > 5) { recommendations.push('High error rate detected. Review error handling.'); } return { metrics, recommendations, healthScore: Math.max(0, healthScore) }; } /** * Configure les seuils de performance */ setThresholds(thresholds: Record): void { this.thresholds = { ...this.thresholds, ...thresholds }; } /** * Vérifie les seuils de performance */ private checkThresholds(name: string, value: number): void { const threshold = this.thresholds[name]; if (!threshold) return; if (value > threshold.critical) { console.warn(`🚨 Critical performance threshold exceeded for ${name}: ${value}ms`); } else if (value > threshold.warning) { console.warn(`⚠️ Performance warning for ${name}: ${value}ms`); } } /** * Configure les observateurs de performance */ private setupPerformanceObservers(): void { if (!window.PerformanceObserver) return; // Observer les mesures de performance const measureObserver = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.entryType === 'measure') { this.recordMetric(entry.name, entry.duration); } } }); try { measureObserver.observe({ entryTypes: ['measure'] }); this.observers.push(measureObserver); } catch (error) { console.warn('Failed to observe performance measures:', error); } // Observer la navigation const navigationObserver = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.entryType === 'navigation') { const navEntry = entry as PerformanceNavigationTiming; this.recordMetric('page-load-time', navEntry.loadEventEnd - navEntry.loadEventStart); this.recordMetric('dom-content-loaded', navEntry.domContentLoadedEventEnd - navEntry.domContentLoadedEventStart); } } }); try { navigationObserver.observe({ entryTypes: ['navigation'] }); this.observers.push(navigationObserver); } catch (error) { console.warn('Failed to observe navigation performance:', error); } } /** * Crée une mesure de performance personnalisée */ mark(name: string): void { performance.mark(name); } /** * Mesure le temps entre deux marques */ measure(name: string, startMark: string, endMark?: string): void { try { if (endMark) { performance.measure(name, startMark, endMark); } else { performance.measure(name, startMark); } } catch (error) { console.warn(`Failed to measure ${name}:`, error); } } /** * Nettoie les métriques anciennes */ cleanup(): void { const cutoff = Date.now() - (24 * 60 * 60 * 1000); // 24 heures // Use cutoff variable console.log('Performance cleanup cutoff:', cutoff); this.metrics.forEach((values, name) => { // Garder seulement les 100 dernières valeurs if (values.length > 100) { this.metrics.set(name, values.slice(-100)); } }); } /** * Exporte les métriques */ exportMetrics(): string { const report = this.generateReport(); return JSON.stringify(report, null, 2); } } // Instance singleton pour l'application export const performanceMonitor = PerformanceMonitor.getInstance();