File size: 8,319 Bytes
76b9762
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
"""
日志路由模块
"""

from datetime import datetime
from typing import Dict, List, Optional

from fastapi import (
    APIRouter,
    Body,
    HTTPException,
    Path,
    Query,
    Request,
    Response,
    status,
)
from pydantic import BaseModel

from app.core.security import verify_auth_token
from app.log.logger import get_log_routes_logger
from app.service.error_log import error_log_service

router = APIRouter(prefix="/api/logs", tags=["logs"])

logger = get_log_routes_logger()


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"
    ),
    start_date: Optional[datetime] = Query(
        None, description="Start datetime for filtering"
    ),
    end_date: Optional[datetime] = Query(
        None, description="End datetime for filtering"
    ),
    sort_by: str = Query(
        "id", description="Field to sort by (e.g., 'id', 'request_time')"
    ),
    sort_order: str = Query("desc", description="Sort order ('asc' or 'desc')"),
):
    """
    获取错误日志列表 (返回错误码),支持过滤和排序

    Args:
        request: 请求对象
        limit: 限制数量
        offset: 偏移量
        key_search: 密钥搜索
        error_search: 错误搜索 (可能搜索类型或日志内容,由DB层决定)
        error_code_search: 错误码搜索
        start_date: 开始日期
        end_date: 结束日期
        sort_by: 排序字段
        sort_order: 排序顺序

    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")
        raise HTTPException(status_code=401, detail="Not authenticated")

    try:
        result = await error_log_service.process_get_error_logs(
            limit=limit,
            offset=offset,
            key_search=key_search,
            error_search=error_search,
            error_code_search=error_code_search,
            start_date=start_date,
            end_date=end_date,
            sort_by=sort_by,
            sort_order=sort_order,
        )
        logs_data = result["logs"]
        total_count = result["total"]

        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
    request_msg: Optional[str] = None
    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:
        log_details = await error_log_service.process_get_error_log_details(
            log_id=log_id
        )
        if not log_details:
            raise HTTPException(status_code=404, detail="Error log not found")

        return ErrorLogDetailResponse(**log_details)
    except HTTPException as http_exc:
        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)}"
        )


@router.delete("/errors", status_code=status.HTTP_204_NO_CONTENT)
async def delete_error_logs_bulk_api(
    request: Request, payload: Dict[str, List[int]] = Body(...)
):
    """
    批量删除错误日志 (异步)
    """
    auth_token = request.cookies.get("auth_token")
    if not auth_token or not verify_auth_token(auth_token):
        logger.warning("Unauthorized access attempt to bulk delete error logs")
        raise HTTPException(status_code=401, detail="Not authenticated")

    log_ids = payload.get("ids")
    if not log_ids:
        raise HTTPException(status_code=400, detail="No log IDs provided for deletion.")

    try:
        deleted_count = await error_log_service.process_delete_error_logs_by_ids(
            log_ids
        )
        # 注意:异步函数返回的是尝试删除的数量,可能不是精确值
        logger.info(
            f"Attempted bulk deletion for {deleted_count} error logs with IDs: {log_ids}"
        )
        return Response(status_code=status.HTTP_204_NO_CONTENT)
    except Exception as e:
        logger.exception(f"Error bulk deleting error logs with IDs {log_ids}: {str(e)}")
        raise HTTPException(
            status_code=500, detail="Internal server error during bulk deletion"
        )


@router.delete("/errors/all", status_code=status.HTTP_204_NO_CONTENT)
async def delete_all_error_logs_api(request: Request):
    """
    删除所有错误日志 (异步)
    """
    auth_token = request.cookies.get("auth_token")
    if not auth_token or not verify_auth_token(auth_token):
        logger.warning("Unauthorized access attempt to delete all error logs")
        raise HTTPException(status_code=401, detail="Not authenticated")
 
    try:
        deleted_count = await error_log_service.process_delete_all_error_logs()
        logger.info(f"Successfully deleted all {deleted_count} error logs.")
        # No body needed for 204 response
        return Response(status_code=status.HTTP_204_NO_CONTENT)
    except Exception as e:
        logger.exception(f"Error deleting all error logs: {str(e)}")
        raise HTTPException(
            status_code=500, detail="Internal server error during deletion of all logs"
        )
 
 
@router.delete("/errors/{log_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_error_log_api(request: Request, log_id: int = Path(..., ge=1)):
    """
    删除单个错误日志 (异步)
    """
    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 delete error log ID: {log_id}")
        raise HTTPException(status_code=401, detail="Not authenticated")
 
    try:
        success = await error_log_service.process_delete_error_log_by_id(log_id)
        if not success:
            # 服务层现在在未找到时返回 False,我们在这里转换为 404
            raise HTTPException(
                status_code=404, detail=f"Error log with ID {log_id} not found"
            )
        logger.info(f"Successfully deleted error log with ID: {log_id}")
        return Response(status_code=status.HTTP_204_NO_CONTENT)
    except HTTPException as http_exc:
        raise http_exc
    except Exception as e:
        logger.exception(f"Error deleting error log with ID {log_id}: {str(e)}")
        raise HTTPException(
            status_code=500, detail="Internal server error during deletion"
        )