| |
|
|
| import time |
| import psutil |
| import logging |
| from typing import Dict, Any, List, Optional |
| from fastapi import APIRouter, Depends, HTTPException, status |
| from fastapi.responses import JSONResponse |
| from pydantic import BaseModel, Field |
|
|
| |
| from app.api.auth import get_auth_token, get_admin_api_key |
|
|
| logger = logging.getLogger(__name__) |
|
|
| |
| class MetricData(BaseModel): |
| """服务指标数据模型""" |
| total_requests: int = Field(0, description="总请求数") |
| successful_requests: int = Field(0, description="成功请求数") |
| failed_requests: int = Field(0, description="失败请求数") |
| active_keys_count: int = Field(0, description="活跃认证令牌数量") |
| average_response_time: float = Field(0.0, description="平均响应时间(秒)") |
| memory_usage: str = Field("N/A", description="当前内存使用情况") |
|
|
| class CallRecord(BaseModel): |
| """单次调用记录数据模型""" |
| timestamp: float = Field(..., description="请求时间戳(Unix时间)") |
| key: str = Field(..., description="认证令牌(部分隐藏)") |
| ip: str = Field(..., description="客户端 IP 地址") |
| type: str = Field(..., description="请求类型(如 chat_completion)") |
| model: str = Field("未知模型", description="使用的模型名称") |
| status: str = Field(..., description="请求状态(success 或 failed)") |
| response_time: float = Field(..., description="响应时间(秒)") |
| request_summary: str = Field("N/A", description="请求内容摘要") |
| response_summary: str = Field("N/A", description="响应内容摘要") |
|
|
| |
| |
| |
| service_metrics: MetricData = MetricData() |
| call_records: List[CallRecord] = [] |
| MAX_RECORDS = 1000 |
|
|
| |
| |
| active_tokens_status: Dict[str, float] = {} |
|
|
| |
| process = psutil.Process() |
|
|
| |
| router = APIRouter() |
|
|
| |
| def get_memory_usage(): |
| """获取当前进程的内存使用情况百分比""" |
| try: |
| |
| memory_info = process.memory_info() |
| |
| rss_mb = memory_info.rss / (1024 * 1024) |
|
|
| |
| logger.debug(f"原始内存数据: RSS={rss_mb:.2f} MB") |
|
|
| |
| return f"{rss_mb:.2f} MB" |
| except Exception as e: |
| logger.error(f"获取内存使用情况失败: {e}", exc_info=True) |
| return "N/A" |
|
|
|
|
| def update_metrics_on_request(auth_token: str, client_host: str): |
| """更新请求相关的服务指标""" |
| service_metrics.total_requests += 1 |
| |
| active_tokens_status[auth_token] = time.time() |
|
|
|
|
| def update_metrics_on_response( |
| auth_token: str, |
| status: str, |
| response_time: float, |
| client_host: str, |
| request: Optional[Dict[str, Any]] = None, |
| response: Optional[Dict[str, Any]] = None, |
| ): |
| """更新响应相关的服务指标和调用记录""" |
| if status == "success": |
| service_metrics.successful_requests += 1 |
| |
| |
| if service_metrics.total_requests > 0: |
| service_metrics.average_response_time = ( |
| service_metrics.average_response_time |
| * (service_metrics.total_requests - 1) |
| + response_time |
| ) / service_metrics.total_requests |
| else: |
| service_metrics.average_response_time = response_time |
| else: |
| service_metrics.failed_requests += 1 |
|
|
| |
| model_name = request.get("model", "未知模型") if request else "未知模型" |
|
|
| |
| request_summary = "N/A" |
| if request and request.get("messages"): |
| try: |
| |
| last_message_content = request["messages"][-1]["content"] |
| if isinstance(last_message_content, list): |
| text_parts = [p["text"] for p in last_message_content if p["type"] == "text"] |
| request_summary = (text_parts[0][:50] + "...") if text_parts else "多模态请求" |
| else: |
| request_summary = last_message_content[:50] |
| except Exception: |
| request_summary = "请求解析错误" |
|
|
| |
| response_summary = "N/A" |
| if response and response.get("choices") and response["choices"][0].get("message"): |
| try: |
| response_summary = response["choices"][0]["message"]["content"][ |
| :50 |
| ] |
| except Exception: |
| response_summary = "响应解析错误" |
|
|
| |
| record = CallRecord( |
| timestamp=time.time(), |
| key=f"***{auth_token[-4:]}", |
| ip=client_host, |
| type="chat_completion", |
| model=model_name, |
| status=status, |
| response_time=response_time, |
| request_summary=request_summary, |
| response_summary=response_summary, |
| ) |
| call_records.append(record) |
| |
| if len(call_records) > MAX_RECORDS: |
| call_records.pop(0) |
|
|
|
|
| def get_current_metrics() -> MetricData: |
| """获取当前服务指标数据""" |
| current_time = time.time() |
| |
| service_metrics.active_keys_count = len( |
| [ |
| token |
| for token, last_active in active_tokens_status.items() |
| if current_time - last_active < 300 |
| ] |
| ) |
|
|
| |
| service_metrics.memory_usage = get_memory_usage() |
|
|
| return service_metrics |
|
|
|
|
| def get_current_records() -> List[CallRecord]: |
| """获取最近的调用记录""" |
| |
| return sorted(call_records, key=lambda x: x.timestamp, reverse=True) |
|
|
| @router.get( |
| "/metrics", dependencies=[Depends(get_auth_token)] |
| ) |
| async def get_metrics_endpoint(auth_token: str = Depends(get_auth_token)): |
| """ |
| 获取服务运行指标。 |
| """ |
| |
| |
| |
| try: |
| metrics_data = get_current_metrics() |
| return JSONResponse(content=metrics_data.model_dump()) |
| except Exception as e: |
| logger.error(f"获取指标时发生错误: {e}", exc_info=True) |
| raise HTTPException( |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
| detail=f"获取指标失败: {e}", |
| ) |
|
|
| @router.get( |
| "/records", dependencies=[Depends(get_auth_token)] |
| ) |
| async def get_records_endpoint(auth_token: str = Depends(get_auth_token)): |
| """ |
| 获取服务调用记录。 |
| """ |
| try: |
| records_data = get_current_records() |
| |
| return JSONResponse(content=[record.model_dump() for record in records_data]) |
| except Exception as e: |
| logger.error(f"获取记录时发生错误: {e}", exc_info=True) |
| raise HTTPException( |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
| detail=f"获取记录失败: {e}", |
| ) |
|
|