剥离无效事件系统并收口插件统计链路

- 删除未被实际消费的事件系统实现与相关发布逻辑
- 将插件调用统计改为在机器人主链路中直接埋点记录
- 重构统计收集插件初始化与记录方式,移除事件总线依赖
- 同步更新工程优化文档中的性能与链路治理描述
This commit is contained in:
liuwei
2026-04-30 14:54:22 +08:00
parent 78e4f50b7e
commit 0878f0d4ea
7 changed files with 174 additions and 284 deletions

View File

@@ -2,10 +2,6 @@ from loguru import logger
from typing import Dict, Any, Tuple, Optional, List
from base.plugin_common.plugin_interface import PluginInterface, PluginStatus
from base.event_system.event_manager import EventManager
# 使用正确的事件类型导入
from base.event_system.events.plugin_events import PluginCallStartEvent, PluginCallEndEvent, PluginCallErrorEvent
# 数据库导入
from db.stats_db import StatsDBOperator
from db.connection import DBConnectionManager
@@ -41,81 +37,95 @@ class StatsCollectorPlugin(PluginInterface):
super().__init__()
self.LOG = logger
self.LOG.debug(f"正在初始化 {self.name} 插件...")
# 默认配置
# 默认配置
# 1. 这个插件现在不再依赖事件总线,而是由主消息分发链路直接回调;
# 2. 因此这里保留一份轻量配置,只控制“是否记录”和“排除哪些插件”;
# 3. 这样既能延续原有统计面板数据结构,也能避免事件系统带来的额外复杂度。
self.config = {
"enable": True,
"record_all_plugins": True, # 是否记录所有插件的调用
"excluded_plugins": [], # 排除的插件列表
}
self.event_manager = EventManager.get_instance()
self.db_manager = DBConnectionManager.get_instance()
self.stats_db = StatsDBOperator(self.db_manager)
# 注册功能权限
def initialize(self, config: Dict[str, Any]) -> bool:
def initialize(self, context: Dict[str, Any]) -> bool:
"""初始化插件"""
if config:
self.config.update(config)
# 这里显式只读取插件自己的配置,不再把 system_context 整体 merge 进来:
# 1. 旧实现把 initialize 入参当成“插件配置”使用,但实际传入的是 system_context
# 2. 结果会把 db_manager、redis_pool 等运行时对象误写到 self.config 中;
# 3. 改成只消费 load_config 后的 self._config避免配置结构持续污染。
if isinstance(self._config, dict):
self.config.update(self._config)
# 若主系统已经初始化了 DB 管理器,则优先复用统一实例,避免插件侧再走兜底单例。
if isinstance(context, dict) and context.get("db_manager") is not None:
self.db_manager = context.get("db_manager")
self.stats_db = StatsDBOperator(self.db_manager)
if not self.config["enable"]:
self.LOG.info("统计收集插件已禁用")
return False
# 注册事件处理器
self.event_manager.register(PluginCallStartEvent, self.handle_plugin_call_start)
self.event_manager.register(PluginCallEndEvent, self.handle_plugin_call_end)
self.event_manager.register(PluginCallErrorEvent, self.handle_plugin_error)
return True
def handle_plugin_call_start(self, event: PluginCallStartEvent) -> None:
"""处理插件调用开始事件"""
# 检查是否需要记录该插件
if not self._should_record_plugin(event.plugin_name):
def record_plugin_call(
self,
*,
plugin_name: str,
command: str,
user_id: str,
group_id: Optional[str],
is_group: bool,
process_result: bool,
process_time_ms: float,
) -> None:
"""由主链路直接调用,记录一次插件执行结果。"""
# 主链路可能会在高频场景下频繁回调这里,因此先做最便宜的开关判断,
# 避免对已关闭或被排除插件继续产生数据库写入成本。
if not self._should_record_plugin(plugin_name):
return
# 记录开始时间和相关信息
self.LOG.debug(f"记录插件调用开始: {event.plugin_name} - {event.command}")
def handle_plugin_call_end(self, event: PluginCallEndEvent) -> None:
"""处理插件调用结束事件"""
# 检查是否需要记录该插件
if not self._should_record_plugin(event.plugin_name):
return
# 记录统计数据
try:
# 确保使用正确的属性名
self.stats_db.record_plugin_call(
plugin_name=event.plugin_name,
command=event.command,
user_id=event.user_id,
group_id=event.group_id,
success=event.process_result,
process_time_ms=event.process_time
plugin_name=plugin_name,
command=command,
user_id=user_id,
group_id=group_id if is_group else None,
success=process_result,
process_time_ms=process_time_ms,
)
self.LOG.debug(
f"记录插件调用结束: {event.plugin_name} - {event.command} - 成功: {event.process_result} - 处理时间: {event.process_time}ms")
f"记录插件调用结束: {plugin_name} - {command} - 成功: {process_result} - 处理时间: {process_time_ms}ms"
)
except Exception as e:
self.LOG.error(f"记录插件调用统计数据出错: {e}")
def handle_plugin_error(self, event: PluginCallErrorEvent) -> None:
"""处理插件调用错误事件"""
# 检查是否需要记录该插件
if not self._should_record_plugin(event.plugin_name):
def record_plugin_error(
self,
*,
plugin_name: str,
command: str,
user_id: str,
group_id: Optional[str],
is_group: bool,
error_message: str,
stack_trace: Optional[str] = None,
) -> None:
"""由主链路直接调用,记录一次插件执行异常。"""
if not self._should_record_plugin(plugin_name):
return
# 记录错误信息
try:
self.stats_db.record_error(
plugin_name=event.plugin_name,
command=event.command,
user_id=event.user_id,
group_id=event.group_id,
error_message=event.error_message,
stack_trace=event.stack_trace
plugin_name=plugin_name,
command=command,
user_id=user_id,
group_id=group_id if is_group else None,
error_message=error_message,
stack_trace=stack_trace,
)
self.LOG.debug(f"记录插件调用错误: {event.plugin_name} - {event.command} - {event.error_message}")
self.LOG.debug(f"记录插件调用错误: {plugin_name} - {command} - {error_message}")
except Exception as e:
self.LOG.error(f"记录插件错误信息出错: {e}")
@@ -145,11 +155,6 @@ class StatsCollectorPlugin(PluginInterface):
def shutdown(self) -> None:
"""关闭插件"""
# 取消注册事件处理器
self.event_manager.unregister(PluginCallStartEvent, self.handle_plugin_call_start)
self.event_manager.unregister(PluginCallEndEvent, self.handle_plugin_call_end)
self.event_manager.unregister(PluginCallErrorEvent, self.handle_plugin_error)
self.LOG.info("统计收集插件已关闭")
def start(self) -> bool: