| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- var logSource = null;
- var currentUnit = null;
- var logPid = 0;
- var MAX_LOG_LINES = 800;
- var MIN_LOG_LINES = 500;
- var logLines = [];
- function escapeHtml(s) {
- return s
- .replace(/&/g, "&")
- .replace(/</g, "<")
- .replace(/>/g, ">")
- .replace(/"/g, """)
- .replace(/'/g, "'");
- }
- function colorLog(line) {
- var s = escapeHtml(line);
- var 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 (var i = 0; i < levels.length; i++) {
- var lvl = levels[i][0],
- cls = levels[i][1];
- if (s.includes(lvl) || s.includes(lvl.toLowerCase())) {
- return '<span class="' + cls + '">' + s + "</span>";
- }
- }
- return '<span class="log-default">' + s + "</span>";
- }
- function connectLog(unit, target) {
- var 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 = "";
- var source = new EventSource("/api/service/log?unit=" + encodeURIComponent(unit));
- logSource = source;
- source.onmessage = function (e) {
- if (source !== logSource) {
- return;
- }
- logLines.push(e.data);
- if (logLines.length > MAX_LOG_LINES) {
- logLines.shift();
- }
- var line = document.createElement("div");
- line.innerHTML = colorLog(e.data);
- var 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(function () {
- el.scrollTop = el.scrollHeight;
- });
- }
- };
- source.addEventListener("pid", function (e) {
- if (source !== logSource) {
- return;
- }
- logPid = Number(e.data);
- });
- source.onerror = function () {
- if (source !== logSource) {
- return;
- }
- console.warn("[log-stream] error unit=" + unit + ", state=" + source.readyState);
- };
- }
- function disconnectLog() {
- if (logSource) {
- logSource.close();
- logSource = null;
- }
- if (logPid > 0) {
- fetch("/api/service/log/close?pid=" + logPid).catch(function () {});
- logPid = 0;
- }
- currentUnit = null;
- logLines = [];
- }
- function getCurrentUnit() {
- return currentUnit;
- }
- function clearLog(target) {
- logLines = [];
- var el = typeof target === "string" ? document.getElementById(target) : target;
- if (el) {
- // replaceChildren 兼容兜底 (Chrome < 86 / Safari < 14)
- if (el.replaceChildren) {
- el.replaceChildren();
- } else {
- while (el.firstChild) {
- el.removeChild(el.firstChild);
- }
- }
- el.scrollTop = 0;
- }
- }
- function copyLog(target) {
- var el = typeof target === "string" ? document.getElementById(target) : target;
- var text = el ? el.textContent : logLines.join("\n");
- if (navigator.clipboard) {
- navigator.clipboard
- .writeText(text)
- .then(function () {
- return alert("复制成功");
- })
- .catch(function () {
- return alert("复制失败");
- });
- } else {
- var ta = document.createElement("textarea");
- ta.value = text;
- document.body.appendChild(ta);
- ta.select();
- document.execCommand("copy");
- ta.remove();
- alert("复制成功");
- }
- }
- ["beforeunload", "pagehide"].forEach(function (e) {
- return window.addEventListener(e, disconnectLog);
- });
|