Improve UI logs (#16434)

* use react-logviewer and backend streaming

* layout adjustments

* readd copy handler

* reorder and fix key

* add loading state

* handle frigate log consolidation

* handle newlines in sheet

* update react-logviewer

* fix scrolling and use chunked log download

* don't combine frigate log lines with timestamp

* basic deduplication

* use react-logviewer and backend streaming

* layout adjustments

* readd copy handler

* reorder and fix key

* add loading state

* handle frigate log consolidation

* handle newlines in sheet

* update react-logviewer

* fix scrolling and use chunked log download

* don't combine frigate log lines with timestamp

* basic deduplication

* move process logs function to services util

* improve layout and scrolling behavior

* clean up
This commit is contained in:
Josh Hawkins
2025-02-10 09:38:56 -06:00
committed by GitHub
parent e207b2f50b
commit 2a28964e63
13 changed files with 813 additions and 377 deletions

View File

@@ -18,13 +18,29 @@ export function parseLogLines(logService: LogType, logs: string[]) {
if (!match) {
const infoIndex = line.indexOf("[INFO]");
const loggingIndex = line.indexOf("[LOGGING]");
if (loggingIndex != -1) {
return {
dateStamp: line.substring(0, 19),
severity: "info",
section: "logging",
content: line
.substring(loggingIndex + 9)
.trim()
.replace(/\u200b/g, "\n"),
};
}
if (infoIndex != -1) {
return {
dateStamp: line.substring(0, 19),
severity: "info",
section: "startup",
content: line.substring(infoIndex + 6).trim(),
content: line
.substring(infoIndex + 6)
.trim()
.replace(/\u200b/g, "\n"),
};
}
@@ -32,7 +48,10 @@ export function parseLogLines(logService: LogType, logs: string[]) {
dateStamp: line.substring(0, 19),
severity: "unknown",
section: "unknown",
content: line.substring(30).trim(),
content: line
.substring(30)
.trim()
.replace(/\u200b/g, "\n"),
};
}
@@ -54,7 +73,8 @@ export function parseLogLines(logService: LogType, logs: string[]) {
section: sectionMatch.toString(),
content: line
.substring(line.indexOf(":", match.index + match[0].length) + 2)
.trim(),
.trim()
.replace(/\u200b/g, "\n"),
};
})
.filter((value) => value != null) as LogLine[];
@@ -86,6 +106,15 @@ export function parseLogLines(logService: LogType, logs: string[]) {
contentStart = line.indexOf(section) + section.length + 2;
}
if (line.includes("[LOGGING]")) {
return {
dateStamp: line.substring(0, 19),
severity: "info",
section: "logging",
content: line.substring(line.indexOf("[LOGGING]") + 9).trim(),
};
}
let severityCat: LogSeverity;
switch (severity?.at(0)?.toString().trim()) {
case "INF":
@@ -116,18 +145,68 @@ export function parseLogLines(logService: LogType, logs: string[]) {
} else if (logService == "nginx") {
return logs
.map((line) => {
if (line.length == 0) {
return null;
}
if (line.trim().length === 0) return null;
return {
dateStamp: line.substring(0, 19),
severity: "info",
section: httpMethods.exec(line)?.at(0)?.toString() ?? "META",
content: line.substring(line.indexOf(" ", 20)).trim(),
};
// Match full timestamp including nanoseconds
const timestampRegex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+/;
const timestampMatch = timestampRegex.exec(line);
const fullTimestamp = timestampMatch ? timestampMatch[0] : "";
// Remove nanoseconds from the final output
const dateStamp = fullTimestamp.split(".")[0];
if (line.includes("[LOGGING]")) {
return {
dateStamp,
severity: "info",
section: "logging",
content: line.slice(line.indexOf("[LOGGING]") + 9).trim(),
};
} else if (line.includes("[INFO]")) {
return {
dateStamp,
severity: "info",
section: "startup",
content: line.slice(fullTimestamp.length).trim(),
};
} else if (line.includes("[error]")) {
// Error log
const errorMatch = line.match(/(\[error\].*?,.*request: "[^"]*")/);
const content = errorMatch ? errorMatch[1] : line;
return {
dateStamp,
severity: "error",
section: "error",
content,
};
} else if (
line.includes("GET") ||
line.includes("POST") ||
line.includes("HTTP")
) {
// HTTP request log
const httpMethodMatch = httpMethods.exec(line);
const section = httpMethodMatch ? httpMethodMatch[0] : "META";
const contentStart = line.indexOf('"', fullTimestamp.length);
const content =
contentStart !== -1 ? line.slice(contentStart).trim() : line;
return {
dateStamp,
severity: "info",
section,
content,
};
} else {
// Fallback: unknown format
return {
dateStamp,
severity: "unknown",
section: "unknown",
content: line.slice(fullTimestamp.length).trim(),
};
}
})
.filter((value) => value != null) as LogLine[];
.filter((value) => value !== null) as LogLine[];
}
return [];