feat: 实现统一的错误处理机制和增强日志系统

添加错误码定义和统一响应格式
增强日志记录功能,支持模块专用日志记录器
实现全局异常捕获和友好错误提示
更新文档说明错误处理机制
This commit is contained in:
2026-01-19 14:05:14 +08:00
parent 698240b1a2
commit 8beeaef424
14 changed files with 1074 additions and 364 deletions

View File

@@ -25,7 +25,11 @@ from .bot import Bot
from .config_loader import global_config
from .managers.command_manager import matcher
from .utils.executor import CodeExecutor
from .utils.logger import logger
from .utils.logger import logger, ModuleLogger
from .utils.exceptions import (
WebSocketError, WebSocketConnectionError, WebSocketAuthenticationError
)
from .utils.error_codes import ErrorCode, create_error_response
class WS:
@@ -45,11 +49,15 @@ class WS:
self.token = cfg.token
self.reconnect_interval = cfg.reconnect_interval
# 初始化状态
self.ws: Optional[WebSocketClientProtocol] = None
self._pending_requests: Dict[str, asyncio.Future] = {}
self._pending_requests: Dict[str, asyncio.Future] = {} # echo: future
self.bot: Bot | None = None
self.self_id: int | None = None
self.code_executor = code_executor
# 创建模块专用日志记录器
self.logger = ModuleLogger("WebSocket")
async def connect(self) -> None:
"""
@@ -62,24 +70,43 @@ class WS:
while True:
try:
logger.info(f"正在尝试连接至 NapCat: {self.url}")
self.logger.info(f"正在尝试连接至 NapCat: {self.url}")
async with websockets.connect(
self.url, additional_headers=headers
) as websocket_raw:
websocket = cast(WebSocketClientProtocol, websocket_raw)
self.ws = websocket
logger.success("连接成功!")
self.logger.success("连接成功!")
await self._listen_loop(websocket)
except websockets.exceptions.AuthenticationError as e:
error = WebSocketAuthenticationError(
message=f"WebSocket认证失败: {str(e)}",
code=ErrorCode.WS_AUTH_FAILED,
original_error=e
)
self.logger.error(f"连接失败: {error.message}")
self.logger.log_custom_exception(error)
except (
websockets.exceptions.ConnectionClosed,
ConnectionRefusedError,
) as e:
logger.warning(f"连接断开或服务器拒绝访问: {e}")
error = WebSocketConnectionError(
message=f"连接断开或服务器拒绝访问: {str(e)}",
code=ErrorCode.WS_CONNECTION_FAILED,
original_error=e
)
self.logger.warning(f"连接失败: {error.message}")
except Exception as e:
logger.exception(f"运行异常: {e}")
error = WebSocketError(
message=f"WebSocket运行异常: {str(e)}",
code=ErrorCode.WS_MESSAGE_ERROR,
original_error=e
)
self.logger.exception(f"运行异常: {error.message}")
self.logger.log_custom_exception(error)
logger.info(f"{self.reconnect_interval}秒后尝试重连...")
self.logger.info(f"{self.reconnect_interval}秒后尝试重连...")
await asyncio.sleep(self.reconnect_interval)
async def _listen_loop(self, websocket_connection: WebSocketClientProtocol) -> None:
@@ -111,8 +138,22 @@ class WS:
# 使用 create_task 异步执行,避免阻塞 WebSocket 接收循环
asyncio.create_task(self.on_event(data))
except json.JSONDecodeError as e:
error = WebSocketError(
message=f"JSON解析失败: {str(e)}",
code=ErrorCode.WS_MESSAGE_ERROR,
original_error=e
)
self.logger.error(f"解析消息异常: {error.message}")
self.logger.debug(f"原始消息: {message}")
except Exception as e:
logger.exception(f"解析消息异常: {e}")
error = WebSocketError(
message=f"处理消息异常: {str(e)}",
code=ErrorCode.WS_MESSAGE_ERROR,
original_error=e
)
self.logger.exception(f"解析消息异常: {error.message}")
self.logger.log_custom_exception(error)
async def on_event(self, event_data: Dict[str, Any]) -> None:
"""
@@ -136,17 +177,17 @@ class WS:
if self.bot is None and hasattr(event, 'self_id'):
self.self_id = event.self_id
self.bot = Bot(self)
logger.success(f"Bot 实例初始化完成: self_id={self.self_id}")
self.logger.success(f"Bot 实例初始化完成: self_id={self.self_id}")
# 将代码执行器注入到 Bot 和执行器自身
if self.code_executor:
self.bot.code_executor = self.code_executor
self.code_executor.bot = self.bot
logger.info("代码执行器已成功注入 Bot 实例。")
self.logger.info("代码执行器已成功注入 Bot 实例。")
# 如果 bot 尚未初始化,则不处理后续事件
if self.bot is None:
logger.warning("Bot 尚未初始化,跳过事件处理。")
self.logger.warning("Bot 尚未初始化,跳过事件处理。")
return
event.bot = self.bot # 注入 Bot 实例
@@ -157,23 +198,28 @@ class WS:
message_type = getattr(event, "message_type", "Unknown")
user_id = getattr(event, "user_id", "Unknown")
raw_message = getattr(event, "raw_message", "")
logger.info(f"[消息] {message_type} | {user_id}({sender_name}): {raw_message}")
self.logger.info(f"[消息] {message_type} | {user_id}({sender_name}): {raw_message}")
elif event.post_type == "notice":
notice_type = getattr(event, "notice_type", "Unknown")
logger.info(f"[通知] {notice_type}")
self.logger.info(f"[通知] {notice_type}")
elif event.post_type == "request":
request_type = getattr(event, "request_type", "Unknown")
logger.info(f"[请求] {request_type}")
self.logger.info(f"[请求] {request_type}")
elif event.post_type == "meta_event":
meta_event_type = getattr(event, "meta_event_type", "Unknown")
logger.debug(f"[元事件] {meta_event_type}")
self.logger.debug(f"[元事件] {meta_event_type}")
# 分发事件
await matcher.handle_event(self.bot, event)
except Exception as e:
logger.exception(f"事件处理异常: {e}")
self.logger.exception(f"事件处理异常: {str(e)}")
error = WebSocketError(
message=f"事件处理异常: {str(e)}",
code=ErrorCode.WS_MESSAGE_ERROR,
original_error=e
)
self.logger.log_custom_exception(error)
async def call_api(self, action: str, params: Optional[Dict[Any, Any]] = None) -> Dict[Any, Any]:
"""
@@ -191,14 +237,22 @@ class WS:
表示失败的字典。
"""
if not self.ws:
logger.error("调用 API 失败: WebSocket 未初始化")
return {"status": "failed", "msg": "websocket not initialized"}
self.logger.error("调用 API 失败: WebSocket 未初始化")
return create_error_response(
code=ErrorCode.WS_DISCONNECTED,
message="WebSocket未初始化",
data={"action": action, "params": params}
)
from websockets.protocol import State
if getattr(self.ws, "state", None) is not State.OPEN:
logger.error("调用 API 失败: WebSocket 连接未打开")
return {"status": "failed", "msg": "websocket is not open"}
self.logger.error("调用 API 失败: WebSocket 连接未打开")
return create_error_response(
code=ErrorCode.WS_DISCONNECTED,
message="WebSocket连接未打开",
data={"action": action, "params": params}
)
echo_id = str(uuid.uuid4())
payload = {"action": action, "params": params or {}, "echo": echo_id}
@@ -207,12 +261,23 @@ class WS:
future = loop.create_future()
self._pending_requests[echo_id] = future
await self.ws.send(json.dumps(payload))
try:
await self.ws.send(json.dumps(payload))
return await asyncio.wait_for(future, timeout=30.0)
except asyncio.TimeoutError:
self._pending_requests.pop(echo_id, None)
logger.warning(f"API 调用超时: action={action}, params={params}")
return {"status": "failed", "retcode": -1, "msg": "api timeout"}
self.logger.warning(f"API 调用超时: action={action}, params={params}")
return create_error_response(
code=ErrorCode.TIMEOUT_ERROR,
message="API调用超时",
data={"action": action, "params": params}
)
except Exception as e:
self._pending_requests.pop(echo_id, None)
self.logger.exception(f"API 调用异常: action={action}, error={str(e)}")
return create_error_response(
code=ErrorCode.WS_MESSAGE_ERROR,
message=f"API调用异常: {str(e)}",
data={"action": action, "params": params}
)