Files
HAMeter/hameter/web/templates/logs.html
2026-03-06 12:25:27 -05:00

85 lines
2.7 KiB
HTML

{% extends "base.html" %}
{% block title %}Logs - HAMeter{% endblock %}
{% block page_title %}Logs{% endblock %}
{% block top_actions %}
<div class="log-controls">
<select id="log-level-filter" onchange="filterLogs()">
<option value="">All Levels</option>
<option value="DEBUG">DEBUG</option>
<option value="INFO">INFO</option>
<option value="WARNING">WARNING</option>
<option value="ERROR">ERROR</option>
</select>
<input type="text" id="log-search" placeholder="Filter..." oninput="filterLogs()">
<label class="checkbox-label">
<input type="checkbox" id="log-autoscroll" checked> Auto-scroll
</label>
<button class="btn btn-sm btn-secondary" onclick="clearLogs()">Clear</button>
</div>
{% endblock %}
{% block content %}
<div class="log-viewer" id="log-viewer"></div>
<script>
const logViewer = document.getElementById('log-viewer');
let allLogs = [];
// Load initial logs
fetch('/api/logs?count=500')
.then(r => r.json())
.then(logs => {
allLogs = logs;
renderLogs();
});
function addLogEntry(entry) {
allLogs.push(entry);
if (allLogs.length > 2000) allLogs = allLogs.slice(-1000);
renderLogs();
}
function renderLogs() {
const level = document.getElementById('log-level-filter').value;
const search = document.getElementById('log-search').value.toLowerCase();
const filtered = allLogs.filter(log => {
if (level && log.level !== level) return false;
if (search && !log.message.toLowerCase().includes(search) && !log.name.toLowerCase().includes(search)) return false;
return true;
});
logViewer.innerHTML = filtered.map(log => {
const cls = 'log-' + (log.level || 'INFO').toLowerCase();
return '<div class="log-line ' + cls + '">' +
'<span class="log-ts">' + (log.timestamp || '') + '</span> ' +
'<span class="log-level">[' + (log.level || '?') + ']</span> ' +
'<span class="log-name">' + (log.name || '') + ':</span> ' +
'<span class="log-msg">' + escapeHtml(log.message || '') + '</span>' +
'</div>';
}).join('');
if (document.getElementById('log-autoscroll').checked) {
logViewer.scrollTop = logViewer.scrollHeight;
}
}
function filterLogs() { renderLogs(); }
function clearLogs() { allLogs = []; renderLogs(); }
function escapeHtml(s) {
return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}
// SSE log handler
if (typeof window._onLogUpdate === 'undefined') {
window._onLogUpdate = function(logs) {
if (Array.isArray(logs)) {
logs.forEach(addLogEntry);
}
};
}
</script>
{% endblock %}