// Monitoring Page Module class MonitoringPage { constructor() { this.isPaused = false; this.requestStream = []; this.systemLogs = []; this.init(); } async init() { // Load initial data await this.loadSystemMetrics(); await this.loadCharts(); // Setup event listeners this.setupEventListeners(); // Setup WebSocket subscriptions this.setupWebSocketSubscriptions(); // Start simulated updates for demo this.startDemoUpdates(); } async loadSystemMetrics() { const container = document.getElementById('system-metrics'); if (!container) return; const metrics = [ { label: 'CPU Usage', value: '24%', trend: 'down', color: 'success' }, { label: 'Memory Usage', value: '1.8 GB', trend: 'stable', color: 'warning' }, { label: 'Disk I/O', value: '45 MB/s', trend: 'up', color: 'primary' }, { label: 'Network', value: '125 KB/s', trend: 'up', color: 'info' }, { label: 'Active Connections', value: '42', trend: 'stable', color: 'success' }, { label: 'Queue Length', value: '3', trend: 'down', color: 'success' } ]; container.innerHTML = metrics.map(metric => `
${metric.label}
${metric.value}
`).join(''); // Add CSS for metrics this.addMetricStyles(); } addMetricStyles() { const style = document.createElement('style'); style.textContent = ` .metric-item { display: flex; align-items: center; justify-content: space-between; padding: 0.75rem; border-bottom: 1px solid var(--border-color); } .metric-item:last-child { border-bottom: none; } .metric-label { font-size: 0.875rem; color: var(--text-secondary); } .metric-value { font-weight: 600; color: var(--text-primary); } .metric-trend { width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 0.75rem; } .metric-trend.up { background-color: rgba(239, 68, 68, 0.1); color: var(--danger); } .metric-trend.down { background-color: rgba(16, 185, 129, 0.1); color: var(--success); } .metric-trend.stable { background-color: rgba(100, 116, 139, 0.1); color: var(--text-secondary); } .monitoring-stream { height: 300px; overflow-y: auto; border: 1px solid var(--border-color); border-radius: var(--border-radius-sm); padding: 0.5rem; background-color: var(--bg-secondary); } .stream-entry { display: flex; align-items: center; gap: 0.75rem; padding: 0.5rem; border-bottom: 1px solid var(--border-color); transition: background-color 0.2s ease; } .stream-entry:last-child { border-bottom: none; } .stream-entry.highlight { background-color: rgba(37, 99, 235, 0.05); } .stream-entry-time { font-size: 0.75rem; color: var(--text-light); min-width: 60px; } .stream-entry-icon { font-size: 0.875rem; width: 24px; text-align: center; } .stream-entry-content { flex: 1; font-size: 0.875rem; } .stream-entry-details { font-size: 0.75rem; color: var(--text-secondary); margin-top: 0.125rem; } .log-stream { height: 400px; overflow-y: auto; border: 1px solid var(--border-color); border-radius: var(--border-radius-sm); padding: 0.5rem; background-color: var(--bg-secondary); font-family: monospace; font-size: 0.75rem; } .log-entry { display: flex; align-items: center; gap: 0.75rem; padding: 0.25rem 0.5rem; border-bottom: 1px solid var(--border-color); } .log-entry:last-child { border-bottom: none; } .log-time { color: var(--text-light); min-width: 80px; } .log-level { width: 24px; text-align: center; } .log-info .log-level { color: var(--info); } .log-warn .log-level { color: var(--warning); } .log-error .log-level { color: var(--danger); } .log-debug .log-level { color: var(--text-light); } .log-message { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } `; document.head.appendChild(style); } async loadCharts() { await this.loadResponseTimeChart(); await this.loadErrorRateChart(); await this.loadRateLimitChart(); } async loadResponseTimeChart() { try { // Generate demo data for response time const labels = Array.from({ length: 20 }, (_, i) => `${i + 1}m`); const data = { labels: labels, datasets: [{ label: 'Response Time (ms)', data: labels.map(() => Math.floor(Math.random() * 200) + 300), color: '#3b82f6', fill: true }] }; window.chartManager.createLineChart('response-time-chart', data, { scales: { y: { title: { display: true, text: 'Milliseconds' } } } }); } catch (error) { console.error('Error loading response time chart:', error); } } async loadErrorRateChart() { try { const labels = Array.from({ length: 20 }, (_, i) => `${i + 1}m`); const data = { labels: labels, datasets: [{ label: 'Error Rate (%)', data: labels.map(() => Math.random() * 5), color: '#ef4444', fill: true }] }; window.chartManager.createLineChart('error-rate-chart', data, { scales: { y: { title: { display: true, text: 'Percentage' }, ticks: { callback: function(value) { return value + '%'; } } } } }); } catch (error) { console.error('Error loading error rate chart:', error); } } async loadRateLimitChart() { try { const labels = ['Web App', 'Mobile App', 'API Integration', 'Internal Tools', 'Testing']; const data = { labels: labels, datasets: [{ label: 'Rate Limit Usage', data: [65, 45, 78, 34, 60], color: '#10b981' }] }; window.chartManager.createBarChart('rate-limit-chart', data, { scales: { y: { title: { display: true, text: 'Percentage' }, ticks: { callback: function(value) { return value + '%'; } } } } }); } catch (error) { console.error('Error loading rate limit chart:', error); } } setupEventListeners() { // Pause/resume monitoring button const pauseBtn = document.getElementById('pause-monitoring'); if (pauseBtn) { pauseBtn.addEventListener('click', () => { this.togglePause(); }); } } setupWebSocketSubscriptions() { if (!window.wsManager) return; // Subscribe to request updates window.wsManager.subscribe('requests', (request) => { if (!this.isPaused) { this.addToRequestStream(request); } }); // Subscribe to log updates window.wsManager.subscribe('logs', (log) => { if (!this.isPaused) { this.addToLogStream(log); } }); // Subscribe to metric updates window.wsManager.subscribe('metrics', (metric) => { if (!this.isPaused) { this.updateCharts(metric); } }); } togglePause() { this.isPaused = !this.isPaused; const pauseBtn = document.getElementById('pause-monitoring'); if (pauseBtn) { if (this.isPaused) { pauseBtn.innerHTML = ' Resume'; pauseBtn.classList.remove('btn-secondary'); pauseBtn.classList.add('btn-success'); if (window.authManager) { window.authManager.showToast('Monitoring paused', 'warning'); } } else { pauseBtn.innerHTML = ' Pause'; pauseBtn.classList.remove('btn-success'); pauseBtn.classList.add('btn-secondary'); if (window.authManager) { window.authManager.showToast('Monitoring resumed', 'success'); } } } } addToRequestStream(request) { const streamElement = document.getElementById('request-stream'); if (!streamElement) return; // Create entry const entry = document.createElement('div'); entry.className = 'stream-entry'; // Format time const time = new Date().toLocaleTimeString(); // Determine icon based on status let icon = 'question-circle'; let color = 'var(--text-secondary)'; if (request.status === 'success') { icon = 'check-circle'; color = 'var(--success)'; } else if (request.status === 'error') { icon = 'exclamation-circle'; color = 'var(--danger)'; } entry.innerHTML = `
${time}
${request.client_id || 'Unknown'} → ${request.provider || 'Unknown'} (${request.model || 'Unknown'})
${request.total_tokens || request.tokens || 0} tokens • ${request.duration_ms || request.duration || 0}ms
`; // Add to top of stream streamElement.insertBefore(entry, streamElement.firstChild); // Store in memory (limit to 100) this.requestStream.unshift({ time, request, element: entry }); if (this.requestStream.length > 100) { const oldEntry = this.requestStream.pop(); if (oldEntry.element.parentNode) { oldEntry.element.remove(); } } // Add highlight animation entry.classList.add('highlight'); setTimeout(() => entry.classList.remove('highlight'), 1000); } addToLogStream(log) { const logElement = document.getElementById('system-logs'); if (!logElement) return; // Create entry const entry = document.createElement('div'); entry.className = `log-entry log-${log.level || 'info'}`; // Format time const time = new Date().toLocaleTimeString(); // Determine icon based on level let icon = 'info-circle'; if (log.level === 'error') icon = 'exclamation-circle'; if (log.level === 'warn') icon = 'exclamation-triangle'; if (log.level === 'debug') icon = 'bug'; entry.innerHTML = `
${time}
${log.message || ''}
`; // Add to top of stream logElement.insertBefore(entry, logElement.firstChild); // Store in memory (limit to 100) this.systemLogs.unshift({ time, log, element: entry }); if (this.systemLogs.length > 100) { const oldEntry = this.systemLogs.pop(); if (oldEntry.element.parentNode) { oldEntry.element.remove(); } } } updateCharts(metric) { // Update charts with new metric data if (metric.type === 'response_time' && window.chartManager.charts.has('response-time-chart')) { this.updateResponseTimeChart(metric.value); } if (metric.type === 'error_rate' && window.chartManager.charts.has('error-rate-chart')) { this.updateErrorRateChart(metric.value); } } updateResponseTimeChart(value) { window.chartManager.addDataPoint('response-time-chart', value); } updateErrorRateChart(value) { window.chartManager.addDataPoint('error-rate-chart', value); } startDemoUpdates() { // Demo updates disabled — real data comes via WebSocket subscriptions } simulateRequest() { const clients = ['client-1', 'client-2', 'client-3', 'client-4', 'client-5']; const providers = ['OpenAI', 'Gemini', 'DeepSeek', 'Grok']; const models = ['gpt-4', 'gpt-3.5-turbo', 'gemini-pro', 'deepseek-chat', 'grok-beta']; const statuses = ['success', 'success', 'success', 'error', 'warning']; // Mostly success const request = { client_id: clients[Math.floor(Math.random() * clients.length)], provider: providers[Math.floor(Math.random() * providers.length)], model: models[Math.floor(Math.random() * models.length)], tokens: Math.floor(Math.random() * 2000) + 100, duration: Math.floor(Math.random() * 1000) + 100, status: statuses[Math.floor(Math.random() * statuses.length)], timestamp: Date.now() }; this.addToRequestStream(request); } simulateLog() { const levels = ['info', 'info', 'info', 'warn', 'error']; const messages = [ 'Request processed successfully', 'Cache hit for model gpt-4', 'Rate limit check passed', 'High latency detected for DeepSeek provider', 'API key validation failed', 'Database connection pool healthy', 'New client registered: client-7', 'Backup completed successfully', 'Memory usage above 80% threshold', 'Provider Grok is offline' ]; const log = { level: levels[Math.floor(Math.random() * levels.length)], message: messages[Math.floor(Math.random() * messages.length)], timestamp: Date.now() }; this.addToLogStream(log); } simulateMetric() { const metricTypes = ['response_time', 'error_rate']; const type = metricTypes[Math.floor(Math.random() * metricTypes.length)]; let value; if (type === 'response_time') { value = Math.floor(Math.random() * 200) + 300; // 300-500ms } else { value = Math.random() * 5; // 0-5% } this.updateCharts({ type, value }); } clearStreams() { const streamElement = document.getElementById('request-stream'); const logElement = document.getElementById('system-logs'); if (streamElement) { streamElement.innerHTML = ''; this.requestStream = []; } if (logElement) { logElement.innerHTML = ''; this.systemLogs = []; } } refresh() { this.loadSystemMetrics(); this.loadCharts(); this.clearStreams(); if (window.authManager) { window.authManager.showToast('Monitoring refreshed', 'success'); } } } // Initialize monitoring page when needed window.initMonitoring = async () => { window.monitoringPage = new MonitoringPage(); }; // Export for use in other modules if (typeof module !== 'undefined' && module.exports) { module.exports = MonitoringPage; }