From d94d24f96c307b9a0cbc09e9062db1b3b7ff2625 Mon Sep 17 00:00:00 2001 From: snaily Date: Thu, 10 Apr 2025 15:40:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(error=5Fhandling):=20=E5=A2=9E=E5=BC=BA=20?= =?UTF-8?q?API=20=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86=E5=92=8C=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 扩展 ErrorLog 数据模型,增加 model_name, error_type, error_code 字段,以记录更详细的错误信息。 - 在 GeminiChatService 和 OpenAIChatService 中添加了 try-except 块,用于捕获 API 调用(包括普通和流式调用)时发生的异常。 - 实现从异常消息中通过正则表达式提取 HTTP 状态码的功能。 - 调用 add_error_log 服务将详细的错误信息(包括模型、错误类型、代码、请求体)持久化到数据库。 - 更新了 error_logs 前端页面,增加显示模型名称列及详情。 - 优化数据库连接池配置 (pool_recycle=3600),提高连接稳定性。 --- app/database/connection.py | 5 +-- app/database/models.py | 3 ++ app/database/services.py | 7 ++++ app/service/chat/gemini_chat_service.py | 43 ++++++++++++++++++++-- app/service/chat/openai_chat_service.py | 47 ++++++++++++++++++++++--- app/static/js/error_logs.js | 3 ++ app/templates/error_logs.html | 5 +++ 7 files changed, 103 insertions(+), 10 deletions(-) diff --git a/app/database/connection.py b/app/database/connection.py index 1e52f8f..dca6d27 100644 --- a/app/database/connection.py +++ b/app/database/connection.py @@ -22,8 +22,9 @@ metadata = MetaData() # 创建基类 Base = declarative_base(metadata=metadata) -# 创建数据库连接池 -database = Database(DATABASE_URL) +# 创建数据库连接池,并配置连接池参数 +# pool_recycle=3600: 回收空闲超过1小时的连接,防止MySQL服务器超时断开 +database = Database(DATABASE_URL, min_size=5, max_size=20, pool_recycle=3600) async def connect_to_db(): diff --git a/app/database/models.py b/app/database/models.py index 6a3bbd0..162e3cb 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -32,7 +32,10 @@ class ErrorLog(Base): id = Column(Integer, primary_key=True, autoincrement=True) gemini_key = Column(String(100), nullable=True, comment="Gemini API密钥") + model_name = Column(String(100), nullable=True, comment="模型名称") + error_type = Column(String(50), nullable=True, comment="错误类型") error_log = Column(Text, nullable=True, comment="错误日志") + error_code = Column(Integer, nullable=True, comment="错误代码") request_msg = Column(JSON, nullable=True, comment="请求消息") request_time = Column(DateTime, default=datetime.datetime.now, comment="请求时间") diff --git a/app/database/services.py b/app/database/services.py index 52e9f28..4158b7c 100644 --- a/app/database/services.py +++ b/app/database/services.py @@ -101,7 +101,10 @@ async def update_setting(key: str, value: str, description: Optional[str] = None async def add_error_log( gemini_key: Optional[str] = None, + model_name: Optional[str] = None, + error_type: Optional[str] = None, error_log: Optional[str] = None, + error_code: Optional[int] = None, request_msg: Optional[Union[Dict[str, Any], str]] = None ) -> bool: """ @@ -110,6 +113,7 @@ async def add_error_log( Args: gemini_key: Gemini API密钥 error_log: 错误日志 + error_code: 错误代码 (例如 HTTP 状态码) request_msg: 请求消息 Returns: @@ -132,7 +136,10 @@ async def add_error_log( insert(ErrorLog) .values( gemini_key=gemini_key, + error_type=error_type, error_log=error_log, + model_name=model_name, + error_code=error_code, request_msg=request_msg_json, request_time=datetime.datetime.now() ) diff --git a/app/service/chat/gemini_chat_service.py b/app/service/chat/gemini_chat_service.py index 61955e9..3d2a291 100644 --- a/app/service/chat/gemini_chat_service.py +++ b/app/service/chat/gemini_chat_service.py @@ -1,6 +1,7 @@ # app/services/chat_service.py import json +import re from typing import Any, AsyncGenerator, Dict, List from app.config.config import settings @@ -10,6 +11,7 @@ from app.handler.stream_optimizer import gemini_optimizer from app.log.logger import get_gemini_logger from app.service.client.api_client import GeminiApiClient from app.service.key.key_manager import KeyManager +from app.database.services import add_error_log logger = get_gemini_logger() @@ -145,8 +147,27 @@ class GeminiChatService: ) -> Dict[str, Any]: """生成内容""" payload = _build_payload(model, request) - response = await self.api_client.generate_content(payload, model, api_key) - return self.response_handler.handle_response(response, model, stream=False) + try: + response = await self.api_client.generate_content(payload, model, api_key) + return self.response_handler.handle_response(response, model, stream=False) + except Exception as e: + logger.error(f"Normal API call failed with error: {str(e)}") + error_code = None + error_log_msg = str(e) + # 尝试从异常消息中解析状态码 + match = re.search(r"status code (\d+)", error_log_msg) + if match: + error_code = int(match.group(1)) + + await add_error_log( + gemini_key=api_key, + model_name=model, + error_type="gemini_chat_service", + error_log=error_log_msg, + error_code=error_code, + request_msg=payload + ) + raise e # 重新抛出异常,以便上层处理 async def stream_generate_content( self, model: str, request: GeminiRequest, api_key: str @@ -185,9 +206,25 @@ class GeminiChatService: break except Exception as e: retries += 1 + error_log_msg = str(e) logger.warning( - f"Streaming API call failed with error: {str(e)}. Attempt {retries} of {max_retries}" + f"Streaming API call failed with error: {error_log_msg}. Attempt {retries} of {max_retries}" ) + # 解析错误信息并记录到数据库 + error_code = None + match = re.search(r"status code (\d+)", error_log_msg) + if match: + error_code = int(match.group(1)) + + await add_error_log( + gemini_key=api_key, + model_name=model, + error_log=error_log_msg, + error_code=error_code, + request_msg=payload + ) + + # 尝试切换 API Key api_key = await self.key_manager.handle_api_failure(api_key) logger.info(f"Switched to new API key: {api_key}") if retries >= max_retries: diff --git a/app/service/chat/openai_chat_service.py b/app/service/chat/openai_chat_service.py index be87f59..29ac7a2 100644 --- a/app/service/chat/openai_chat_service.py +++ b/app/service/chat/openai_chat_service.py @@ -1,6 +1,7 @@ # app/services/chat_service.py import json +import re from copy import deepcopy from typing import Any, AsyncGenerator, Dict, List, Optional, Union @@ -13,6 +14,7 @@ from app.log.logger import get_openai_logger from app.service.client.api_client import GeminiApiClient from app.service.image.image_create_service import ImageCreateService from app.service.key.key_manager import KeyManager +from app.database.services import add_error_log logger = get_openai_logger() @@ -189,10 +191,28 @@ class OpenAIChatService: self, model: str, payload: Dict[str, Any], api_key: str ) -> Dict[str, Any]: """处理普通聊天完成""" - response = await self.api_client.generate_content(payload, model, api_key) - return self.response_handler.handle_response( - response, model, stream=False, finish_reason="stop" - ) + try: + response = await self.api_client.generate_content(payload, model, api_key) + return self.response_handler.handle_response( + response, model, stream=False, finish_reason="stop" + ) + except Exception as e: + logger.error(f"Normal API call failed with error: {str(e)}") + error_code = None + error_log_msg = str(e) + # 尝试从异常消息中解析状态码 + match = re.search(r"status code (\d+)", error_log_msg) + if match: + error_code = int(match.group(1)) + + await add_error_log( + gemini_key=api_key, + model_name=model, + error_log=error_log_msg, + error_code=error_code, + request_msg=payload + ) + raise e # 重新抛出异常,以便上层处理 async def _handle_stream_completion( self, model: str, payload: Dict[str, Any], api_key: str @@ -241,9 +261,26 @@ class OpenAIChatService: break # 成功后退出循环 except Exception as e: retries += 1 + error_log_msg = str(e) logger.warning( - f"Streaming API call failed with error: {str(e)}. Attempt {retries} of {max_retries}" + f"Streaming API call failed with error: {error_log_msg}. Attempt {retries} of {max_retries}" ) + # 解析错误信息并记录到数据库 + error_code = None + match = re.search(r"status code (\d+)", error_log_msg) + if match: + error_code = int(match.group(1)) + print(model) + await add_error_log( + gemini_key=api_key, + model_name=model, + error_type="openai_chat_service", + error_log=error_log_msg, + error_code=error_code, + request_msg=payload + ) + + # 尝试切换 API Key api_key = await self.key_manager.handle_api_failure(api_key) logger.info(f"Switched to new API key: {api_key}") if retries >= max_retries: diff --git a/app/static/js/error_logs.js b/app/static/js/error_logs.js index 4c84828..c2d30d4 100644 --- a/app/static/js/error_logs.js +++ b/app/static/js/error_logs.js @@ -109,6 +109,7 @@ function renderErrorLogs() { ${log.id} ${log.gemini_key || '无'} ${errorLogContent} + ${log.model_name || '未知'} ${formattedTime}