fix: correct log level hierarchy and enhance log filtering logic

This commit is contained in:
Tunglies
2025-08-08 23:30:24 +08:00
parent 319c5b84fa
commit 52e8e45daf
5 changed files with 90 additions and 25 deletions

View File

@@ -39,6 +39,7 @@
- 修复首页流量统计刻度线显示问题
- 修复日志页面按钮功能混淆问题
- 修复日志等级设置保存问题
- 修复日志等级异常过滤
- 修复偶发性启动卡死问题
### 🔧 技术改进

View File

@@ -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<Self, <Self as TryFrom<&str>>::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<String> for LogLevel {
type Error = String;
fn try_from(value: String) -> Result<Self, <Self as TryFrom<String>>::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<RwLock<CurrentLogs>>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
if let Ok(log_data) = serde_json::from_str::<LogData>(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<String>) -> 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<serde_json::Value> = 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,

View File

@@ -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)}
>
<MenuItem value="all">ALL</MenuItem>
<MenuItem value="debug">DEBUG</MenuItem>
<MenuItem value="info">INFO</MenuItem>
<MenuItem value="warning">WARNING</MenuItem>
<MenuItem value="error">ERROR</MenuItem>
<MenuItem value="debug">DEBUG</MenuItem>
</BaseStyledSelect>
<BaseSearchBox
onSearch={(matcher, state) => {

View File

@@ -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;

View File

@@ -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;