feat(日志): 增加后端日志级别过滤功能并优化前端显示

1. 在后端添加日志级别过滤参数,支持按级别筛选日志
2. 前端移除本地级别过滤,改为向后端传递过滤参数
3. 优化日志显示样式和自动刷新逻辑
This commit is contained in:
Wenjie Zhang 2025-12-23 11:21:34 +08:00
parent ccf6ddaaeb
commit 696cd97618
3 changed files with 46 additions and 15 deletions

View File

@ -52,18 +52,42 @@ async def update_config_batch(items: dict = Body(...), current_user: User = Depe
@system.get("/logs")
async def get_system_logs(current_user: User = Depends(get_admin_user)):
"""获取系统日志"""
async def get_system_logs(
levels: str | None = None,
current_user: User = Depends(get_admin_user)
):
"""获取系统日志
Args:
levels: 可选的日志级别过滤多个级别用逗号分隔 "INFO,ERROR,DEBUG,WARNING"
"""
try:
from src.utils.logging_config import LOG_FILE
# 解析日志级别过滤条件
level_filter = None
if levels:
level_filter = set(level.strip().upper() for level in levels.split(',') if level.strip())
async with aiofiles.open(LOG_FILE) as f:
# 读取最后1000行
lines = []
async for line in f:
lines.append(line)
if len(lines) > 1000:
lines.pop(0)
filtered_line = line.rstrip('\n\r')
# 如果指定了日志级别过滤,则按级别过滤
if level_filter:
# 日志格式: 2025-03-10 08:26:37,269 - INFO - module - message
# 提取日志级别
parts = filtered_line.split(' - ')
if len(parts) >= 2 and parts[1].strip() in level_filter:
lines.append(filtered_line + '\n')
# 继续读取以保持行数统计准确
if len(lines) > 1000:
lines.pop(0)
else:
lines.append(filtered_line + '\n')
if len(lines) > 1000:
lines.pop(0)
log = "".join(lines)
return {"log": log, "message": "success", "log_file": LOG_FILE}

View File

@ -54,9 +54,13 @@ export const configApi = {
/**
* 获取系统日志
* @param {string} levels - 可选的日志级别过滤多个级别用逗号分隔
* @returns {Promise} - 系统日志
*/
getLogs: async () => apiAdminGet('/api/system/logs')
getLogs: async (levels) => {
const url = levels ? `/api/system/logs?levels=${encodeURIComponent(levels)}` : '/api/system/logs';
return apiAdminGet(url);
}
}
// =============================================================================

View File

@ -196,7 +196,6 @@ const processedLogs = computed(() => {
return state.rawLogs
.map(parseLogLine)
.filter(log => log !== null)
.filter(log => state.selectedLevels.includes(log.level))
.filter(log => {
if (!state.searchText) return true;
return log.raw.toLowerCase().includes(state.searchText.toLowerCase());
@ -210,7 +209,9 @@ const fetchLogs = async () => {
state.fetching = true;
try {
error.value = '';
const logData = await configApi.getLogs();
//
const levelsParam = state.selectedLevels.join(',');
const logData = await configApi.getLogs(levelsParam);
state.rawLogs = logData.log.split('\n').filter(line => line.trim());
await nextTick();
@ -248,12 +249,18 @@ const toggleLogLevel = (level) => {
const index = currentLevels.indexOf(level);
if (index > -1) {
//
if (currentLevels.length === 1) {
return;
}
currentLevels.splice(index, 1);
} else {
currentLevels.push(level);
}
state.selectedLevels = currentLevels;
//
fetchLogs();
};
//
@ -470,10 +477,6 @@ const printAgentConfig = async () => {
</script>
<style scoped>
.log-viewer {
background: var(--gray-0);
}
.log-viewer.fullscreen {
padding: 16px;
}
@ -555,11 +558,11 @@ const printAgentConfig = async () => {
.log-container {
height: calc(80vh - 200px);
overflow-y: auto;
background: var(--gray-900);
color: var(--gray-0);
background: var(--gray-0);
color: var(--gray-1000);
border-radius: 5px;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 13px;
font-size: 12px;
}
.log-lines {