diff --git a/core/logger.py b/core/logger.py new file mode 100644 index 0000000..76ec223 --- /dev/null +++ b/core/logger.py @@ -0,0 +1,50 @@ +""" +日志模块 + +该模块负责初始化和配置 loguru 日志记录器,为整个应用程序提供统一的日志记录接口。 +""" +import sys +from pathlib import Path +from loguru import logger + +# 定义日志格式 +LOG_FORMAT = ( + "{time:YYYY-MM-DD HH:mm:ss.SSS} | " + "{level: <8} | " + "{name}:{function}:{line} - " + "{message}" +) + +# 移除 loguru 默认的处理器 +logger.remove() + +# 添加控制台输出处理器 +logger.add( + sys.stderr, + level="INFO", + format=LOG_FORMAT, + colorize=True, + enqueue=True # 异步写入 +) + +# 定义日志文件路径 +log_dir = Path("logs") +log_dir.mkdir(exist_ok=True) +log_file_path = log_dir / "{time:YYYY-MM-DD}.log" + +# 添加文件输出处理器 +logger.add( + log_file_path, + level="DEBUG", + format=LOG_FORMAT, + colorize=False, + rotation="00:00", # 每天午夜创建新文件 + retention="7 days", # 保留最近 7 天的日志 + encoding="utf-8", + enqueue=True, # 异步写入 + backtrace=True, # 记录完整的异常堆栈 + diagnose=True # 添加异常诊断信息 +) + +# 导出配置好的 logger +__all__ = ["logger"] diff --git a/core/plugin_manager.py b/core/plugin_manager.py index df15828..aa16f52 100644 --- a/core/plugin_manager.py +++ b/core/plugin_manager.py @@ -9,6 +9,7 @@ import pkgutil import sys from core.command_manager import matcher +from .logger import logger def load_all_plugins(): @@ -24,7 +25,7 @@ def load_all_plugins(): plugin_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "plugins") package_name = "plugins" - print(f" 正在从 {package_name} 加载插件...") + logger.info(f"正在从 {package_name} 加载插件...") for loader, module_name, is_pkg in pkgutil.iter_modules([plugin_dir]): full_module_name = f"{package_name}.{module_name}" @@ -43,6 +44,6 @@ def load_all_plugins(): matcher.plugins[full_module_name] = meta type_str = "包" if is_pkg else "文件" - print(f" [{type_str}] 成功{action}: {module_name}") + logger.success(f" [{type_str}] 成功{action}: {module_name}") except Exception as e: - print(f" {action if 'action' in locals() else '加载'}插件 {module_name} 失败: {e}") + logger.error(f" {action if 'action' in locals() else '加载'}插件 {module_name} 失败: {e}") diff --git a/core/redis_manager.py b/core/redis_manager.py index 2b232d2..3786d9c 100644 --- a/core/redis_manager.py +++ b/core/redis_manager.py @@ -1,5 +1,6 @@ import redis from .config_loader import global_config as config +from .logger import logger class RedisManager: """ @@ -20,7 +21,7 @@ class RedisManager: db = config.redis['db'] password = config.redis.get('password') - print(f" 正在尝试连接 Redis: {host}:{port}, DB: {db}") + logger.info(f"正在尝试连接 Redis: {host}:{port}, DB: {db}") cls._pool = redis.ConnectionPool( host=host, @@ -31,15 +32,15 @@ class RedisManager: ) cls._client = redis.Redis(connection_pool=cls._pool) if cls._client.ping(): - print(" Redis 连接成功!") + logger.success("Redis 连接成功!") else: - print(" Redis 连接失败: PING 命令无响应") + logger.error("Redis 连接失败: PING 命令无响应") except redis.exceptions.ConnectionError as e: - print(f" Redis 连接失败: {e}") + logger.error(f"Redis 连接失败: {e}") cls._pool = None cls._client = None except Exception as e: - print(f" Redis 初始化时发生未知错误: {e}") + logger.exception(f"Redis 初始化时发生未知错误: {e}") cls._pool = None cls._client = None diff --git a/core/ws.py b/core/ws.py index e35317f..c9d2b77 100644 --- a/core/ws.py +++ b/core/ws.py @@ -24,6 +24,7 @@ from models import EventFactory from .bot import Bot from .command_manager import matcher from .config_loader import global_config +from .logger import logger class WS: @@ -58,24 +59,23 @@ class WS: while True: try: - print(f" 正在尝试连接至 NapCat: {self.url}") + logger.info(f"正在尝试连接至 NapCat: {self.url}") async with websockets.connect( self.url, additional_headers=headers ) as websocket: self.ws = websocket - print(" 连接成功!") + logger.success("连接成功!") await self._listen_loop(websocket) except ( websockets.exceptions.ConnectionClosed, ConnectionRefusedError, ) as e: - print(f" 连接断开或服务器拒绝访问: {e}") + logger.warning(f"连接断开或服务器拒绝访问: {e}") except Exception as e: - print(f" 运行异常: {e}") - traceback.print_exc() + logger.exception(f"运行异常: {e}") - print(f" {self.reconnect_interval}秒后尝试重连...") + logger.info(f"{self.reconnect_interval}秒后尝试重连...") await asyncio.sleep(self.reconnect_interval) async def _listen_loop(self, websocket): @@ -108,8 +108,7 @@ class WS: asyncio.create_task(self.on_event(data)) except Exception as e: - print(f" 解析消息异常: {e}") - traceback.print_exc() + logger.exception(f"解析消息异常: {e}") async def on_event(self, raw_data: dict): """ @@ -130,21 +129,22 @@ class WS: event.bot = self.bot # 注入 Bot 实例 # 打印日志 - t = datetime.fromtimestamp(event.time).strftime("%H:%M:%S") if event.post_type == "message": sender_name = event.sender.nickname if event.sender else "Unknown" - print(f" [{t}] [消息] {event.message_type} | {event.user_id}({sender_name}): {event.raw_message}") + logger.info(f"[消息] {event.message_type} | {event.user_id}({sender_name}): {event.raw_message}") elif event.post_type == "notice": - print(f" [{t}] [通知] {event.notice_type}") + logger.info(f"[通知] {event.notice_type}") elif event.post_type == "request": - print(f" [{t}] [请求] {event.request_type}") + logger.info(f"[请求] {event.request_type}") + elif event.post_type == "meta_event": + logger.debug(f"[元事件] {event.meta_event_type}") + # 分发事件 await matcher.handle_event(self.bot, event) except Exception as e: - print(f" 事件处理异常: {e}") - traceback.print_exc() + logger.exception(f"事件处理异常: {e}") async def call_api(self, action: str, params: dict = None): """ @@ -162,11 +162,13 @@ class WS: 表示失败的字典。 """ if not self.ws: + logger.error("调用 API 失败: WebSocket 未初始化") return {"status": "failed", "msg": "websocket not initialized"} 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"} echo_id = str(uuid.uuid4()) @@ -182,5 +184,6 @@ class WS: 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"} diff --git a/main.py b/main.py index 50b87cc..1d9d6cc 100644 --- a/main.py +++ b/main.py @@ -10,9 +10,11 @@ import time from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler +# 初始化日志系统,必须在其他 core 模块导入之前执行 +from core.logger import logger + from core import WS from core.plugin_manager import load_all_plugins -#from core.redis_manager import redis_client class PluginReloadHandler(FileSystemEventHandler): @@ -55,17 +57,18 @@ class PluginReloadHandler(FileSystemEventHandler): self.last_reload_time = current_time - print(f"\n[HotReload] 检测到文件变更: {event.src_path}") - print("[HotReload] 正在重载插件...") + logger.info(f"检测到文件变更: {event.src_path}") + logger.info("正在重载插件...") try: # 重新扫描并加载插件 load_all_plugins() - print("[HotReload] 插件重载完成") + logger.success("插件重载完成") except Exception as e: - print(f"[HotReload] 重载失败: {e}") + logger.exception(f"重载失败: {e}") +@logger.catch async def main(): """ 主函数 @@ -87,9 +90,9 @@ async def main(): if os.path.exists(plugin_path): observer.schedule(event_handler, plugin_path, recursive=True) observer.start() - print(f"[HotReload] 已启动插件热重载监控: {plugin_path}") + logger.info(f"已启动插件热重载监控: {plugin_path}") else: - print(f"[HotReload] 警告: 插件目录不存在 {plugin_path}") + logger.warning(f"插件目录不存在 {plugin_path}") try: bot = WS() diff --git a/requirements.txt b/requirements.txt index a0a58de..f630282 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,4 @@ websockets==15.0.1 yarg==0.1.10 watchdog==6.0.0 redis==5.0.7 +loguru