log-stream.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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 s;
  26. }
  27. const monthMap = {
  28. Jan: '01', Feb: '02', Mar: '03', Apr: '04',
  29. May: '05', Jun: '06', Jul: '07', Aug: '08',
  30. Sep: '09', Oct: '10', Nov: '11', Dec: '12'
  31. };
  32. function formatLog(line) {
  33. return line.replace(
  34. /^(\w{3})\s+(\d{1,2})\s+(\d{2}:\d{2}:\d{2})\s+\S+\s+[^\s]+\[\d+\]:\s+\[([a-z]+)\]:\s+\[[^\]]+\](.*)/i,
  35. (_, mon, day, time, level, msg) =>
  36. `${monthMap[mon]}-${day.padStart(2, '0')} ${time} ${level.toUpperCase()}${msg}`
  37. );
  38. }
  39. function connectLog(unit, target) {
  40. const el = typeof target === "string"
  41. ? document.getElementById(target)
  42. : target;
  43. if (!el) {
  44. console.error("[log-stream] target element not found");
  45. return;
  46. }
  47. if (currentUnit === unit && logSource &&
  48. logSource.readyState !== EventSource.CLOSED) {
  49. return;
  50. }
  51. disconnectLog();
  52. currentUnit = unit;
  53. logLines = [];
  54. el.textContent = "";
  55. const source = new EventSource(
  56. `/api/service/log?unit=${encodeURIComponent(unit)}`
  57. );
  58. logSource = source;
  59. source.onmessage = (e) => {
  60. if (source !== logSource) {
  61. return;
  62. }
  63. logLines.push(e.data);
  64. if (logLines.length > MAX_LOG_LINES) {
  65. logLines.splice(
  66. 0,
  67. logLines.length - MAX_LOG_LINES
  68. );
  69. }
  70. el.innerHTML = logLines
  71. .map(formatLog)
  72. .map(colorLog)
  73. .join("<br>") + "<br><br><br>";
  74. el.scrollTop = el.scrollHeight;
  75. };
  76. source.onerror = () => {
  77. if (source !== logSource) {
  78. return;
  79. }
  80. console.log(
  81. `[log-stream] error unit=${unit}, state=${source.readyState}`
  82. );
  83. };
  84. }
  85. function disconnectLog() {
  86. if (logSource) {
  87. logSource.close();
  88. logSource = null;
  89. }
  90. currentUnit = null;
  91. logLines = [];
  92. }
  93. function getCurrentUnit() {
  94. return currentUnit;
  95. }