diff --git a/UPDATELOG.md b/UPDATELOG.md index 37f49cd20..cdeed7874 100644 --- a/UPDATELOG.md +++ b/UPDATELOG.md @@ -39,6 +39,7 @@ - 修复首页流量统计刻度线显示问题 - 修复日志页面按钮功能混淆问题 - 修复日志等级设置保存问题 +- 修复日志等级异常过滤 - 修复偶发性启动卡死问题 ### 🔧 技术改进 diff --git a/src-tauri/src/ipc/logs.rs b/src-tauri/src/ipc/logs.rs index dd55412b7..cd4ad2cae 100644 --- a/src-tauri/src/ipc/logs.rs +++ b/src-tauri/src/ipc/logs.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use std::fmt; use std::{collections::VecDeque, sync::Arc, time::Instant}; use tokio::{sync::RwLock, task::JoinHandle, time::Duration}; @@ -22,6 +23,74 @@ pub struct LogItem { pub time: String, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +enum LogLevel { + Debug, + Info, + Warning, + Error, + All, +} + +impl fmt::Display for LogLevel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LogLevel::Debug => write!(f, "debug"), + LogLevel::Info => write!(f, "info"), + LogLevel::Warning => write!(f, "warning"), + LogLevel::Error => write!(f, "error"), + LogLevel::All => write!(f, "all"), + } + } +} + +impl TryFrom<&str> for LogLevel { + type Error = String; + + fn try_from(value: &str) -> Result>::Error> { + match value.to_lowercase().as_str() { + "debug" => Ok(LogLevel::Debug), + "info" => Ok(LogLevel::Info), + "warning" | "warn" => Ok(LogLevel::Warning), + "error" | "err" => Ok(LogLevel::Error), + "all" => Ok(LogLevel::All), + _ => Err(format!("Invalid log level: '{}'", value)), + } + } +} + +impl TryFrom for LogLevel { + type Error = String; + + fn try_from(value: String) -> Result>::Error> { + LogLevel::try_from(value.as_str()) + } +} + +impl LogLevel { + /// Parse from string with a default fallback + pub fn from_str_or_default(s: &str, default: LogLevel) -> LogLevel { + Self::try_from(s).unwrap_or(default) + } + + /// Check if this log level should include logs of the specified type + pub fn should_include(&self, log_type: &str) -> bool { + match LogLevel::try_from(log_type) { + Ok(log_level) => match self { + LogLevel::All => true, + LogLevel::Debug => true, // Debug includes all levels + LogLevel::Info => log_level >= LogLevel::Info, + LogLevel::Warning => log_level >= LogLevel::Warning, + LogLevel::Error => log_level >= LogLevel::Error, + }, + Err(_) => { + // If we can't parse the log type, include it by default + true + } + } + } +} + impl LogItem { fn new(log_type: String, payload: String) -> Self { use std::time::{SystemTime, UNIX_EPOCH}; @@ -231,11 +300,9 @@ impl LogsMonitor { current: Arc>, ) -> Result<(), Box> { if let Ok(log_data) = serde_json::from_str::(line.trim()) { - // Filter logs based on level if needed - let should_include = match filter_level { - "all" => true, - level => log_data.log_type.to_lowercase() == level.to_lowercase(), - }; + // Use LogLevel enum for smarter filtering with hierarchical support + let filter_log_level = LogLevel::from_str_or_default(filter_level, LogLevel::Info); + let should_include = filter_log_level.should_include(&log_data.log_type); if should_include { let log_item = LogItem::new(log_data.log_type, log_data.payload); @@ -277,20 +344,16 @@ impl LogsMonitor { pub async fn get_logs_as_json(&self, level: Option) -> serde_json::Value { let current = self.current().await; + // Use the same filtering logic as process_log_line for consistency + let filter_log_level = level + .as_deref() + .map(|l| LogLevel::from_str_or_default(l, LogLevel::Info)) + .unwrap_or(LogLevel::All); + let filtered_logs: Vec = current .logs .iter() - .filter(|log| { - if let Some(ref filter_level) = level { - if filter_level == "all" { - true - } else { - log.log_type.to_lowercase() == filter_level.to_lowercase() - } - } else { - true - } - }) + .filter(|log| filter_log_level.should_include(&log.log_type)) .map(|log| { serde_json::json!({ "type": log.log_type, diff --git a/src/pages/logs.tsx b/src/pages/logs.tsx index d5dd862f9..2ae1e6f09 100644 --- a/src/pages/logs.tsx +++ b/src/pages/logs.tsx @@ -24,13 +24,14 @@ import { toggleLogEnabled, } from "@/services/global-log-service"; -// 定义日志级别结构 +// 定义日志级别结构 - 与后端保持一致 +// 后端顺序:Debug < Info < Warning < Error, All 显示所有 const LOG_LEVEL_HIERARCHY = { - all: ["info", "warning", "error", "debug"], - info: ["info", "warning", "error"], - warning: ["warning", "error"], - error: ["error"], - debug: ["debug"], + all: ["debug", "info", "warning", "error"], // All: 显示所有等级 + debug: ["debug", "info", "warning", "error"], // Debug: 显示所有等级(最低级别) + info: ["info", "warning", "error"], // Info: 显示 Info、Warning、Error + warning: ["warning", "error"], // Warning: 显示 Warning、Error + error: ["error"], // Error: 仅显示 Error }; const LogPage = () => { @@ -133,10 +134,10 @@ const LogPage = () => { onChange={(e) => handleLogLevelChange(e.target.value as LogLevel)} > ALL + DEBUG INFO WARNING ERROR - DEBUG { diff --git a/src/services/global-log-service.ts b/src/services/global-log-service.ts index f60a159f2..23e387f1a 100644 --- a/src/services/global-log-service.ts +++ b/src/services/global-log-service.ts @@ -11,7 +11,7 @@ import dayjs from "dayjs"; // 最大日志数量 const MAX_LOG_NUM = 1000; -export type LogLevel = "warning" | "info" | "debug" | "error" | "all"; +export type LogLevel = "debug" | "info" | "warning" | "error" | "all"; export interface ILogItem { time?: string; diff --git a/src/services/ipc-log-service.ts b/src/services/ipc-log-service.ts index 954f1b60a..28e76bcfe 100644 --- a/src/services/ipc-log-service.ts +++ b/src/services/ipc-log-service.ts @@ -7,7 +7,7 @@ import { } from "@/services/cmds"; import dayjs from "dayjs"; -export type LogLevel = "warning" | "info" | "debug" | "error" | "all"; +export type LogLevel = "debug" | "info" | "warning" | "error" | "all"; export interface ILogItem { time?: string;