diff --git a/Dockerfile b/Dockerfile index f7e1b46..484a05d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -76,3 +76,4 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ # Run the application using the virtual environment's python interpreter CMD ["/opt/venv/bin/python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers", "--forwarded-allow-ips", "*"] +#CMD ["/opt/venv/bin/python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/app/main.py b/app/main.py index 3ab3c3c..8848459 100644 --- a/app/main.py +++ b/app/main.py @@ -346,6 +346,7 @@ async def read_root(request: Request): "uptime_seconds": data.get("uptime_seconds"), "formatted_uptime": format_uptime(data.get("uptime_seconds")), # Pre-format uptime for HTML "load_avg": data.get("load_avg"), + "load_avg_fmt": [f"{x:.2f}" for x in data.get("load_avg", [])] if data.get("load_avg") else None, "memory_usage_percent": data.get("memory_usage_percent"), "connections": connections, } diff --git a/app/web/static/logs.js b/app/web/static/logs.js index a9e474f..3710bf9 100644 --- a/app/web/static/logs.js +++ b/app/web/static/logs.js @@ -4,12 +4,47 @@ document.addEventListener('DOMContentLoaded', () => { 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 { @@ -24,13 +59,11 @@ document.addEventListener('DOMContentLoaded', () => { 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')); // NEW: Log Content-Type + console.log('Response Content-Type:', response.headers.get('Content-Type')); if (!response.ok) { - const errorText = await response.text(); // Try to get response body as text - console.error('Raw response text on error:', errorText.substring(0, 500) + (errorText.length > 500 ? '...' : '')); // Log first 500 chars - // If the server returns a 404, it might be due to a stale UUID. - // Log a more specific message. + 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 = `
`; @@ -39,7 +72,8 @@ document.addEventListener('DOMContentLoaded', () => { console.error(`HTTP error! status: ${response.status} - ${errorText}`); logTableContainer.innerHTML = ` `; } - return; // Stop further processing if error + stopPolling(); // Stop polling on error + return; } // Attempt to parse JSON. This is where the error would occur if the content is HTML. @@ -50,6 +84,7 @@ document.addEventListener('DOMContentLoaded', () => { } catch (error) { console.error("Error fetching logs (JSON parsing or other):", error); logTableContainer.innerHTML = ' '; + stopPolling(); // Stop polling on error } } @@ -151,19 +186,26 @@ document.addEventListener('DOMContentLoaded', () => { 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 : ''; - // Send the datetime-local value directly. It's already in YYYY-MM-DDTHH:mm (24-hour) format. - // The backend will interpret this as UTC. currentSince = sinceFilter.value; console.log('Applying filters:', { level: currentLevel, since: currentSince }); - fetchLogs(); + // 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'); - // Call fetchLogs immediately on page load to populate the table with fresh data - // and handle the initial refresh logic. - fetchLogs(); - setInterval(fetchLogs, POLLING_INTERVAL_MS); + // Initial call to start polling when the page loads + startPolling(); }); diff --git a/app/web/static/script.js b/app/web/static/script.js index 670205f..c0bed9a 100644 --- a/app/web/static/script.js +++ b/app/web/static/script.js @@ -72,16 +72,30 @@ document.addEventListener('DOMContentLoaded', () => {UUID: ${rowNode.uuid}
IP: ${rowNode.ip}
Last Seen: ${new Date(rowNode.last_seen).toLocaleTimeString()}
-Uptime: ${rowNode.uptime_seconds ? formatUptime(rowNode.uptime_seconds) : 'N/A'}
-Load Avg (1m, 5m, 15m): ${rowNode.load_avg ? rowNode.load_avg.map(l => l.toFixed(2)).join(', ') : 'N/A'}
-Memory Usage: ${rowNode.memory_usage_percent ? rowNode.memory_usage_percent.toFixed(2) + '%' : 'N/A'}
+Uptime: ${rowNode.uptime_seconds =! null ? formatUptime(rowNode.uptime_seconds) : 'N/A'}
+Load Avg (1m, 5m, 15m): ${ + Array.isArray(rowNode.load_avg) + ? rowNode.load_avg.map(l => l.toFixed(2)).join(', ') + : 'N/A' + }
+Memory Usage: ${ + rowNode.memory_usage_percent != null + ? rowNode.memory_usage_percent.toFixed(2) + '%' + : 'N/A' + }
`; } else { // Off-diagonal: show ping latency - const latency = rowNode.connections && colNode.uuid in rowNode.connections && rowNode.connections[colNode.uuid] !== null ? rowNode.connections[colNode.uuid] : null; - const displayLatency = latency !== null && !isNaN(latency) ? `${latency.toFixed(1)} ms` : 'N/A'; - const latencyClass = latency !== null && !isNaN(latency) ? getLatencyClass(latency) : 'latency-unavailable'; + const latency = ( + rowNode.connections && + colNode.uuid in rowNode.connections && + rowNode.connections[colNode.uuid] !== null + ) ? rowNode.connections[colNode.uuid] : null; + + const displayLatency = (latency != null && !isNaN(latency)) ? `${latency.toFixed(1)} ms` : 'N/A'; + const latencyClass = (latency != null && !isNaN(latency)) ? getLatencyClass(latency) : 'latency-unavailable'; + cell.classList.add(latencyClass); cell.innerHTML = `IP: {{ row_node.ip }}
Last Seen: {{ row_node.formatted_last_seen }}
Uptime: {{ row_node.formatted_uptime }}
-Load Avg (1m, 5m, 15m): {{ row_node.load_avg | map('%.2f' | format) | join(', ') if row_node.load_avg else 'N/A' }}
+Load Avg (1m, 5m, 15m): {{ row_node.load_avg_fmt | join(', ') if row_node.load_avg_fmt else 'N/A' }}
Memory Usage: {{ '%.2f' | format(row_node.memory_usage_percent) + '%' if row_node.memory_usage_percent is not none else 'N/A' }}
{% else %} diff --git a/app/web/templates/logs.html b/app/web/templates/logs.html index 7b6296b..dd684e7 100644 --- a/app/web/templates/logs.html +++ b/app/web/templates/logs.html @@ -9,7 +9,7 @@Service UUID: {{ service_uuid }}
Total Logs: {{ log_count }} (Auto-refreshing every 5 seconds)
+Total Logs: {{ log_count }} ()