document.addEventListener('DOMContentLoaded', () => { const logTableContainer = document.getElementById('log-table-container'); const logCountSpan = document.getElementById('log-count'); const levelRadios = document.querySelectorAll('input[name="log-level"]'); const sinceFilter = document.getElementById('since-filter'); const applyFiltersButton = document.getElementById('apply-filters'); const togglePollingButton = document.getElementById('toggle-polling'); const pollingStatusSpan = document.getElementById('polling-status'); const POLLING_INTERVAL_MS = 5000; const serviceUuid = logTableContainer.dataset.serviceUuid; // Get service UUID from data attribute let currentLevel = ''; let currentSince = ''; let pollingIntervalId = null; let isPollingActive = true; // Start active by default // Function to update the UI elements related to polling status function updatePollingStatusUI() { if (isPollingActive) { togglePollingButton.textContent = 'Pause Auto-Refresh'; pollingStatusSpan.textContent = `Auto-refresh: ON (every ${POLLING_INTERVAL_MS / 1000} seconds)`; } else { togglePollingButton.textContent = 'Resume Auto-Refresh'; pollingStatusSpan.textContent = 'Auto-refresh: OFF (paused)'; } } // Function to start polling function startPolling() { if (pollingIntervalId) { clearInterval(pollingIntervalId); // Clear any existing interval to prevent duplicates } isPollingActive = true; fetchLogs(); // Fetch immediately when starting/resuming pollingIntervalId = setInterval(fetchLogs, POLLING_INTERVAL_MS); updatePollingStatusUI(); } // Function to stop polling function stopPolling() { isPollingActive = false; clearInterval(pollingIntervalId); pollingIntervalId = null; updatePollingStatusUI(); } async function fetchLogs() { console.log('Fetching logs with params:', { level: currentLevel, since: currentSince }); try { const params = new URLSearchParams({ format: 'json', limit: '100' }); if (currentLevel) params.append('level', currentLevel); if (currentSince) params.append('since', currentSince); // Use window.API_BASE_PATH for dynamic base URL const url = `${window.API_BASE_PATH}/${serviceUuid}/logs?${params.toString()}`; console.log('Fetch URL:', url); const response = await fetch(url); console.log('Response status:', response.status); console.log('Response Content-Type:', response.headers.get('Content-Type')); if (!response.ok) { const errorText = await response.text(); console.error('Raw response text on error:', errorText.substring(0, 500) + (errorText.length > 500 ? '...' : '')); if (response.status === 404) { console.error(`404 Not Found: Service UUID mismatch. Current page UUID: ${serviceUuid}. Server UUID might have changed. Please hard refresh the page.`); logTableContainer.innerHTML = `

Error: Service UUID mismatch. Please hard refresh your browser tab (Ctrl+F5 or Cmd+Shift+R).

`; } else { // Log the status code to help debug console.error(`HTTP error! status: ${response.status} - ${errorText}`); logTableContainer.innerHTML = `

Error loading logs. HTTP Status: ${response.status}. Please check server connection or hard refresh.

`; } stopPolling(); // Stop polling on error return; } // Attempt to parse JSON. This is where the error would occur if the content is HTML. const data = await response.json(); console.log('Received logs:', data.logs.length); renderLogTable(data.logs); logCountSpan.textContent = data.log_count; } catch (error) { console.error("Error fetching logs (JSON parsing or other):", error); logTableContainer.innerHTML = '

Error loading logs. Failed to parse response or other network issue. See console for details.

'; stopPolling(); // Stop polling on error } } function renderLogTable(logs) { console.log('Rendering logs:', logs.length); logTableContainer.innerHTML = ''; // Clear existing content before rendering if (logs.length === 0) { logTableContainer.innerHTML = '

No logs available.

'; return; } const table = document.createElement('table'); table.classList.add('log-table'); const thead = document.createElement('thead'); const headerRow = document.createElement('tr'); const headers = [ { key: 'timestamp', label: 'Timestamp' }, { key: 'level', label: 'Level' }, { key: 'message', label: 'Message' }, { key: 'extra', label: 'Extra' } ]; headers.forEach(header => { const th = document.createElement('th'); th.textContent = header.label; th.dataset.key = header.key; th.classList.add('sortable'); th.addEventListener('click', () => sortTable(header.key)); headerRow.appendChild(th); }); thead.appendChild(headerRow); table.appendChild(thead); const tbody = document.createElement('tbody'); logs.forEach(log => { const row = document.createElement('tr'); row.innerHTML = ` ${new Date(log.timestamp).toLocaleString()} ${log.level || 'N/A'} ${escapeHtml(log.message)} ${log.extra ? `
Show JSON
` : '-'} `; tbody.appendChild(row); }); table.appendChild(tbody); logTableContainer.appendChild(table); document.querySelectorAll('.json-toggle').forEach(toggle => { toggle.addEventListener('click', () => { const jsonContent = toggle.nextElementSibling; const isHidden = jsonContent.style.display === 'none'; jsonContent.style.display = isHidden ? 'block' : 'none'; toggle.textContent = isHidden ? 'Hide JSON' : 'Show JSON'; }); }); } function sortTable(key) { const table = document.querySelector('.log-table'); const tbody = table.querySelector('tbody'); const rows = Array.from(tbody.querySelectorAll('tr')); const isAsc = table.dataset.sortKey === key && table.dataset.sortDir !== 'desc'; table.dataset.sortKey = key; table.dataset.sortDir = isAsc ? 'desc' : 'asc'; rows.sort((a, b) => { let aValue, bValue; if (key === 'timestamp') { aValue = new Date(a.cells[0].textContent); bValue = new Date(b.cells[0].textContent); } else if (key === 'level') { aValue = a.cells[1].textContent.toLowerCase(); bValue = b.cells[1].textContent.toLowerCase(); } else if (key === 'message') { aValue = a.cells[2].textContent.toLowerCase(); bValue = b.cells[2].textContent.toLowerCase(); } else { aValue = a.cells[3].querySelector('.json-content')?.textContent || ''; bValue = b.cells[3].querySelector('.json-content')?.textContent || ''; } return isAsc ? (aValue < bValue ? -1 : 1) : (aValue > bValue ? -1 : 1); }); tbody.innerHTML = ''; rows.forEach(row => tbody.appendChild(row)); } function escapeHtml(str) { const div = document.createElement('div'); div.textContent = str; return div.innerHTML; } // Event listener for the "Apply Filters" button applyFiltersButton.addEventListener('click', () => { const selectedRadio = document.querySelector('input[name="log-level"]:checked'); currentLevel = selectedRadio ? selectedRadio.value : ''; currentSince = sinceFilter.value; console.log('Applying filters:', { level: currentLevel, since: currentSince }); // When filters are applied, always restart polling to fetch new data startPolling(); }); // Event listener for the new "Toggle Polling" button togglePollingButton.addEventListener('click', () => { if (isPollingActive) { stopPolling(); } else { startPolling(); } }); console.log('Initializing logs page'); // Initial call to start polling when the page loads startPolling(); });