mirror of
https://github.com/snailyp/gemini-balance.git
synced 2026-05-11 18:09:55 +08:00
feat(error_handling): 增强 API 错误处理和日志记录
- 扩展 ErrorLog 数据模型,增加 model_name, error_type, error_code 字段,以记录更详细的错误信息。 - 在 GeminiChatService 和 OpenAIChatService 中添加了 try-except 块,用于捕获 API 调用(包括普通和流式调用)时发生的异常。 - 实现从异常消息中通过正则表达式提取 HTTP 状态码的功能。 - 调用 add_error_log 服务将详细的错误信息(包括模型、错误类型、代码、请求体)持久化到数据库。 - 更新了 error_logs 前端页面,增加显示模型名称列及详情。 - 优化数据库连接池配置 (pool_recycle=3600),提高连接稳定性。
This commit is contained in:
@@ -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():
|
||||
|
||||
@@ -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="请求时间")
|
||||
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -109,6 +109,7 @@ function renderErrorLogs() {
|
||||
<td>${log.id}</td>
|
||||
<td>${log.gemini_key || '无'}</td>
|
||||
<td class="error-log-content">${errorLogContent}</td>
|
||||
<td>${log.model_name || '未知'}</td>
|
||||
<td>${formattedTime}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary btn-view-details" data-log-id="${log.id}">
|
||||
@@ -168,6 +169,8 @@ function showLogDetails(logId) {
|
||||
document.getElementById('modalGeminiKey').textContent = log.gemini_key || '无';
|
||||
document.getElementById('modalErrorLog').textContent = log.error_log || '无';
|
||||
document.getElementById('modalRequestMsg').textContent = formattedRequestMsg;
|
||||
// Add model name display logic here - assuming an element with id 'modalModelName' exists
|
||||
document.getElementById('modalModelName').textContent = log.model_name || '未知';
|
||||
document.getElementById('modalRequestTime').textContent = formattedTime;
|
||||
|
||||
// 显示模态框
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
<th>ID</th>
|
||||
<th>Gemini密钥</th>
|
||||
<th>错误日志</th>
|
||||
<th>模型名称</th>
|
||||
<th>请求时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
@@ -138,6 +139,10 @@
|
||||
<h6>请求消息:</h6>
|
||||
<pre id="modalRequestMsg" class="bg-light p-2 rounded"></pre>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<h6>模型名称:</h6>
|
||||
<p id="modalModelName"></p>
|
||||
</div>
|
||||
<div>
|
||||
<h6>请求时间:</h6>
|
||||
<p id="modalRequestTime"></p>
|
||||
|
||||
Reference in New Issue
Block a user