/** * SYSTEM HEALTH MONITORING * Monitor application performance and health metrics */ export class SystemHealthMonitor { constructor() { this.metrics = { performance: {}, errors: [], warnings: [], uptime: Date.now(), lastCheck: Date.now() }; this.thresholds = { memoryUsage: 100 * 1024 * 1024, // 100MB responseTime: 1000, // 1 second errorRate: 0.05 // 5% }; this.startMonitoring(); } startMonitoring() { // Monitor performance every 30 seconds setInterval(() => { this.checkSystemHealth(); }, 30000); // Monitor memory usage if (performance.memory) { this.monitorMemoryUsage(); } // Monitor network performance this.monitorNetworkPerformance(); } checkSystemHealth() { const now = Date.now(); const uptime = now - this.metrics.uptime; this.metrics.lastCheck = now; this.metrics.performance.uptime = uptime; // Check for performance issues this.checkPerformanceMetrics(); // Clean old errors (keep last 100) if (this.metrics.errors.length > 100) { this.metrics.errors = this.metrics.errors.slice(-100); } // Clean old warnings (keep last 50) if (this.metrics.warnings.length > 50) { this.metrics.warnings = this.metrics.warnings.slice(-50); } return this.getHealthStatus(); } monitorMemoryUsage() { if (performance.memory) { const memory = performance.memory; this.metrics.performance.memory = { used: memory.usedJSHeapSize, total: memory.totalJSHeapSize, limit: memory.jsHeapSizeLimit, timestamp: Date.now() }; // Check for memory leaks if (memory.usedJSHeapSize > this.thresholds.memoryUsage) { this.addWarning('High memory usage detected', { used: memory.usedJSHeapSize, threshold: this.thresholds.memoryUsage }); } } } monitorNetworkPerformance() { // Monitor fetch performance const originalFetch = window.fetch; const self = this; window.fetch = function(...args) { const startTime = performance.now(); return originalFetch.apply(this, args) .then(response => { const endTime = performance.now(); const duration = endTime - startTime; self.recordNetworkMetric(args[0], duration, response.status); return response; }) .catch(error => { const endTime = performance.now(); const duration = endTime - startTime; self.recordNetworkError(args[0], duration, error); throw error; }); }; } recordNetworkMetric(url, duration, status) { if (!this.metrics.performance.network) { this.metrics.performance.network = []; } this.metrics.performance.network.push({ url: typeof url === 'string' ? url : url.toString(), duration, status, timestamp: Date.now() }); // Keep only last 50 network requests if (this.metrics.performance.network.length > 50) { this.metrics.performance.network = this.metrics.performance.network.slice(-50); } // Check for slow requests if (duration > this.thresholds.responseTime) { this.addWarning('Slow network request detected', { url: typeof url === 'string' ? url : url.toString(), duration, threshold: this.thresholds.responseTime }); } } recordNetworkError(url, duration, error) { this.addError('Network request failed', { url: typeof url === 'string' ? url : url.toString(), duration, error: error.message }); } checkPerformanceMetrics() { // Check navigation timing if (performance.navigation && performance.timing) { const timing = performance.timing; const loadTime = timing.loadEventEnd - timing.navigationStart; this.metrics.performance.pageLoad = { loadTime, domReady: timing.domContentLoadedEventEnd - timing.navigationStart, timestamp: Date.now() }; } // Check resource timing if (performance.getEntriesByType) { const resources = performance.getEntriesByType('resource'); const slowResources = resources.filter(resource => resource.duration > 1000); if (slowResources.length > 0) { this.addWarning('Slow resources detected', { count: slowResources.length, resources: slowResources.map(r => ({ name: r.name, duration: r.duration })) }); } } } addError(message, details = {}) { this.metrics.errors.push({ message, details, timestamp: Date.now(), type: 'error' }); console.error('System Health Error:', message, details); } addWarning(message, details = {}) { this.metrics.warnings.push({ message, details, timestamp: Date.now(), type: 'warning' }); console.warn('System Health Warning:', message, details); } getHealthStatus() { const now = Date.now(); const recentErrors = this.metrics.errors.filter(e => now - e.timestamp < 300000); // Last 5 minutes const recentWarnings = this.metrics.warnings.filter(w => now - w.timestamp < 300000); let status = 'healthy'; let score = 100; // Deduct points for errors and warnings score -= recentErrors.length * 10; score -= recentWarnings.length * 5; // Check memory usage if (this.metrics.performance.memory) { const memoryUsage = this.metrics.performance.memory.used / this.metrics.performance.memory.limit; if (memoryUsage > 0.8) { score -= 20; } else if (memoryUsage > 0.6) { score -= 10; } } // Check network performance if (this.metrics.performance.network) { const recentRequests = this.metrics.performance.network.filter(r => now - r.timestamp < 300000); const slowRequests = recentRequests.filter(r => r.duration > this.thresholds.responseTime); const errorRate = slowRequests.length / recentRequests.length; if (errorRate > this.thresholds.errorRate) { score -= 15; } } // Determine status based on score if (score >= 90) status = 'excellent'; else if (score >= 75) status = 'good'; else if (score >= 60) status = 'fair'; else if (score >= 40) status = 'poor'; else status = 'critical'; return { status, score: Math.max(0, score), uptime: now - this.metrics.uptime, lastCheck: this.metrics.lastCheck, recentErrors: recentErrors.length, recentWarnings: recentWarnings.length, metrics: this.metrics }; } getDetailedReport() { return { ...this.getHealthStatus(), performance: this.metrics.performance, recentErrors: this.metrics.errors.slice(-10), recentWarnings: this.metrics.warnings.slice(-10) }; } reset() { this.metrics = { performance: {}, errors: [], warnings: [], uptime: Date.now(), lastCheck: Date.now() }; } } // Global instance export const systemHealth = new SystemHealthMonitor(); // Utility functions export const getSystemHealth = () => systemHealth.getHealthStatus(); export const getDetailedHealthReport = () => systemHealth.getDetailedReport(); export const recordError = (message, details) => systemHealth.addError(message, details); export const recordWarning = (message, details) => systemHealth.addWarning(message, details);