72 lines
2.9 KiB
Python
72 lines
2.9 KiB
Python
# =============================================================================
|
|
# 企微IT智能服务台 — 运行期日志 API (standard SOP 第三阶段)
|
|
# =============================================================================
|
|
# 说明:管理后台查看后端运行期日志
|
|
# GET /admin/runtime-logs 正常查询(分页 + 级别/时间/关键字筛选)
|
|
# GET /admin/runtime-logs?download=true 下载命中行(text/plain 附件)
|
|
# 权限:require_admin(非 admin 返回 403,由依赖装饰器强制)
|
|
# =============================================================================
|
|
|
|
import logging
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
from fastapi import APIRouter, Depends, Query
|
|
from fastapi.responses import StreamingResponse
|
|
|
|
from app.dependencies import require_admin, get_current_user, UserInfo
|
|
from app.services.runtime_log_service import (
|
|
iter_runtime_log_lines,
|
|
query_runtime_logs,
|
|
)
|
|
from app.utils.response import success_response
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/admin/runtime-logs", tags=["运行期日志"])
|
|
|
|
|
|
@router.get("")
|
|
@require_admin
|
|
async def get_runtime_logs(
|
|
level: str = Query("INFO", description="日志级别阈值(DEBUG/INFO/WARNING/ERROR/CRITICAL),返回 >= 该级别"),
|
|
from_time: Optional[datetime] = Query(None, alias="from", description="起始时间(ISO8601)"),
|
|
to_time: Optional[datetime] = Query(None, alias="to", description="结束时间(ISO8601)"),
|
|
keyword: Optional[str] = Query(None, description="按消息关键字子串筛选(大小写不敏感)"),
|
|
page: int = Query(1, ge=1, description="页码"),
|
|
page_size: int = Query(50, ge=1, le=500, description="每页条数"),
|
|
download: bool = Query(False, description="true 时返回命中行文本下载"),
|
|
current_user: UserInfo = Depends(get_current_user),
|
|
):
|
|
"""查询后端运行期日志(分页 + 级别/时间/关键字筛选)。
|
|
|
|
权限:仅 admin 角色可访问,非 admin 由 require_admin 装饰器返回 403。
|
|
"""
|
|
# 下载模式:流式返回命中行文本,触发浏览器文件下载
|
|
if download:
|
|
ts = datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
headers = {
|
|
"Content-Disposition": f'attachment; filename="runtime-logs-{ts}.log"'
|
|
}
|
|
return StreamingResponse(
|
|
iter_runtime_log_lines(
|
|
level=level,
|
|
from_time=from_time,
|
|
to_time=to_time,
|
|
keyword=keyword,
|
|
),
|
|
media_type="text/plain; charset=utf-8",
|
|
headers=headers,
|
|
)
|
|
|
|
# 普通查询:分页返回结构化条目
|
|
result = await query_runtime_logs(
|
|
level=level,
|
|
from_time=from_time,
|
|
to_time=to_time,
|
|
keyword=keyword,
|
|
page=page,
|
|
page_size=page_size,
|
|
)
|
|
return success_response(data=result)
|