trying to make the logs view to have better date format.

This commit is contained in:
Kalzu Rekku
2025-06-12 11:16:26 +03:00
parent 1cbf9311e5
commit 4e31fe7cce
7 changed files with 71 additions and 20 deletions

View File

@ -2,7 +2,7 @@ import os
import uuid
import json
import logging
from datetime import datetime, timezone
from datetime import datetime, timezone, timedelta
from fastapi import FastAPI, Request, status, Query
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
@ -12,6 +12,7 @@ from typing import Dict, List, Annotated
import uuid as uuid_lib
from collections import deque
from dateutil.parser import isoparse
from pythonjsonlogger import jsonlogger
import sys
@ -61,7 +62,7 @@ class LogBuffer:
# Apply level filter
if level and level.strip():
level = level.upper()
valid_levels = {'INFO', 'WARNING', 'ERROR', 'DEBUG'} # Added DEBUG for completeness
valid_levels = {'INFO', 'WARNING', 'ERROR', 'DEBUG'}
if level in valid_levels:
logs = [log for log in logs if log['level'].upper() == level]
else:
@ -69,8 +70,16 @@ class LogBuffer:
# Apply since filter
if since:
try:
# Handle 'Z' for UTC and ensure timezone awareness for comparison
since_dt = datetime.fromisoformat(since.replace('Z', '+00:00')).astimezone(timezone.utc)
# Use isoparse for robust parsing of ISO 8601 strings
since_dt = isoparse(since)
# If the parsed datetime is naive (no timezone info), assume it's UTC
if since_dt.tzinfo is None:
since_dt = since_dt.replace(tzinfo=timezone.utc)
else:
# If it has timezone info, convert it to UTC for consistent comparison
since_dt = since_dt.astimezone(timezone.utc)
logs = [log for log in logs if
datetime.fromisoformat(log['timestamp'].replace('Z', '+00:00')).astimezone(timezone.utc) >= since_dt]
except ValueError:
@ -90,6 +99,13 @@ if not logger.handlers:
logger.addHandler(logHandler)
logger.addHandler(BufferHandler())
# Configure Uvicorn's loggers to propagate to the root logger
# This ensures Uvicorn's startup and access logs are captured by our BufferHandler
logging.getLogger("uvicorn").propagate = True
logging.getLogger("uvicorn.access").propagate = True
logging.getLogger("uvicorn.error").propagate = True
# --- FastAPI Application ---
app = FastAPI(
title="Node Monitoring System",
@ -139,6 +155,7 @@ LOAD_AVG_CRITICAL_THRESHOLD = 3.0
MEMORY_WARNING_THRESHOLD = 75.0
MEMORY_CRITICAL_THRESHOLD = 90.0
LAST_SEEN_CRITICAL_THRESHOLD_SECONDS = 30
NODE_INACTIVE_REMOVAL_THRESHOLD_SECONDS = 300 # Remove node from UI after 5 minutes of inactivity
def get_node_health(node_data: Dict) -> str:
last_seen_str = node_data.get("last_seen")
@ -330,13 +347,25 @@ async def update_node_status(
@app.get("/nodes/status")
async def get_all_nodes_status():
logger.info("Fetching all nodes status for UI.")
response_nodes = []
# Prune inactive nodes from known_nodes_db before processing
current_time_utc = datetime.now(timezone.utc)
nodes_to_remove = []
for node_uuid, data in known_nodes_db.items():
last_seen_dt = datetime.fromisoformat(data["last_seen"]).replace(tzinfo=timezone.utc)
if (current_time_utc - last_seen_dt).total_seconds() > NODE_INACTIVE_REMOVAL_THRESHOLD_SECONDS:
nodes_to_remove.append(node_uuid)
logger.info(f"Removing inactive node {node_uuid} from known_nodes_db.")
for node_uuid in nodes_to_remove:
known_nodes_db.pop(node_uuid)
response_nodes = []
for node_uuid, data in known_nodes_db.items():
current_health = get_node_health(data)
connections = {}
for target_uuid in known_nodes_db:
for target_uuid in known_nodes_db: # Only iterate over currently active nodes
if target_uuid != node_uuid:
ping_data = database.get_ping_data(node_uuid, target_uuid, start_time="-300s")
latency_ms = None
@ -363,3 +392,4 @@ async def get_all_nodes_status():
@app.get("/health")
async def health_check():
return {"status": "ok"}
# --- END OF FILE main.py ---

View File

@ -25,15 +25,27 @@ document.addEventListener('DOMContentLoaded', () => {
const response = await fetch(url);
console.log('Response status:', response.status);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
const errorText = await response.text(); // Try to get response body as text
console.error('Response text on error:', errorText); // Log it
// If the server returns a 404, it might be due to a stale UUID.
// Log a more specific message.
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 = `<p class="loading-message error-message">Error: Service UUID mismatch. Please hard refresh your browser tab (Ctrl+F5 or Cmd+Shift+R).</p>`;
} else {
// Log the status code to help debug
console.error(`HTTP error! status: ${response.status} - ${errorText}`);
logTableContainer.innerHTML = `<p class="loading-message error-message">Error loading logs. HTTP Status: ${response.status}. Please check server connection or hard refresh.</p>`;
}
return; // Stop further processing if error
}
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:", error);
logTableContainer.innerHTML = '<p class="loading-message">Error loading logs. Please check server connection.</p>';
console.error("Error fetching logs (JSON parsing or other):", error);
logTableContainer.innerHTML = '<p class="loading-message error-message">Error loading logs. Failed to parse response or other network issue. See console for details.</p>';
}
}
@ -138,9 +150,9 @@ document.addEventListener('DOMContentLoaded', () => {
applyFiltersButton.addEventListener('click', () => {
const selectedRadio = document.querySelector('input[name="log-level"]:checked');
currentLevel = selectedRadio ? selectedRadio.value : '';
const sinceValue = sinceFilter.value;
// Convert local datetime input to ISO string for backend, handling potential timezone issues
currentSince = sinceValue ? new Date(sinceValue).toISOString().replace(/\.\d{3}Z$/, 'Z') : ''; // Ensure 'Z' for UTC
// 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();
});

View File

@ -19,8 +19,8 @@ document.addEventListener('DOMContentLoaded', () => {
}
function renderNodeGrid(nodes) {
nodeGridContainer.innerHTML = ''; // Clear existing content
nodeCountSpan.textContent = nodes.length; // Update total node count
nodeGridContainer.innerHTML = '';
nodeCountSpan.textContent = nodes.length;
if (nodes.length === 0) {
nodeGridContainer.innerHTML = '<p class="loading-message">No nodes reporting yet. Start a client!</p>';
@ -132,3 +132,4 @@ document.addEventListener('DOMContentLoaded', () => {
fetchNodeData();
setInterval(fetchNodeData, POLLING_INTERVAL_MS);
});

View File

@ -40,6 +40,8 @@ body {
margin-bottom: 20px;
width: 80vw;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
}
h1 {
@ -63,13 +65,17 @@ code {
}
#node-grid-container, #log-table-container {
width: 80vw;
max-width: 1200px;
width: 95vw;
max-width: 1600px;
min-width: 400px;
padding: 20px;
background-color: var(--nord3);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
margin-bottom: 20px;
margin-left: auto;
margin-right: auto;
overflow-x: auto;
}
.connection-grid {

View File

@ -10,7 +10,7 @@
<div class="header-container">
<h1>Node Monitoring System</h1>
<p>Total Nodes: <span id="node-count">0</span></p>
<p>Service UUID: <code>{{ service_uuid }}</code></p> <!-- Always display service UUID -->
<p>Service UUID: <code>{{ service_uuid }}</code></p>
</div>
<div id="node-grid-container">

View File

@ -2,7 +2,6 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Removed: <meta http-equiv="refresh" content="5"> -->
<title>Node Monitor Logs - {{ service_uuid }}</title>
<link rel="stylesheet" href="{{ url_for('static', path='/style.css') }}">
</head>
@ -19,8 +18,8 @@
<label><input type="radio" name="log-level" value="WARNING"> WARNING</label>
<label><input type="radio" name="log-level" value="ERROR"> ERROR</label>
</fieldset>
<label for="since-filter">Since (ISO):</label>
<input type="datetime-local" id="since-filter" placeholder="2025-06-11T13:32:00">
<label for="since-filter">Since (UTC ISO 8601):</label>
<input type="datetime-local" id="since-filter" placeholder="e.g., 2025-06-11T13:32 (UTC)">
<button id="apply-filters">Apply</button>
</div>
</div>
@ -64,3 +63,4 @@
<script src="{{ url_for('static', path='/logs.js') }}"></script>
</body>
</html>

View File

@ -4,3 +4,5 @@ rrdtool==0.1.16
jinja2==3.1.2
python-multipart==0.0.6
python-json-logger==2.0.7
python-dateutil==2.9.0.post0