/* Logs page JavaScript */ // Logs State let isStreaming = true; let autoScroll = true; let logCounts = { debug: 0, info: 0, warn: 0, error: 0, fatal: 0 }; let searchDebounceTimer = null; let currentFilters = { level: 'all', service: 'all', search: '' }; let logsWs = null; function initLogsWebSocket() { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; logsWs = new WebSocket(`${protocol}//${window.location.host}/ws/logs`); logsWs.onopen = function() { updateLogsConnectionStatus('connected', 'Connected'); }; logsWs.onclose = function() { updateLogsConnectionStatus('disconnected', 'Disconnected'); // Reconnect after 3 seconds setTimeout(initLogsWebSocket, 3000); }; logsWs.onerror = function() { updateLogsConnectionStatus('disconnected', 'Error'); }; logsWs.onmessage = function(event) { if (!isStreaming) return; try { const logData = JSON.parse(event.data); appendLog(logData); } catch (e) { console.error('Failed to parse log message:', e); } }; } function updateLogsConnectionStatus(status, text) { const statusEl = document.getElementById('connection-status'); if (statusEl) { statusEl.className = `connection-status ${status}`; statusEl.querySelector('.status-text').textContent = text; } } function appendLog(log) { const stream = document.getElementById('log-stream'); if (!stream) return; const placeholder = stream.querySelector('.log-placeholder'); if (placeholder) { placeholder.remove(); } const entry = document.createElement('div'); entry.className = 'log-entry'; entry.dataset.level = log.level || 'info'; entry.dataset.service = log.service || 'unknown'; entry.dataset.id = log.id || Date.now(); entry.innerHTML = ` ${(log.level || 'INFO').toUpperCase()} ${log.service || 'unknown'} `; // Store full log data for detail view entry._logData = log; // Apply current filters if (!matchesLogFilters(entry)) { entry.classList.add('hidden'); } stream.appendChild(entry); // Update counts const level = log.level || 'info'; if (logCounts[level] !== undefined) { logCounts[level]++; const countEl = document.getElementById(`${level}-count`); if (countEl) countEl.textContent = logCounts[level]; } const totalEl = document.getElementById('total-count'); if (totalEl) { totalEl.textContent = Object.values(logCounts).reduce((a, b) => a + b, 0); } // Auto-scroll to bottom if (autoScroll) { stream.scrollTop = stream.scrollHeight; } // Limit log entries to prevent memory issues const maxEntries = 1000; while (stream.children.length > maxEntries) { const removed = stream.firstChild; if (removed._logData) { const removedLevel = removed._logData.level || 'info'; if (logCounts[removedLevel] > 0) { logCounts[removedLevel]--; } } stream.removeChild(removed); } } function formatLogTimestamp(timestamp) { if (!timestamp) return '--'; const date = new Date(timestamp); return date.toISOString().replace('T', ' ').slice(0, 23); } function escapeLogHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } function matchesLogFilters(entry) { // Level filter if (currentFilters.level !== 'all' && entry.dataset.level !== currentFilters.level) { return false; } // Service filter if (currentFilters.service !== 'all' && entry.dataset.service !== currentFilters.service) { return false; } // Search filter if (currentFilters.search) { const text = entry.textContent.toLowerCase(); if (!text.includes(currentFilters.search.toLowerCase())) { return false; } } return true; } function applyLogFilters() { currentFilters.level = document.getElementById('log-level-filter')?.value || 'all'; currentFilters.service = document.getElementById('service-filter')?.value || 'all'; const entries = document.querySelectorAll('.log-entry'); entries.forEach(entry => { if (matchesLogFilters(entry)) { entry.classList.remove('hidden'); } else { entry.classList.add('hidden'); } }); } function debounceLogSearch(value) { clearTimeout(searchDebounceTimer); searchDebounceTimer = setTimeout(() => { currentFilters.search = value; applyLogFilters(); }, 300); } function toggleStream() { isStreaming = !isStreaming; const btn = document.getElementById('stream-toggle'); if (!btn) return; if (isStreaming) { btn.classList.remove('paused'); btn.innerHTML = ` Pause `; } else { btn.classList.add('paused'); btn.innerHTML = ` Resume `; } } function clearLogs() { if (confirm('Are you sure you want to clear all logs?')) { const stream = document.getElementById('log-stream'); if (!stream) return; stream.innerHTML = `
Logs cleared
New logs will appear here