为MaiBot适配器增加插件级DEBUG日志开关

变更项:\n1. 新增 MaiBotAdapter.log_level 配置项,支持 INFO/DEBUG,默认非法值自动回落 INFO。\n2. 在插件内新增 _log_runtime 方法,根据 log_level 动态输出运行日志级别,便于在不改全局日志配置的情况下单独拉高该插件观测粒度。\n3. 将适配器关键运行日志(初始化、连接、收发包、ACK、路由判定、入队、回复决策)统一接入 _log_runtime。\n4. 在插件配置中增加详细中文注释,并按你的要求将 log_level 设为 DEBUG。
This commit is contained in:
liuwei
2026-04-29 10:37:42 +08:00
parent 84992bcd6c
commit e9a31337bf
2 changed files with 37 additions and 20 deletions

View File

@@ -1,5 +1,11 @@
[MaiBotAdapter]
enable = false
enable = true
# 插件日志级别:
# 1. INFO输出关键生命周期与异常信息日志量更小
# 2. DEBUG输出完整运行细节入队、路由判定、收发包、ACK 等);
# 3. 你当前要求查看更细日志,默认改为 DEBUG。
log_level = "DEBUG"
# 是否默认采集群消息到 MaiBot
# 1. 这是“把消息送给 MaiBot 做上下文与记忆”的总开关;

View File

@@ -80,6 +80,7 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
self._reconnect_delay = 5
self._queue_maxsize = 500
self._max_send_retries = 3
self._log_level = "INFO"
# 运行时状态。
self._config_ready = False
@@ -117,6 +118,9 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
self._reconnect_delay = max(1, int(plugin_config.get("reconnect_delay", 5) or 5))
self._queue_maxsize = max(50, int(plugin_config.get("queue_maxsize", 500) or 500))
self._max_send_retries = max(1, int(plugin_config.get("max_send_retries", 3) or 3))
self._log_level = str(plugin_config.get("log_level", "INFO") or "INFO").strip().upper()
if self._log_level not in {"DEBUG", "INFO"}:
self._log_level = "INFO"
# 官方 API Server 至少需要 ws 地址与 api_key
# 1. 地址用于建立长期 WebSocket
@@ -126,7 +130,7 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
if not self._config_ready:
self.LOG.warning(f"[{self.name}] api_server_ws_url/api_key 未配置完整,插件会加载成功但不会转发消息")
self.LOG.info(
self._log_runtime(
f"[{self.name}] 初始化完成: enabled={self._enabled}, "
f"collect_group_messages={self._collect_group_messages}, collect_private_messages={self._collect_private_messages}, "
f"enable_reply_output={self._enable_reply_output}, reply_group_messages={self._reply_group_messages}, "
@@ -137,7 +141,7 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
def start(self) -> bool:
self.status = PluginStatus.RUNNING
self.LOG.info(f"[{self.name}] 插件已启动,等待首条消息后再懒启动长连接")
self._log_runtime(f"[{self.name}] 插件已启动,等待首条消息后再懒启动长连接")
return True
def stop(self) -> bool:
@@ -192,7 +196,7 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
payload = self._build_outbound_payload(message=message, normalized_content=normalized_content)
try:
outbound_queue.put_nowait(payload)
self.LOG.info(
self._log_runtime(
f"[{self.name}] 消息已入队: route_type={payload['route_type']}, roomid={payload['roomid']}, "
f"route_source={payload['route_source']}, sender={payload['sender']}, "
f"msg_type={payload['message_type']}, queue_size={outbound_queue.qsize()}, "
@@ -231,7 +235,7 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
asyncio.create_task(self._sender_loop(), name="maibot_adapter_sender_loop"),
]
self._runtime_started = True
self.LOG.info(
self._log_runtime(
f"[{self.name}] 后台运行时已启动: connection_uuid={self._connection_uuid}, "
f"queue_maxsize={self._queue_maxsize}, heartbeat_interval={self._heartbeat_interval}"
)
@@ -270,7 +274,7 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
}
ssl_option = None if self._verify_ssl else False
self.LOG.info(
self._log_runtime(
f"[{self.name}] 正在连接 MaiBot API Server: url={self._api_server_ws_url}, "
f"platform={self._platform_name}, connection_uuid={headers['x-uuid']}"
)
@@ -287,7 +291,7 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
if self._connected_event is not None:
self._connected_event.set()
self.LOG.info(
self._log_runtime(
f"[{self.name}] MaiBot API Server 连接成功: url={self._api_server_ws_url}, "
f"connection_uuid={headers['x-uuid']}"
)
@@ -303,7 +307,7 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
if message.type == aiohttp.WSMsgType.TEXT:
raw_text = str(message.data or "")
self.LOG.info(f"[{self.name}] 收到 MaiBot 原始消息: {raw_text[:500]}")
self._log_runtime(f"[{self.name}] 收到 MaiBot 原始消息: {raw_text[:500]}")
payload = self._parse_json_message(raw_text)
if payload is None:
continue
@@ -358,7 +362,7 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
}
await websocket.send_json(package)
self.LOG.info(
self._log_runtime(
f"[{self.name}] 已发送到 MaiBot: roomid={payload['roomid']}, sender={payload['sender']}, "
f"msg_type={payload['message_type']}, package_id={package['msg_id']}, "
f"content_preview={payload['normalized_content'][:120]}"
@@ -395,11 +399,11 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
if package_type == "sys_ack":
acked_msg_id = str(((package.get("meta") or {}).get("acked_msg_id")) or "")
self.LOG.info(f"[{self.name}] 收到 MaiBot ACK: package_id={package_id}, acked_msg_id={acked_msg_id}")
self._log_runtime(f"[{self.name}] 收到 MaiBot ACK: package_id={package_id}, acked_msg_id={acked_msg_id}")
return
if package_type != "sys_std":
self.LOG.info(f"[{self.name}] 忽略非 sys_std 消息: package_type={package_type}, package_id={package_id}")
self._log_runtime(f"[{self.name}] 忽略非 sys_std 消息: package_type={package_type}, package_id={package_id}")
return
api_message = package.get("payload") or {}
@@ -409,7 +413,7 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
reply_text = self._extract_segment_text(message_segment).strip()
if not reply_text:
self.LOG.info(
self._log_runtime(
f"[{self.name}] MaiBot 返回了空文本或非文本片段,忽略发送: package_id={package_id}, "
f"segment_type={message_segment.get('type')}"
)
@@ -423,7 +427,7 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
)
return
self.LOG.info(
self._log_runtime(
f"[{self.name}] 收到 MaiBot 回复: package_id={package_id}, route_type={route['route_type']}, "
f"target={route['target']}, at_target={route.get('at_target', '')}, "
f"platform={message_dim.get('platform')}, reply_preview={reply_text[:120]}"
@@ -434,7 +438,7 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
async def _emit_reply(self, route: Dict[str, str], reply_text: str, api_message: Dict[str, Any]) -> None:
"""按配置决定是否把 MaiBot 回复真正发回微信。"""
if not self._enable_reply_output:
self.LOG.info(f"[{self.name}] enable_reply_output=false仅采集不发回微信")
self._log_runtime(f"[{self.name}] enable_reply_output=false仅采集不发回微信")
return
bot = self._last_bot or getattr(self, "bot", None)
@@ -448,16 +452,16 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
if route_type == "group":
if not self._reply_group_messages:
self.LOG.info(f"[{self.name}] reply_group_messages=false群回复已跳过: target={target}")
self._log_runtime(f"[{self.name}] reply_group_messages=false群回复已跳过: target={target}")
return
if self._respect_group_feature_switch and not self._group_reply_allowed(target):
self.LOG.info(f"[{self.name}] 群功能开关未启用,仅采集不回群: target={target}")
self._log_runtime(f"[{self.name}] 群功能开关未启用,仅采集不回群: target={target}")
return
if self._mention_user_on_group_reply and at_target:
await bot.send_at_message(target, reply_text, [at_target])
else:
await bot.send_text_message(target, reply_text, at_target if at_target else "")
self.LOG.info(
self._log_runtime(
f"[{self.name}] 已发出 MaiBot 群回复: target={target}, at_target={at_target}, "
f"reply_len={len(reply_text)}"
)
@@ -465,10 +469,10 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
if route_type == "private":
if not self._reply_private_messages:
self.LOG.info(f"[{self.name}] reply_private_messages=false私聊回复已跳过: target={target}")
self._log_runtime(f"[{self.name}] reply_private_messages=false私聊回复已跳过: target={target}")
return
await bot.send_text_message(target, reply_text, "")
self.LOG.info(f"[{self.name}] 已发出 MaiBot 私聊回复: target={target}, reply_len={len(reply_text)}")
self._log_runtime(f"[{self.name}] 已发出 MaiBot 私聊回复: target={target}, reply_len={len(reply_text)}")
return
self.LOG.warning(f"[{self.name}] 未知路由类型,无法发送 MaiBot 回复: route={route}, api_message={api_message}")
@@ -506,7 +510,7 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
group_name = self._resolve_group_name(message, roomid)
# 路由诊断日志:这里把关键字段一次性打全,便于定位“群消息为何被识别成私聊”。
self.LOG.info(
self._log_runtime(
f"[{self.name}] 路由判定: route_type={route_type}, route_source={route_source}, "
f"plugin_roomid={str(message.get('roomid', '') or '').strip()}, "
f"wx_roomid={str(getattr(full_msg, 'roomid', '') or '').strip() if isinstance(full_msg, WxMessage) else ''}, "
@@ -813,6 +817,13 @@ class MaiBotAdapterPlugin(MessagePluginInterface):
return None
return payload
def _log_runtime(self, message: str) -> None:
"""根据插件配置动态输出运行日志级别。"""
if self._log_level == "DEBUG":
self.LOG.debug(message)
return
self.LOG.info(message)
def _is_supported_message(self, message: Dict[str, Any]) -> bool:
"""仅接收 MaiBot 当前最适合做上下文理解的消息类型。"""
full_msg = message.get("full_wx_msg")