Files

123 lines
5.2 KiB
HTML
Raw Permalink Normal View History

2026-07-20 13:16:17 +08:00
{% extends "base.html" %}
{% block title %}自动签名系统 - 日志{% endblock %}
{% block content %}
<div style="display:flex;flex-direction:column;height:100%;">
<div class="log-toolbar">
<span style="font-size:13px;font-weight:600;color:#1a237e;"><i class="bi bi-terminal"></i> 系统日志</span>
<select class="form-select" id="logSourceFilter">
<option value="all">全部来源</option>
<option value="前端操作">前端操作</option>
<option value="外部API">外部API</option>
</select>
<select class="form-select" id="logLevelFilter">
<option value="all">全部级别</option>
<option value="INFO">INFO</option>
<option value="WARN">WARN</option>
<option value="ERROR">ERROR</option>
</select>
<input type="text" class="form-control" id="logKeyword" placeholder="关键字搜索" style="width:200px;">
<div class="form-check form-check-inline ms-2">
<input class="form-check-input" type="checkbox" id="autoScroll" checked>
<label class="form-check-label" for="autoScroll" style="font-size:12px;">自动滚动</label>
</div>
<button class="btn btn-outline-secondary btn-sm" id="btnClearLog"><i class="bi bi-trash"></i> 清空</button>
<button class="btn btn-outline-danger btn-sm" id="btnLocateError"><i class="bi bi-exclamation-triangle"></i> 定位异常</button>
<span class="ms-auto" style="font-size:12px;color:#757575;" id="logCount">0 条</span>
</div>
<div class="log-content" id="logContent"></div>
</div>
{% endblock %}
{% block extra_js %}
<script>
(function() {
const logContent = document.getElementById('logContent');
const logSourceFilter = document.getElementById('logSourceFilter');
const logLevelFilter = document.getElementById('logLevelFilter');
const logKeyword = document.getElementById('logKeyword');
const autoScroll = document.getElementById('autoScroll');
const logCountEl = document.getElementById('logCount');
let allLogs = [];
let logId = 0;
function addLogEntry(level, source, message) {
logId++;
const entry = { id: logId, level, source, message, time: new Date() };
allLogs.push(entry);
renderEntry(entry);
logCountEl.textContent = allLogs.length + ' 条';
}
function renderEntry(entry) {
if (!matchFilter(entry)) return;
const div = document.createElement('div');
div.className = `log-entry level-${entry.level}`;
div.dataset.id = entry.id;
div.dataset.level = entry.level;
div.dataset.source = entry.source;
const sourceClass = entry.source === '外部API' ? 'log-source-api' : 'log-source-frontend';
const timeStr = entry.time.toLocaleTimeString('zh-CN', { hour12: false });
div.innerHTML = `<span class="log-timestamp">[${timeStr}]</span> <span class="${sourceClass}">[${entry.source}]</span> <span style="font-weight:600;">[${entry.level}]</span> ${escapeHtml(entry.message)}`;
logContent.appendChild(div);
if (autoScroll.checked) {
logContent.scrollTop = logContent.scrollHeight;
}
}
function matchFilter(entry) {
const src = logSourceFilter.value;
const lvl = logLevelFilter.value;
const kw = logKeyword.value.trim().toLowerCase();
if (src !== 'all' && entry.source !== src) return false;
if (lvl !== 'all' && entry.level !== lvl) return false;
if (kw && !entry.message.toLowerCase().includes(kw)) return false;
return true;
}
function rerenderAll() {
logContent.innerHTML = '';
allLogs.forEach(entry => renderEntry(entry));
}
logSourceFilter.addEventListener('change', rerenderAll);
logLevelFilter.addEventListener('change', rerenderAll);
logKeyword.addEventListener('input', rerenderAll);
document.getElementById('btnClearLog').addEventListener('click', function() {
allLogs = [];
logContent.innerHTML = '';
logCountEl.textContent = '0 条';
});
document.getElementById('btnLocateError').addEventListener('click', function() {
const errorEntries = logContent.querySelectorAll('.level-ERROR');
if (errorEntries.length > 0) {
errorEntries.forEach(e => e.classList.remove('highlight-error'));
const last = errorEntries[errorEntries.length - 1];
last.scrollIntoView({ behavior: 'smooth', block: 'center' });
last.classList.add('highlight-error');
} else {
alert('没有发现异常日志');
}
});
const evtSource = new EventSource('/api/logs/stream');
evtSource.onmessage = function(event) {
try {
const data = JSON.parse(event.data);
addLogEntry(data.level || 'INFO', data.source || '系统', data.message || '');
} catch (e) {
addLogEntry('INFO', '系统', event.data);
}
};
evtSource.onerror = function() {
addLogEntry('WARN', '系统', '日志流连接断开5秒后重连...');
};
addLogEntry('INFO', '系统', '日志页面已加载,正在监听系统日志...');
})();
</script>
{% endblock %}