feat(logger)

This commit is contained in:
SXP-Simon
2026-02-08 23:28:56 +08:00
parent b8ea7adf5a
commit e73fd99e26
31 changed files with 100 additions and 72 deletions
+9 -4
View File
@@ -17,11 +17,16 @@ sys.path.insert(0, project_root)
print(f"Project Root: {project_root}") print(f"Project Root: {project_root}")
# Mock logger before importing anything that uses it # Mock logger before importing anything that uses it
from astrbot.api import logger # noqa: E402 # Mock logger
from data.plugins.astrbot_plugin_qq_group_daily_analysis.src.utils import (
logger as plugin_logger_module,
)
logger.info = lambda msg, *args, **kwargs: print(f"[INFO] {msg}") plugin_logger_module.logger.info = lambda msg, *args, **kwargs: print(f"[INFO] {msg}")
logger.error = lambda msg, *args, **kwargs: print(f"[ERROR] {msg}") plugin_logger_module.logger.error = lambda msg, *args, **kwargs: print(f"[ERROR] {msg}")
logger.warning = lambda msg, *args, **kwargs: print(f"[WARN] {msg}") plugin_logger_module.logger.warning = lambda msg, *args, **kwargs: print(
f"[WARN] {msg}"
)
# Now import plugin modules # Now import plugin modules
try: try:
+1 -1
View File
@@ -6,7 +6,7 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Any from typing import Any
from astrbot.api import logger from ...utils.logger import logger
from ...models.data_models import TokenUsage from ...models.data_models import TokenUsage
from ..utils.json_utils import parse_json_response from ..utils.json_utils import parse_json_response
@@ -5,7 +5,7 @@
from datetime import datetime from datetime import datetime
from astrbot.api import logger from ...utils.logger import logger
from ...models.data_models import GoldenQuote, TokenUsage from ...models.data_models import GoldenQuote, TokenUsage
from ..utils import InfoUtils from ..utils import InfoUtils
+1 -1
View File
@@ -6,7 +6,7 @@
import re import re
from datetime import datetime from datetime import datetime
from astrbot.api import logger from ...utils.logger import logger
from ...models.data_models import SummaryTopic, TokenUsage from ...models.data_models import SummaryTopic, TokenUsage
from ..utils import InfoUtils from ..utils import InfoUtils
@@ -3,7 +3,7 @@
专门处理用户称号和MBTI类型分析 专门处理用户称号和MBTI类型分析
""" """
from astrbot.api import logger from ...utils.logger import logger
from ...models.data_models import TokenUsage, UserTitle from ...models.data_models import TokenUsage, UserTitle
from ..utils.json_utils import extract_user_titles_with_regex from ..utils.json_utils import extract_user_titles_with_regex
+1 -1
View File
@@ -5,7 +5,7 @@ LLM分析器模块
import asyncio import asyncio
from astrbot.api import logger from ..utils.logger import logger
from ..models.data_models import GoldenQuote, SummaryTopic, TokenUsage, UserTitle from ..models.data_models import GoldenQuote, SummaryTopic, TokenUsage, UserTitle
from .analyzers.golden_quote_analyzer import GoldenQuoteAnalyzer from .analyzers.golden_quote_analyzer import GoldenQuoteAnalyzer
+1 -1
View File
@@ -6,7 +6,7 @@ JSON处理工具模块
import json import json
import re import re
from astrbot.api import logger from ...utils.logger import logger
def fix_json(text: str) -> str: def fix_json(text: str) -> str:
+1 -1
View File
@@ -6,7 +6,7 @@ LLM API请求处理工具模块
import asyncio import asyncio
from typing import Any from typing import Any
from astrbot.api import logger from ...utils.logger import logger
from ...utils.resilience import CircuitBreaker, global_llm_rate_limiter from ...utils.resilience import CircuitBreaker, global_llm_rate_limiter
+25 -27
View File
@@ -12,7 +12,7 @@
from typing import Optional, List, Dict, Any from typing import Optional, List, Dict, Any
from dataclasses import dataclass from dataclasses import dataclass
from astrbot.api import logger from ..utils.logger import logger
from ..domain.value_objects.unified_message import UnifiedMessage from ..domain.value_objects.unified_message import UnifiedMessage
from ..domain.value_objects.platform_capabilities import PlatformCapabilities from ..domain.value_objects.platform_capabilities import PlatformCapabilities
@@ -23,6 +23,7 @@ from .message_converter import MessageConverter
@dataclass @dataclass
class AnalysisConfig: class AnalysisConfig:
"""分析操作配置""" """分析操作配置"""
days: int = 1 days: int = 1
max_messages: int = 1000 max_messages: int = 1000
min_messages_threshold: int = 10 min_messages_threshold: int = 10
@@ -32,13 +33,13 @@ class AnalysisConfig:
class AnalysisOrchestrator: class AnalysisOrchestrator:
""" """
分析编排器 - 协调分析工作流。 分析编排器 - 协调分析工作流。
职责: 职责:
1. 使用 PlatformAdapter 获取消息(DDD 方式) 1. 使用 PlatformAdapter 获取消息(DDD 方式)
2. 转换消息以兼容现有分析器 2. 转换消息以兼容现有分析器
3. 协调分析流程 3. 协调分析流程
4. 提供平台能力检查 4. 提供平台能力检查
此类作为以下组件之间的桥梁: 此类作为以下组件之间的桥梁:
- 新的 DDD 基础设施(PlatformAdapter, UnifiedMessage - 新的 DDD 基础设施(PlatformAdapter, UnifiedMessage
- 现有分析逻辑(MessageHandler, LLMAnalyzer 等) - 现有分析逻辑(MessageHandler, LLMAnalyzer 等)
@@ -51,7 +52,7 @@ class AnalysisOrchestrator:
): ):
""" """
初始化编排器。 初始化编排器。
参数: 参数:
adapter: 用于消息操作的平台适配器 adapter: 用于消息操作的平台适配器
config: 分析配置 config: 分析配置
@@ -69,13 +70,13 @@ class AnalysisOrchestrator:
) -> Optional["AnalysisOrchestrator"]: ) -> Optional["AnalysisOrchestrator"]:
""" """
工厂方法 - 为特定平台创建编排器。 工厂方法 - 为特定平台创建编排器。
参数: 参数:
platform_name: 平台名称(如 "aiocqhttp", "telegram" platform_name: 平台名称(如 "aiocqhttp", "telegram"
bot_instance: 来自 AstrBot 的 bot 实例 bot_instance: 来自 AstrBot 的 bot 实例
config: 平台特定配置 config: 平台特定配置
analysis_config: 分析配置 analysis_config: 分析配置
返回: 返回:
AnalysisOrchestrator 或 None(如果平台不支持) AnalysisOrchestrator 或 None(如果平台不支持)
""" """
@@ -83,7 +84,7 @@ class AnalysisOrchestrator:
if adapter is None: if adapter is None:
logger.warning(f"平台 '{platform_name}' 不支持分析功能") logger.warning(f"平台 '{platform_name}' 不支持分析功能")
return None return None
return cls(adapter, analysis_config) return cls(adapter, analysis_config)
def get_capabilities(self) -> PlatformCapabilities: def get_capabilities(self) -> PlatformCapabilities:
@@ -106,29 +107,26 @@ class AnalysisOrchestrator:
) -> List[UnifiedMessage]: ) -> List[UnifiedMessage]:
""" """
使用平台适配器获取消息。 使用平台适配器获取消息。
参数: 参数:
group_id: 要获取消息的群组 ID group_id: 要获取消息的群组 ID
days: 天数(默认使用配置值) days: 天数(默认使用配置值)
max_count: 最大消息数量(默认使用配置值) max_count: 最大消息数量(默认使用配置值)
返回: 返回:
UnifiedMessage 列表 UnifiedMessage 列表
""" """
days = days or self.config.days days = days or self.config.days
max_count = max_count or self.config.max_messages max_count = max_count or self.config.max_messages
# 应用平台能力限制 # 应用平台能力限制
caps = self.adapter.get_capabilities() caps = self.adapter.get_capabilities()
effective_days = caps.get_effective_days(days) effective_days = caps.get_effective_days(days)
effective_count = caps.get_effective_count(max_count) effective_count = caps.get_effective_count(max_count)
if effective_days < days: if effective_days < days:
logger.info( logger.info(f"平台限制:请求 {days} 天,实际使用 {effective_days}")
f"平台限制:请求 {days} 天,"
f"实际使用 {effective_days}"
)
return await self.adapter.fetch_messages( return await self.adapter.fetch_messages(
group_id=group_id, group_id=group_id,
days=effective_days, days=effective_days,
@@ -143,15 +141,15 @@ class AnalysisOrchestrator:
) -> List[dict]: ) -> List[dict]:
""" """
获取消息并转换为原始字典格式。 获取消息并转换为原始字典格式。
此方法提供与现有分析器的向后兼容性, 此方法提供与现有分析器的向后兼容性,
这些分析器期望原始字典格式的消息。 这些分析器期望原始字典格式的消息。
参数: 参数:
group_id: 要获取消息的群组 ID group_id: 要获取消息的群组 ID
days: 天数 days: 天数
max_count: 最大消息数量 max_count: 最大消息数量
返回: 返回:
原始消息字典列表(通用格式,由适配器决定具体格式) 原始消息字典列表(通用格式,由适配器决定具体格式)
""" """
@@ -178,11 +176,11 @@ class AnalysisOrchestrator:
# } # }
# for msg in unified_messages # for msg in unified_messages
# ] # ]
# 暂时直接使用适配器获取 raw 格式,如果适配器支持 # 暂时直接使用适配器获取 raw 格式,如果适配器支持
# 这是为了确保现有逻辑完全兼容,因为 convert_to_raw_format 可能有损 # 这是为了确保现有逻辑完全兼容,因为 convert_to_raw_format 可能有损
# 但我们希望尽可能使用新的 fetch_messages # 但我们希望尽可能使用新的 fetch_messages
unified_messages = await self.fetch_messages(group_id, days, max_count) unified_messages = await self.fetch_messages(group_id, days, max_count)
return self.adapter.convert_to_raw_format(unified_messages) return self.adapter.convert_to_raw_format(unified_messages)
@@ -197,11 +195,11 @@ class AnalysisOrchestrator:
) -> Dict[str, Optional[str]]: ) -> Dict[str, Optional[str]]:
""" """
批量获取用户头像 URL。 批量获取用户头像 URL。
参数: 参数:
user_ids: 用户 ID 列表 user_ids: 用户 ID 列表
size: 头像尺寸 size: 头像尺寸
返回: 返回:
用户 ID 到头像 URL 的映射字典(URL 可能为 None) 用户 ID 到头像 URL 的映射字典(URL 可能为 None)
""" """
@@ -232,10 +230,10 @@ class AnalysisOrchestrator:
def validate_message_count(self, messages: List[UnifiedMessage]) -> bool: def validate_message_count(self, messages: List[UnifiedMessage]) -> bool:
""" """
检查消息数量是否达到最小阈值。 检查消息数量是否达到最小阈值。
参数: 参数:
messages: 消息列表 messages: 消息列表
返回: 返回:
如果数量足够返回 True 如果数量足够返回 True
""" """
@@ -244,10 +242,10 @@ class AnalysisOrchestrator:
def get_analysis_text(self, messages: List[UnifiedMessage]) -> str: def get_analysis_text(self, messages: List[UnifiedMessage]) -> str:
""" """
将消息转换为 LLM 分析文本格式。 将消息转换为 LLM 分析文本格式。
参数: 参数:
messages: UnifiedMessage 列表 messages: UnifiedMessage 列表
返回: 返回:
格式化的 LLM 分析文本 格式化的 LLM 分析文本
""" """
+4 -2
View File
@@ -7,7 +7,7 @@
from datetime import datetime from datetime import datetime
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
from astrbot.api import logger from ..utils.logger import logger
from ..domain.services import ReportGenerator from ..domain.services import ReportGenerator
from ..domain.value_objects.topic import Topic from ..domain.value_objects.topic import Topic
@@ -78,7 +78,9 @@ class ReportingService:
statistics=statistics, statistics=statistics,
topics=topics if self.config.get_include_topics() else [], topics=topics if self.config.get_include_topics() else [],
user_titles=user_titles if self.config.get_include_user_titles() else [], user_titles=user_titles if self.config.get_include_user_titles() else [],
golden_quotes=golden_quotes if self.config.get_include_golden_quotes() else [], golden_quotes=golden_quotes
if self.config.get_include_golden_quotes()
else [],
include_header=True, include_header=True,
include_footer=True, include_footer=True,
) )
+6 -2
View File
@@ -9,10 +9,14 @@ import asyncio
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Any, Callable, Dict, List, Optional, Set from typing import Any, Callable, Dict, List, Optional, Set
from astrbot.api import logger from ..utils.logger import logger
from ..infrastructure.config import ConfigManager from ..infrastructure.config import ConfigManager
from ..shared.constants import TASK_STATE_PENDING, TASK_STATE_RUNNING, TASK_STATE_COMPLETED from ..shared.constants import (
TASK_STATE_PENDING,
TASK_STATE_RUNNING,
TASK_STATE_COMPLETED,
)
class ScheduledTask: class ScheduledTask:
+1 -1
View File
@@ -7,7 +7,7 @@ Bot实例管理模块
from typing import Any, Optional from typing import Any, Optional
from astrbot.api import logger from ..utils.logger import logger
from ..infrastructure.platform import PlatformAdapter, PlatformAdapterFactory from ..infrastructure.platform import PlatformAdapter, PlatformAdapterFactory
+1 -1
View File
@@ -7,7 +7,7 @@
import datetime import datetime
from typing import Any from typing import Any
from astrbot.api import logger from ..utils.logger import logger
class HistoryManager: class HistoryManager:
+1 -1
View File
@@ -6,7 +6,7 @@
from collections import defaultdict from collections import defaultdict
from datetime import datetime, timedelta from datetime import datetime, timedelta
from astrbot.api import logger from ..utils.logger import logger
from ..models.data_models import EmojiStatistics, GroupStatistics, TokenUsage from ..models.data_models import EmojiStatistics, GroupStatistics, TokenUsage
from ..visualization.activity_charts import ActivityVisualizer from ..visualization.activity_charts import ActivityVisualizer
+1 -1
View File
@@ -2,7 +2,7 @@ import base64
import aiohttp import aiohttp
from astrbot.api import logger from ..utils.logger import logger
from ..utils.trace_context import TraceContext from ..utils.trace_context import TraceContext
+1 -1
View File
@@ -7,7 +7,7 @@
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
from astrbot.api import logger from ...utils.logger import logger
class ConfigManager: class ConfigManager:
+4 -2
View File
@@ -7,7 +7,7 @@ LLM 客户端 - 包装 AstrBot 的 LLM 提供商系统
from typing import Any, Dict, List, Optional, Tuple from typing import Any, Dict, List, Optional, Tuple
from astrbot.api import logger from ...utils.logger import logger
from ...domain.value_objects.statistics import TokenUsage from ...domain.value_objects.statistics import TokenUsage
from ...domain.exceptions import LLMException, LLMRateLimitException from ...domain.exceptions import LLMException, LLMRateLimitException
@@ -110,7 +110,9 @@ class LLMClient:
if hasattr(response, "completion_text"): if hasattr(response, "completion_text"):
response_text = response.completion_text response_text = response.completion_text
elif isinstance(response, dict): elif isinstance(response, dict):
response_text = response.get("completion_text", response.get("text", "")) response_text = response.get(
"completion_text", response.get("text", "")
)
else: else:
response_text = str(response) response_text = str(response)
@@ -11,7 +11,7 @@ from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
from astrbot.api import logger from ...utils.logger import logger
class HistoryRepository: class HistoryRepository:
@@ -121,9 +121,7 @@ class HistoryRepository:
history = self.load_group_history(group_id) history = self.load_group_history(group_id)
return history.get("daily", {}).get(date_str) return history.get("daily", {}).get(date_str)
def get_recent_results( def get_recent_results(self, group_id: str, limit: int = 7) -> List[Dict[str, Any]]:
self, group_id: str, limit: int = 7
) -> List[Dict[str, Any]]:
""" """
获取最近的分析结果。 获取最近的分析结果。
+11 -8
View File
@@ -2,7 +2,9 @@
平台适配器工厂 平台适配器工厂
""" """
from typing import Optional, Any, Dict, Type from typing import Any, Dict, List, Optional, Type
from ...utils.logger import logger
from .base import PlatformAdapter from .base import PlatformAdapter
@@ -10,7 +12,7 @@ from .base import PlatformAdapter
class PlatformAdapterFactory: class PlatformAdapterFactory:
""" """
平台适配器工厂 平台适配器工厂
根据平台名称创建适配器实例。 根据平台名称创建适配器实例。
使用注册表模式便于扩展。 使用注册表模式便于扩展。
""" """
@@ -31,12 +33,12 @@ class PlatformAdapterFactory:
) -> Optional[PlatformAdapter]: ) -> Optional[PlatformAdapter]:
""" """
创建平台适配器 创建平台适配器
参数: 参数:
platform_name: 平台名称(如 "aiocqhttp""telegram" platform_name: 平台名称(如 "aiocqhttp""telegram"
bot_instance: AstrBot 机器人实例 bot_instance: AstrBot 机器人实例
config: 配置字典 config: 配置字典
返回: 返回:
平台适配器实例,如果不支持则返回 None 平台适配器实例,如果不支持则返回 None
""" """
@@ -49,8 +51,7 @@ class PlatformAdapterFactory:
return adapter_class(bot_instance, config) return adapter_class(bot_instance, config)
except Exception: except Exception:
# 记录异常,但不崩溃 # 记录异常,但不崩溃
import logging logger.error(f"{platform_name} 创建适配器时出错", exc_info=True)
logging.getLogger(__name__).error(f"{platform_name} 创建适配器时出错", exc_info=True)
return None return None
@classmethod @classmethod
@@ -68,15 +69,17 @@ class PlatformAdapterFactory:
def _register_adapters(): def _register_adapters():
try: try:
from .adapters.onebot_adapter import OneBotAdapter from .adapters.onebot_adapter import OneBotAdapter
PlatformAdapterFactory.register("aiocqhttp", OneBotAdapter) PlatformAdapterFactory.register("aiocqhttp", OneBotAdapter)
PlatformAdapterFactory.register("onebot", OneBotAdapter) PlatformAdapterFactory.register("onebot", OneBotAdapter)
except ImportError: except ImportError:
pass pass
try: try:
from .adapters.discord_adapter import DiscordAdapter from .adapters.discord_adapter import DiscordAdapter
PlatformAdapterFactory.register("discord", DiscordAdapter) PlatformAdapterFactory.register("discord", DiscordAdapter)
PlatformAdapterFactory.register("discord_bot", DiscordAdapter) # 添加别名 PlatformAdapterFactory.register("discord_bot", DiscordAdapter) # 添加别名
except ImportError: except ImportError:
pass pass
@@ -9,7 +9,7 @@ from dataclasses import dataclass, field
from enum import Enum from enum import Enum
from typing import Callable, Optional from typing import Callable, Optional
from astrbot.api import logger from ...utils.logger import logger
class CircuitState(Enum): class CircuitState(Enum):
@@ -9,7 +9,7 @@ import time
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Optional from typing import Optional
from astrbot.api import logger from ...utils.logger import logger
@dataclass @dataclass
+1 -1
View File
@@ -10,7 +10,7 @@ from dataclasses import dataclass
from functools import wraps from functools import wraps
from typing import Callable, Optional, Tuple, Type, Union from typing import Callable, Optional, Tuple, Type, Union
from astrbot.api import logger from ...utils.logger import logger
@dataclass @dataclass
+1 -1
View File
@@ -1,7 +1,7 @@
from collections.abc import Callable from collections.abc import Callable
from typing import Any from typing import Any
from astrbot.api import logger from ..utils.logger import logger
from ..utils.trace_context import TraceContext from ..utils.trace_context import TraceContext
+1 -1
View File
@@ -10,7 +10,7 @@ from pathlib import Path
import aiohttp import aiohttp
from astrbot.api import logger from ..utils.logger import logger
from ..visualization.activity_charts import ActivityVisualizer from ..visualization.activity_charts import ActivityVisualizer
from .templates import HTMLTemplates from .templates import HTMLTemplates
+1 -1
View File
@@ -9,7 +9,7 @@ import threading
from jinja2 import Environment, FileSystemLoader, select_autoescape from jinja2 import Environment, FileSystemLoader, select_autoescape
from astrbot.api import logger from ..utils.logger import logger
class HTMLTemplates: class HTMLTemplates:
+1 -1
View File
@@ -8,7 +8,7 @@ import weakref
from apscheduler.triggers.cron import CronTrigger from apscheduler.triggers.cron import CronTrigger
from astrbot.api import logger from ..utils.logger import logger
from ..core.message_sender import MessageSender from ..core.message_sender import MessageSender
from ..reports.dispatcher import ReportDispatcher from ..reports.dispatcher import ReportDispatcher
+1 -1
View File
@@ -6,7 +6,7 @@ import aiohttp
from collections.abc import Callable from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from astrbot.api import logger from ..utils.logger import logger
@dataclass @dataclass
+1 -1
View File
@@ -5,7 +5,7 @@
import asyncio import asyncio
from astrbot.api import logger from .logger import logger
from ..analysis.llm_analyzer import LLMAnalyzer from ..analysis.llm_analyzer import LLMAnalyzer
from ..analysis.statistics import UserAnalyzer from ..analysis.statistics import UserAnalyzer
+16
View File
@@ -0,0 +1,16 @@
import logging
from astrbot.api import logger as astrbot_logger
class PluginLoggerAdapter(logging.LoggerAdapter):
"""
插件日志适配器
自动为日志添加 [QQ群分析] 前缀,方便区分
"""
def process(self, msg, kwargs):
return f"[QQ群分析] {msg}", kwargs
# 导出带前缀的 logger
logger = PluginLoggerAdapter(astrbot_logger, {})
+1 -1
View File
@@ -7,7 +7,7 @@ import asyncio
import sys import sys
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from astrbot.api import logger from .logger import logger
class PDFInstaller: class PDFInstaller:
+1 -1
View File
@@ -1,7 +1,7 @@
import asyncio import asyncio
import time import time
from astrbot.api import logger from .logger import logger
class CircuitBreaker: class CircuitBreaker: