| import { useState, useCallback, useRef, useEffect } from 'react'; |
| import { PerformanceLogEntry, ResourceUsage } from '../types'; |
|
|
| interface PerformanceConfig { |
| enableLogging?: boolean; |
| logLevel?: 'debug' | 'info' | 'warn' | 'error'; |
| metricsInterval?: number; |
| maxLogEntries?: number; |
| } |
|
|
| interface PerformanceMetrics { |
| cpu: number; |
| memory: number; |
| diskIO: number; |
| networkIO: number; |
| responseTime: number; |
| throughput: number; |
| errorRate: number; |
| } |
|
|
| interface AnomalyThresholds { |
| cpu: number; |
| memory: number; |
| responseTime: number; |
| errorRate: number; |
| } |
|
|
| export const usePerformanceMonitoring = (config: PerformanceConfig = {}) => { |
| const [performanceLog, setPerformanceLog] = useState<PerformanceLogEntry[]>([]); |
| const [isMonitoring, setIsMonitoring] = useState(false); |
| const [currentMetrics, setCurrentMetrics] = useState<PerformanceMetrics>({ |
| cpu: 0, |
| memory: 0, |
| diskIO: 0, |
| networkIO: 0, |
| responseTime: 0, |
| throughput: 0, |
| errorRate: 0 |
| }); |
|
|
| const monitoringIntervalRef = useRef<NodeJS.Timeout | null>(null); |
| const metricsHistoryRef = useRef<PerformanceMetrics[]>([]); |
| const startTimeRef = useRef<number>(Date.now()); |
| const requestCountRef = useRef<number>(0); |
| const errorCountRef = useRef<number>(0); |
|
|
| const defaultThresholds: AnomalyThresholds = { |
| cpu: 0.8, |
| memory: 0.85, |
| responseTime: 5000, |
| errorRate: 0.05 |
| }; |
|
|
| |
| const startMonitoring = useCallback(async () => { |
| if (isMonitoring) return; |
|
|
| setIsMonitoring(true); |
| startTimeRef.current = Date.now(); |
| |
| const interval = config.metricsInterval || 5000; |
| |
| monitoringIntervalRef.current = setInterval(() => { |
| collectMetrics(); |
| }, interval); |
|
|
| |
| logEvent({ |
| id: crypto.randomUUID(), |
| timestamp: new Date(), |
| eventType: 'monitoring_started', |
| status: 'success', |
| details: { interval, config }, |
| resourceUsage: { cpu: 0, memory: 0, diskIO: 0, networkIO: 0 } |
| }); |
| }, [config, isMonitoring]); |
|
|
| |
| const stopMonitoring = useCallback(() => { |
| if (!isMonitoring) return; |
|
|
| setIsMonitoring(false); |
| |
| if (monitoringIntervalRef.current) { |
| clearInterval(monitoringIntervalRef.current); |
| monitoringIntervalRef.current = null; |
| } |
|
|
| |
| logEvent({ |
| id: crypto.randomUUID(), |
| timestamp: new Date(), |
| eventType: 'monitoring_stopped', |
| status: 'success', |
| details: { |
| duration: Date.now() - startTimeRef.current, |
| totalRequests: requestCountRef.current, |
| totalErrors: errorCountRef.current |
| }, |
| resourceUsage: currentMetrics |
| }); |
| }, [isMonitoring, currentMetrics]); |
|
|
| |
| const collectMetrics = useCallback(() => { |
| const metrics: PerformanceMetrics = { |
| cpu: getCPUUsage(), |
| memory: getMemoryUsage(), |
| diskIO: getDiskIOUsage(), |
| networkIO: getNetworkIOUsage(), |
| responseTime: getAverageResponseTime(), |
| throughput: calculateThroughput(), |
| errorRate: calculateErrorRate() |
| }; |
|
|
| setCurrentMetrics(metrics); |
| |
| |
| metricsHistoryRef.current = [...metricsHistoryRef.current.slice(-99), metrics]; |
|
|
| |
| if (config.enableLogging !== false) { |
| logEvent({ |
| id: crypto.randomUUID(), |
| timestamp: new Date(), |
| eventType: 'metrics_collected', |
| status: 'success', |
| details: metrics, |
| resourceUsage: { |
| cpu: metrics.cpu, |
| memory: metrics.memory, |
| diskIO: metrics.diskIO, |
| networkIO: metrics.networkIO |
| } |
| }); |
| } |
| }, [config.enableLogging]); |
|
|
| |
| const getCPUUsage = (): number => { |
| |
| const baseUsage = 0.2 + Math.random() * 0.3; |
| const timeVariation = Math.sin(Date.now() / 10000) * 0.1; |
| return Math.max(0, Math.min(1, baseUsage + timeVariation)); |
| }; |
|
|
| const getMemoryUsage = (): number => { |
| |
| const baseUsage = 0.3; |
| const growth = (Date.now() - startTimeRef.current) / (1000 * 60 * 60) * 0.1; |
| const randomVariation = Math.random() * 0.1; |
| return Math.max(0, Math.min(1, baseUsage + growth + randomVariation)); |
| }; |
|
|
| const getDiskIOUsage = (): number => { |
| |
| const baseIO = 0.1 + Math.random() * 0.2; |
| return Math.max(0, Math.min(1, baseIO)); |
| }; |
|
|
| const getNetworkIOUsage = (): number => { |
| |
| const baseIO = 0.05 + Math.random() * 0.15; |
| return Math.max(0, Math.min(1, baseIO)); |
| }; |
|
|
| const getAverageResponseTime = (): number => { |
| |
| const baseTime = 100 + Math.random() * 200; |
| const loadFactor = currentMetrics.cpu * 500; |
| return baseTime + loadFactor; |
| }; |
|
|
| const calculateThroughput = (): number => { |
| |
| const elapsedSeconds = (Date.now() - startTimeRef.current) / 1000; |
| return elapsedSeconds > 0 ? requestCountRef.current / elapsedSeconds : 0; |
| }; |
|
|
| const calculateErrorRate = (): number => { |
| |
| return requestCountRef.current > 0 ? errorCountRef.current / requestCountRef.current : 0; |
| }; |
|
|
| |
| const logEvent = useCallback((entry: PerformanceLogEntry) => { |
| const logLevel = config.logLevel || 'info'; |
| const shouldLog = shouldLogLevel(entry.eventType, logLevel); |
| |
| if (!shouldLog) return; |
|
|
| setPerformanceLog(prev => { |
| const maxEntries = config.maxLogEntries || 1000; |
| const newLog = [...prev, entry]; |
| |
| |
| if (newLog.length > maxEntries) { |
| return newLog.slice(-maxEntries); |
| } |
| |
| return newLog; |
| }); |
| }, [config.logLevel, config.maxLogEntries]); |
|
|
| |
| const shouldLogLevel = (eventType: string, logLevel: string): boolean => { |
| const eventLevels: Record<string, number> = { |
| debug: 0, |
| info: 1, |
| warn: 2, |
| error: 3 |
| }; |
|
|
| const configLevels: Record<string, number> = { |
| debug: 0, |
| info: 1, |
| warn: 2, |
| error: 3 |
| }; |
|
|
| const eventLevel = eventType.includes('error') ? 3 : |
| eventType.includes('warn') || eventType.includes('anomaly') ? 2 : 1; |
| |
| return eventLevel >= (configLevels[logLevel] || 1); |
| }; |
|
|
| |
| const getPerformanceMetrics = useCallback(async (): Promise<PerformanceMetrics> => { |
| return currentMetrics; |
| }, [currentMetrics]); |
|
|
| |
| const detectAnomalies = useCallback(async (metrics: PerformanceMetrics): Promise<PerformanceLogEntry[]> => { |
| const anomalies: PerformanceLogEntry[] = []; |
| const thresholds = defaultThresholds; |
|
|
| |
| if (metrics.cpu > thresholds.cpu) { |
| anomalies.push({ |
| id: crypto.randomUUID(), |
| timestamp: new Date(), |
| eventType: 'cpu_anomaly', |
| status: 'warning', |
| details: { |
| currentCPU: metrics.cpu, |
| threshold: thresholds.cpu, |
| severity: metrics.cpu > 0.95 ? 'critical' : 'warning' |
| }, |
| resourceUsage: { |
| cpu: metrics.cpu, |
| memory: metrics.memory, |
| diskIO: metrics.diskIO, |
| networkIO: metrics.networkIO |
| } |
| }); |
| } |
|
|
| |
| if (metrics.memory > thresholds.memory) { |
| anomalies.push({ |
| id: crypto.randomUUID(), |
| timestamp: new Date(), |
| eventType: 'memory_anomaly', |
| status: 'warning', |
| details: { |
| currentMemory: metrics.memory, |
| threshold: thresholds.memory, |
| severity: metrics.memory > 0.95 ? 'critical' : 'warning' |
| }, |
| resourceUsage: { |
| cpu: metrics.cpu, |
| memory: metrics.memory, |
| diskIO: metrics.diskIO, |
| networkIO: metrics.networkIO |
| } |
| }); |
| } |
|
|
| |
| if (metrics.responseTime > thresholds.responseTime) { |
| anomalies.push({ |
| id: crypto.randomUUID(), |
| timestamp: new Date(), |
| eventType: 'response_time_anomaly', |
| status: 'warning', |
| details: { |
| currentResponseTime: metrics.responseTime, |
| threshold: thresholds.responseTime, |
| severity: metrics.responseTime > 10000 ? 'critical' : 'warning' |
| }, |
| resourceUsage: { |
| cpu: metrics.cpu, |
| memory: metrics.memory, |
| diskIO: metrics.diskIO, |
| networkIO: metrics.networkIO |
| } |
| }); |
| } |
|
|
| |
| if (metrics.errorRate > thresholds.errorRate) { |
| anomalies.push({ |
| id: crypto.randomUUID(), |
| timestamp: new Date(), |
| eventType: 'error_rate_anomaly', |
| status: 'warning', |
| details: { |
| currentErrorRate: metrics.errorRate, |
| threshold: thresholds.errorRate, |
| severity: metrics.errorRate > 0.1 ? 'critical' : 'warning' |
| }, |
| resourceUsage: { |
| cpu: metrics.cpu, |
| memory: metrics.memory, |
| diskIO: metrics.diskIO, |
| networkIO: metrics.networkIO |
| } |
| }); |
| } |
|
|
| |
| if (metricsHistoryRef.current.length >= 5) { |
| const recentMetrics = metricsHistoryRef.current.slice(-5); |
| |
| |
| const cpuTrend = recentMetrics.map(m => m.cpu); |
| const cpuIncrease = cpuTrend[cpuTrend.length - 1] - cpuTrend[0]; |
| if (cpuIncrease > 0.3) { |
| anomalies.push({ |
| id: crypto.randomUUID(), |
| timestamp: new Date(), |
| eventType: 'cpu_trend_anomaly', |
| status: 'warning', |
| details: { |
| trend: 'increasing', |
| increase: cpuIncrease, |
| timeWindow: '5 measurements' |
| }, |
| resourceUsage: { |
| cpu: metrics.cpu, |
| memory: metrics.memory, |
| diskIO: metrics.diskIO, |
| networkIO: metrics.networkIO |
| } |
| }); |
| } |
|
|
| |
| const memoryTrend = recentMetrics.map(m => m.memory); |
| const memoryIncrease = memoryTrend[memoryTrend.length - 1] - memoryTrend[0]; |
| if (memoryIncrease > 0.2 && memoryTrend.every((val, i) => i === 0 || val >= memoryTrend[i - 1])) { |
| anomalies.push({ |
| id: crypto.randomUUID(), |
| timestamp: new Date(), |
| eventType: 'memory_leak_anomaly', |
| status: 'warning', |
| details: { |
| trend: 'consistent_increase', |
| increase: memoryIncrease, |
| pattern: 'potential_memory_leak' |
| }, |
| resourceUsage: { |
| cpu: metrics.cpu, |
| memory: metrics.memory, |
| diskIO: metrics.diskIO, |
| networkIO: metrics.networkIO |
| } |
| }); |
| } |
| } |
|
|
| |
| anomalies.forEach(anomaly => { |
| logEvent(anomaly); |
| }); |
|
|
| return anomalies; |
| }, [logEvent]); |
|
|
| |
| const trackRequest = useCallback((isError: boolean = false) => { |
| requestCountRef.current++; |
| if (isError) { |
| errorCountRef.current++; |
| } |
| }, []); |
|
|
| |
| const getPerformanceStats = useCallback(() => { |
| const uptime = Date.now() - startTimeRef.current; |
| const avgCPU = metricsHistoryRef.current.length > 0 |
| ? metricsHistoryRef.current.reduce((sum, m) => sum + m.cpu, 0) / metricsHistoryRef.current.length |
| : 0; |
| const avgMemory = metricsHistoryRef.current.length > 0 |
| ? metricsHistoryRef.current.reduce((sum, m) => sum + m.memory, 0) / metricsHistoryRef.current.length |
| : 0; |
|
|
| return { |
| uptime, |
| totalRequests: requestCountRef.current, |
| totalErrors: errorCountRef.current, |
| currentMetrics, |
| averageMetrics: { |
| cpu: avgCPU, |
| memory: avgMemory |
| }, |
| logEntries: performanceLog.length, |
| isMonitoring |
| }; |
| }, [currentMetrics, performanceLog.length, isMonitoring]); |
|
|
| |
| const reset = useCallback(() => { |
| setPerformanceLog([]); |
| setCurrentMetrics({ |
| cpu: 0, |
| memory: 0, |
| diskIO: 0, |
| networkIO: 0, |
| responseTime: 0, |
| throughput: 0, |
| errorRate: 0 |
| }); |
| metricsHistoryRef.current = []; |
| requestCountRef.current = 0; |
| errorCountRef.current = 0; |
| startTimeRef.current = Date.now(); |
| }, []); |
|
|
| |
| useEffect(() => { |
| return () => { |
| if (monitoringIntervalRef.current) { |
| clearInterval(monitoringIntervalRef.current); |
| } |
| }; |
| }, []); |
|
|
| return { |
| performanceLog, |
| currentMetrics, |
| isMonitoring, |
| startMonitoring, |
| stopMonitoring, |
| getPerformanceMetrics, |
| detectAnomalies, |
| trackRequest, |
| getPerformanceStats, |
| logEvent, |
| reset |
| }; |
| }; |
|
|
|
|