log-stream.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. let logSource = null;
  2. let currentUnit = null;
  3. const MAX_LOG_LINES = 2000;
  4. let logLines = [];
  5. function colorLog(line) {
  6. let s = line
  7. .replace(/&/g, "&")
  8. .replace(/</g, "&lt;")
  9. .replace(/>/g, "&gt;");
  10. const levels = [
  11. ["TRACE", "log-trace"],
  12. ["DEBUG", "log-debug"],
  13. ["INFO", "log-info"],
  14. ["WARN", "log-warn"],
  15. ["WARNING", "log-warn"],
  16. ["ERROR", "log-error"],
  17. ["FATAL", "log-fatal"],
  18. ["PANIC", "log-panic"]
  19. ];
  20. for (const [level, cls] of levels) {
  21. if (s.includes(level) || s.includes(level.toLowerCase())) {
  22. return `<span class="${cls}">${s}</span>`;
  23. }
  24. }
  25. return `<span class="log-default">${s}</span>`;
  26. }
  27. function connectLog(unit, target) {
  28. const el = typeof target === "string"
  29. ? document.getElementById(target)
  30. : target;
  31. if (!el) {
  32. console.error("[log-stream] target element not found");
  33. return;
  34. }
  35. if (currentUnit === unit && logSource &&
  36. logSource.readyState !== EventSource.CLOSED) {
  37. return;
  38. }
  39. disconnectLog();
  40. currentUnit = unit;
  41. logLines = [];
  42. el.textContent = "";
  43. const source = new EventSource(
  44. `/api/service/log?unit=${encodeURIComponent(unit)}`
  45. );
  46. logSource = source;
  47. source.onmessage = (e) => {
  48. if (source !== logSource) {
  49. return;
  50. }
  51. logLines.push(e.data);
  52. if (logLines.length > MAX_LOG_LINES) {
  53. logLines.splice(
  54. 0,
  55. logLines.length - MAX_LOG_LINES
  56. );
  57. }
  58. el.innerHTML = logLines
  59. .map(colorLog)
  60. .join("<br>") + "<br><br><br>";
  61. el.scrollTop = el.scrollHeight;
  62. };
  63. source.onerror = () => {
  64. if (source !== logSource) {
  65. return;
  66. }
  67. console.log(
  68. `[log-stream] error unit=${unit}, state=${source.readyState}`
  69. );
  70. };
  71. }
  72. function disconnectLog() {
  73. if (logSource) {
  74. logSource.close();
  75. logSource = null;
  76. }
  77. currentUnit = null;
  78. logLines = [];
  79. }
  80. function getCurrentUnit() {
  81. return currentUnit;
  82. }
  83. function clearLog(target) {
  84. logLines = [];
  85. const el = typeof target === "string"
  86. ? document.getElementById(target)
  87. : target;
  88. if (el) {
  89. el.textContent = "";
  90. }
  91. }
  92. function copyLog(target) {
  93. const el = typeof target === "string"
  94. ? document.getElementById(target)
  95. : target;
  96. if (!el) return;
  97. const text = el.value !== undefined ? el.value : el.innerText;
  98. if (navigator.clipboard) {
  99. navigator.clipboard.writeText(text)
  100. .then(() => alert("复制成功"));
  101. } else {
  102. const ta = document.createElement("textarea");
  103. ta.value = text;
  104. document.body.appendChild(ta);
  105. ta.select();
  106. document.execCommand("copy");
  107. ta.remove();
  108. alert("复制成功");
  109. }
  110. }