剥离无效事件系统并收口插件统计链路
- 删除未被实际消费的事件系统实现与相关发布逻辑 - 将插件调用统计改为在机器人主链路中直接埋点记录 - 重构统计收集插件初始化与记录方式,移除事件总线依赖 - 同步更新工程优化文档中的性能与链路治理描述
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user