mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 00:35:38 +08:00
fix: correct log level hierarchy and enhance log filtering logic
This commit is contained in:
@@ -39,6 +39,7 @@
|
|||||||
- 修复首页流量统计刻度线显示问题
|
- 修复首页流量统计刻度线显示问题
|
||||||
- 修复日志页面按钮功能混淆问题
|
- 修复日志页面按钮功能混淆问题
|
||||||
- 修复日志等级设置保存问题
|
- 修复日志等级设置保存问题
|
||||||
|
- 修复日志等级异常过滤
|
||||||
- 修复偶发性启动卡死问题
|
- 修复偶发性启动卡死问题
|
||||||
|
|
||||||
### 🔧 技术改进
|
### 🔧 技术改进
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt;
|
||||||
use std::{collections::VecDeque, sync::Arc, time::Instant};
|
use std::{collections::VecDeque, sync::Arc, time::Instant};
|
||||||
use tokio::{sync::RwLock, task::JoinHandle, time::Duration};
|
use tokio::{sync::RwLock, task::JoinHandle, time::Duration};
|
||||||
|
|
||||||
@@ -22,6 +23,74 @@ pub struct LogItem {
|
|||||||
pub time: String,
|
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 {
|
impl LogItem {
|
||||||
fn new(log_type: String, payload: String) -> Self {
|
fn new(log_type: String, payload: String) -> Self {
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
@@ -231,11 +300,9 @@ impl LogsMonitor {
|
|||||||
current: Arc<RwLock<CurrentLogs>>,
|
current: Arc<RwLock<CurrentLogs>>,
|
||||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
if let Ok(log_data) = serde_json::from_str::<LogData>(line.trim()) {
|
if let Ok(log_data) = serde_json::from_str::<LogData>(line.trim()) {
|
||||||
// Filter logs based on level if needed
|
// Use LogLevel enum for smarter filtering with hierarchical support
|
||||||
let should_include = match filter_level {
|
let filter_log_level = LogLevel::from_str_or_default(filter_level, LogLevel::Info);
|
||||||
"all" => true,
|
let should_include = filter_log_level.should_include(&log_data.log_type);
|
||||||
level => log_data.log_type.to_lowercase() == level.to_lowercase(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if should_include {
|
if should_include {
|
||||||
let log_item = LogItem::new(log_data.log_type, log_data.payload);
|
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 {
|
pub async fn get_logs_as_json(&self, level: Option<String>) -> serde_json::Value {
|
||||||
let current = self.current().await;
|
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
|
let filtered_logs: Vec<serde_json::Value> = current
|
||||||
.logs
|
.logs
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|log| {
|
.filter(|log| filter_log_level.should_include(&log.log_type))
|
||||||
if let Some(ref filter_level) = level {
|
|
||||||
if filter_level == "all" {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
log.log_type.to_lowercase() == filter_level.to_lowercase()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(|log| {
|
.map(|log| {
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
"type": log.log_type,
|
"type": log.log_type,
|
||||||
|
|||||||
@@ -24,13 +24,14 @@ import {
|
|||||||
toggleLogEnabled,
|
toggleLogEnabled,
|
||||||
} from "@/services/global-log-service";
|
} from "@/services/global-log-service";
|
||||||
|
|
||||||
// 定义日志级别结构
|
// 定义日志级别结构 - 与后端保持一致
|
||||||
|
// 后端顺序:Debug < Info < Warning < Error, All 显示所有
|
||||||
const LOG_LEVEL_HIERARCHY = {
|
const LOG_LEVEL_HIERARCHY = {
|
||||||
all: ["info", "warning", "error", "debug"],
|
all: ["debug", "info", "warning", "error"], // All: 显示所有等级
|
||||||
info: ["info", "warning", "error"],
|
debug: ["debug", "info", "warning", "error"], // Debug: 显示所有等级(最低级别)
|
||||||
warning: ["warning", "error"],
|
info: ["info", "warning", "error"], // Info: 显示 Info、Warning、Error
|
||||||
error: ["error"],
|
warning: ["warning", "error"], // Warning: 显示 Warning、Error
|
||||||
debug: ["debug"],
|
error: ["error"], // Error: 仅显示 Error
|
||||||
};
|
};
|
||||||
|
|
||||||
const LogPage = () => {
|
const LogPage = () => {
|
||||||
@@ -133,10 +134,10 @@ const LogPage = () => {
|
|||||||
onChange={(e) => handleLogLevelChange(e.target.value as LogLevel)}
|
onChange={(e) => handleLogLevelChange(e.target.value as LogLevel)}
|
||||||
>
|
>
|
||||||
<MenuItem value="all">ALL</MenuItem>
|
<MenuItem value="all">ALL</MenuItem>
|
||||||
|
<MenuItem value="debug">DEBUG</MenuItem>
|
||||||
<MenuItem value="info">INFO</MenuItem>
|
<MenuItem value="info">INFO</MenuItem>
|
||||||
<MenuItem value="warning">WARNING</MenuItem>
|
<MenuItem value="warning">WARNING</MenuItem>
|
||||||
<MenuItem value="error">ERROR</MenuItem>
|
<MenuItem value="error">ERROR</MenuItem>
|
||||||
<MenuItem value="debug">DEBUG</MenuItem>
|
|
||||||
</BaseStyledSelect>
|
</BaseStyledSelect>
|
||||||
<BaseSearchBox
|
<BaseSearchBox
|
||||||
onSearch={(matcher, state) => {
|
onSearch={(matcher, state) => {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import dayjs from "dayjs";
|
|||||||
// 最大日志数量
|
// 最大日志数量
|
||||||
const MAX_LOG_NUM = 1000;
|
const MAX_LOG_NUM = 1000;
|
||||||
|
|
||||||
export type LogLevel = "warning" | "info" | "debug" | "error" | "all";
|
export type LogLevel = "debug" | "info" | "warning" | "error" | "all";
|
||||||
|
|
||||||
export interface ILogItem {
|
export interface ILogItem {
|
||||||
time?: string;
|
time?: string;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
} from "@/services/cmds";
|
} from "@/services/cmds";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
export type LogLevel = "warning" | "info" | "debug" | "error" | "all";
|
export type LogLevel = "debug" | "info" | "warning" | "error" | "all";
|
||||||
|
|
||||||
export interface ILogItem {
|
export interface ILogItem {
|
||||||
time?: string;
|
time?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user