let logSource = null; let currentUnit = null; const MAX_LOG_LINES = 800; const MIN_LOG_LINES = 500; let logLines = []; function escapeHtml(s) { return s .replace(/&/g, "&") .replace(//g, ">"); } function colorLog(line) { const s = escapeHtml(line); const levels = [ ["TRACE", "log-trace"], ["DEBUG", "log-debug"], ["INFO", "log-info"], ["WARN", "log-warn"], ["WARNING", "log-warn"], ["ERROR", "log-error"], ["FATAL", "log-fatal"], ["PANIC", "log-panic"] ]; for (const [lvl, cls] of levels) { if (s.includes(lvl) || s.includes(lvl.toLowerCase())) { return `${s}`; } } return `${s}`; } function connectLog(unit, target) { const el = typeof target === "string" ? document.getElementById(target) : target; if (!el) { console.error("[log-stream] target element not found"); return; } if (currentUnit === unit && logSource && logSource.readyState !== EventSource.CLOSED) { return; } disconnectLog(); currentUnit = unit; logLines = []; el.textContent = ""; const source = new EventSource( `/api/service/log?unit=${encodeURIComponent(unit)}` ); logSource = source; source.onmessage = (e) => { if (source !== logSource) { return; } logLines.push(e.data); if (logLines.length > MAX_LOG_LINES) { logLines.shift(); } const line = document.createElement("div"); line.innerHTML = colorLog(e.data); const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 20; el.appendChild(line); if (el.childElementCount > MAX_LOG_LINES) { while (el.childElementCount > MIN_LOG_LINES) { el.firstElementChild.remove(); } } if (atBottom) { requestAnimationFrame(() => { el.scrollTop = el.scrollHeight; }); } }; source.onerror = () => { if (source !== logSource) { return; } console.warn( `[log-stream] error unit=${unit}, state=${source.readyState}` ); }; } function disconnectLog() { if (logSource) { logSource.close(); logSource = null; } currentUnit = null; logLines = []; } function getCurrentUnit() { return currentUnit; } function clearLog(target) { logLines = []; const el = typeof target === "string" ? document.getElementById(target) : target; if (el) { el.replaceChildren(); el.scrollTop = 0; } } function copyLog(target) { const text = logLines.join("\n"); if (navigator.clipboard) { navigator.clipboard.writeText(text) .then(() => alert("复制成功")) .catch(() => alert("复制失败")); } else { const ta = document.createElement("textarea"); ta.value = text; document.body.appendChild(ta); ta.select(); document.execCommand("copy"); ta.remove(); alert("复制成功"); } }