mirror of
https://github.com/snailyp/gemini-balance.git
synced 2026-09-05 07:26:39 +08:00
feat(error-logs): 增强错误日志功能和UI交互
- 新增错误码搜索功能,支持精确匹配错误码 - 重构复制功能,支持批量选择和复制密钥 - 优化UI布局和交互体验,添加悬停复制按钮 - 重构路由结构,将log_routes.py重命名为error_log_routes.py
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
"""
|
||||
日志路由模块
|
||||
"""
|
||||
from typing import List, Optional
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel
|
||||
from fastapi import APIRouter, HTTPException, Request, Query, Path
|
||||
|
||||
from app.core.security import verify_auth_token
|
||||
from app.log.logger import get_log_routes_logger
|
||||
# 假设这些服务函数已更新或添加
|
||||
from app.database.services import get_error_logs, get_error_logs_count, get_error_log_details
|
||||
|
||||
# 创建路由
|
||||
router = APIRouter(prefix="/api/logs", tags=["logs"])
|
||||
|
||||
logger = get_log_routes_logger()
|
||||
|
||||
|
||||
# Define a response model that includes the total count for pagination
|
||||
# 用于列表响应的模型,假设 get_error_logs 返回包含 error_code 的字典
|
||||
class ErrorLogListItem(BaseModel):
|
||||
id: int
|
||||
gemini_key: Optional[str] = None
|
||||
error_type: Optional[str] = None
|
||||
error_code: Optional[int] = None # 列表显示错误码 (应为整数)
|
||||
model_name: Optional[str] = None
|
||||
request_time: Optional[datetime] = None
|
||||
|
||||
class ErrorLogListResponse(BaseModel):
|
||||
logs: List[ErrorLogListItem] # 使用定义的模型列表
|
||||
total: int
|
||||
|
||||
@router.get("/errors", response_model=ErrorLogListResponse)
|
||||
async def get_error_logs_api(
|
||||
request: Request,
|
||||
limit: int = Query(10, ge=1, le=1000),
|
||||
offset: int = Query(0, ge=0),
|
||||
key_search: Optional[str] = Query(None, description="Search term for Gemini key (partial match)"),
|
||||
error_search: Optional[str] = Query(None, description="Search term for error type or log message"), # 数据库查询需处理
|
||||
error_code_search: Optional[str] = Query(None, description="Search term for error code"), # Added error code search parameter
|
||||
start_date: Optional[datetime] = Query(None, description="Start datetime for filtering"),
|
||||
end_date: Optional[datetime] = Query(None, description="End datetime for filtering")
|
||||
):
|
||||
"""
|
||||
获取错误日志列表 (返回错误码)
|
||||
|
||||
Args:
|
||||
request: 请求对象
|
||||
limit: 限制数量
|
||||
offset: 偏移量
|
||||
key_search: 密钥搜索
|
||||
error_search: 错误搜索 (可能搜索类型或日志内容,由DB层决定)
|
||||
error_code_search: 错误码搜索
|
||||
start_date: 开始日期
|
||||
end_date: 结束日期
|
||||
|
||||
Returns:
|
||||
ErrorLogListResponse: An object containing the list of logs (with error_code) and the total count.
|
||||
"""
|
||||
auth_token = request.cookies.get("auth_token")
|
||||
if not auth_token or not verify_auth_token(auth_token):
|
||||
logger.warning("Unauthorized access attempt to error logs list")
|
||||
# API 返回 401 更合适
|
||||
raise HTTPException(status_code=401, detail="Not authenticated")
|
||||
|
||||
try:
|
||||
# 假设 get_error_logs 现在返回包含 error_code 的字典列表
|
||||
# 并且可以接受 include_error_code 参数 (如果需要显式指定)
|
||||
logs_data = await get_error_logs(
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
key_search=key_search,
|
||||
error_search=error_search, # 数据库查询需要处理这个
|
||||
error_code_search=error_code_search, # Pass error code search to DB function
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
)
|
||||
# Fetch total count with the same search parameters
|
||||
total_count = await get_error_logs_count(
|
||||
key_search=key_search,
|
||||
error_search=error_search,
|
||||
error_code_search=error_code_search, # Pass error code search to DB count function
|
||||
start_date=start_date,
|
||||
end_date=end_date
|
||||
)
|
||||
# 验证并转换数据以匹配 Pydantic 模型
|
||||
validated_logs = [ErrorLogListItem(**log) for log in logs_data]
|
||||
return ErrorLogListResponse(logs=validated_logs, total=total_count)
|
||||
except Exception as e:
|
||||
logger.exception(f"Failed to get error logs list: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Failed to get error logs list: {str(e)}")
|
||||
|
||||
|
||||
# 新增:获取错误日志详情的路由
|
||||
class ErrorLogDetailResponse(BaseModel):
|
||||
id: int
|
||||
gemini_key: Optional[str] = None
|
||||
error_type: Optional[str] = None
|
||||
error_log: Optional[str] = None # 详情接口返回完整的 error_log
|
||||
request_msg: Optional[str] = None # 详情接口返回 request_msg
|
||||
model_name: Optional[str] = None
|
||||
request_time: Optional[datetime] = None
|
||||
|
||||
@router.get("/errors/{log_id}/details", response_model=ErrorLogDetailResponse)
|
||||
async def get_error_log_detail_api(request: Request, log_id: int = Path(..., ge=1)):
|
||||
"""
|
||||
根据日志 ID 获取错误日志的详细信息 (包括 error_log 和 request_msg)
|
||||
"""
|
||||
auth_token = request.cookies.get("auth_token")
|
||||
if not auth_token or not verify_auth_token(auth_token):
|
||||
logger.warning(f"Unauthorized access attempt to error log details for ID: {log_id}")
|
||||
raise HTTPException(status_code=401, detail="Not authenticated")
|
||||
|
||||
try:
|
||||
# 假设存在一个函数 get_error_log_details(log_id) 来获取完整信息
|
||||
log_details = await get_error_log_details(log_id=log_id)
|
||||
if not log_details:
|
||||
raise HTTPException(status_code=404, detail="Error log not found")
|
||||
|
||||
# 假设 get_error_log_details 返回一个字典或兼容 Pydantic 的对象
|
||||
return ErrorLogDetailResponse(**log_details)
|
||||
except HTTPException as http_exc:
|
||||
# Re-raise HTTPException (like 404)
|
||||
raise http_exc
|
||||
except Exception as e:
|
||||
logger.exception(f"Failed to get error log details for ID {log_id}: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Failed to get error log details: {str(e)}")
|
||||
Reference in New Issue
Block a user