feat: 实现统一的错误处理机制和增强日志系统
添加错误码定义和统一响应格式 增强日志记录功能,支持模块专用日志记录器 实现全局异常捕获和友好错误提示 更新文档说明错误处理机制
This commit is contained in:
115
core/ws.py
115
core/ws.py
@@ -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}
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user