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("复制成功");
}
}