From ba3a9b709df5ab8d59d301e2a2bc93acebd213d0 Mon Sep 17 00:00:00 2001 From: yyhuni Date: Mon, 5 Jan 2026 16:27:31 +0800 Subject: [PATCH] feat(system-logs): enhance ANSI log viewer with log level colorization - Add LOG_LEVEL_COLORS configuration mapping for DEBUG, INFO, WARNING, WARN, ERROR, and CRITICAL levels - Implement hasAnsiCodes() function to detect presence of ANSI escape sequences in log content - Add colorizeLogContent() function to parse plain text logs and apply color styling based on log levels - Support dual-mode log parsing: ANSI color codes and plain text log level detection - Rename converter to ansiConverter for clarity and consistency - Change newline handling from true to false for manual line break control - Apply color-coded styling to timestamps (gray), log levels (level-specific colors), and messages - Add bold font-weight styling for CRITICAL level logs for better visibility --- .../settings/system-logs/ansi-log-viewer.tsx | 61 +++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/frontend/components/settings/system-logs/ansi-log-viewer.tsx b/frontend/components/settings/system-logs/ansi-log-viewer.tsx index 95928b8e..6d612410 100644 --- a/frontend/components/settings/system-logs/ansi-log-viewer.tsx +++ b/frontend/components/settings/system-logs/ansi-log-viewer.tsx @@ -8,11 +8,21 @@ interface AnsiLogViewerProps { className?: string } +// 日志级别颜色配置 +const LOG_LEVEL_COLORS: Record = { + DEBUG: "#4ec9b0", // cyan + INFO: "#6a9955", // green + WARNING: "#dcdcaa", // yellow + WARN: "#dcdcaa", // yellow + ERROR: "#f44747", // red + CRITICAL: "#f44747", // red (bold handled separately) +} + // 创建 ANSI 转换器实例 -const converter = new AnsiToHtml({ +const ansiConverter = new AnsiToHtml({ fg: "#d4d4d4", bg: "#1e1e1e", - newline: true, + newline: false, // 我们自己处理换行 escapeXML: true, colors: { 0: "#1e1e1e", // black @@ -34,14 +44,57 @@ const converter = new AnsiToHtml({ }, }) +// 检测内容是否包含 ANSI 颜色码 +function hasAnsiCodes(text: string): boolean { + // ANSI 转义序列通常以 ESC[ 开头(\x1b[ 或 \u001b[) + return /\x1b\[|\u001b\[/.test(text) +} + +// 解析纯文本日志内容,为日志级别添加颜色 +function colorizeLogContent(content: string): string { + // 匹配日志格式: [时间] [级别] [模块:行号] 消息 + // 例如: [2025-01-05 10:30:00] [INFO] [apps.scan:123] 消息内容 + const logLineRegex = /^(\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\]) (\[(DEBUG|INFO|WARNING|WARN|ERROR|CRITICAL)\]) (.*)$/ + + return content + .split("\n") + .map((line) => { + const match = line.match(logLineRegex) + + if (match) { + const [, timestamp, levelBracket, level, rest] = match + const color = LOG_LEVEL_COLORS[level] || "#d4d4d4" + // ansiConverter.toHtml 已经处理了 HTML 转义 + const escapedTimestamp = ansiConverter.toHtml(timestamp) + const escapedLevelBracket = ansiConverter.toHtml(levelBracket) + const escapedRest = ansiConverter.toHtml(rest) + + // 时间戳灰色,日志级别带颜色,其余默认色 + return `${escapedTimestamp} ${escapedLevelBracket} ${escapedRest}` + } + + // 非标准格式的行,也进行 HTML 转义 + return ansiConverter.toHtml(line) + }) + .join("\n") +} + export function AnsiLogViewer({ content, className }: AnsiLogViewerProps) { const containerRef = useRef(null) const isAtBottomRef = useRef(true) // 跟踪用户是否在底部 - // 将 ANSI 转换为 HTML + // 解析日志并添加颜色 + // 支持两种模式:ANSI 颜色码和纯文本日志级别解析 const htmlContent = useMemo(() => { if (!content) return "" - return converter.toHtml(content) + + // 如果包含 ANSI 颜色码,直接转换 + if (hasAnsiCodes(content)) { + return ansiConverter.toHtml(content) + } + + // 否则解析日志级别添加颜色 + return colorizeLogContent(content) }, [content]) // 监听滚动事件,检测用户是否在底部