refactor(logger): 将控制台渲染器迁移至 Rich 库,支持十六进制颜色

用 Rich 的原生十六进制颜色支持替代了手动 ANSI 颜色处理
将所有模块颜色定义从 ANSI 转义码转换为 #RRGGBB 格式
移除了 _normalize_color() 函数及相关 ANSI 转换逻辑
更新 ModuleColoredConsoleRenderer,使用 Rich Text 对象进行渲染
通过引入 structlog.typing 增强类型提示
保持日志元数据注册的向后兼容性
破坏性变更:日志元数据现在存储原始十六进制颜色,而非 ANSI 代码
This commit is contained in:
明天好像没什么
2025-11-01 22:34:43 +08:00
committed by Windpicker-owo
parent c66bd86095
commit e34c47257c

View File

@@ -7,11 +7,13 @@ import time
from collections.abc import Callable
from datetime import datetime, timedelta
from pathlib import Path
from typing import Any
import orjson
import structlog
import tomlkit
from rich.console import Console
from rich.text import Text
from structlog.typing import EventDict, WrappedLogger
# 创建logs目录
LOG_DIR = Path("logs")
@@ -26,37 +28,11 @@ _LOGGER_META_LOCK = threading.Lock()
_LOGGER_META: dict[str, dict[str, str | None]] = {}
def _normalize_color(color: str | None) -> str | None:
"""接受 ANSI 码 / #RRGGBB / rgb(r,g,b) / 颜色名(直接返回) -> ANSI 码.
不做复杂解析,只支持 #RRGGBB 转 24bit ANSI。
"""
if not color:
return None
color = color.strip()
if color.startswith("\033["):
return color # 已经是ANSI
if color.startswith("#") and len(color) == 7:
try:
r = int(color[1:3], 16)
g = int(color[3:5], 16)
b = int(color[5:7], 16)
return f"\033[38;2;{r};{g};{b}m"
except ValueError:
return None
# 简单 rgb(r,g,b)
if color.lower().startswith("rgb(") and color.endswith(")"):
try:
nums = color[color.find("(") + 1 : -1].split(",")
r, g, b = (int(x) for x in nums[:3])
return f"\033[38;2;{r};{g};{b}m"
except Exception:
return None
# 其他情况直接返回假设是短ANSI或名称控制台渲染器不做翻译仅输出
return color
def _register_logger_meta(name: str, *, alias: str | None = None, color: str | None = None):
"""注册/更新 logger 元数据。"""
"""注册/更新 logger 元数据。
color 参数直接存储 #RRGGBB 格式的颜色值。
"""
if not name:
return
with _LOGGER_META_LOCK:
@@ -64,7 +40,8 @@ def _register_logger_meta(name: str, *, alias: str | None = None, color: str | N
if alias is not None:
meta["alias"] = alias
if color is not None:
meta["color"] = _normalize_color(color)
# 直接存储颜色值(假设已经是 #RRGGBB 格式)
meta["color"] = color.upper() if color.startswith("#") else color
def get_logger_meta(name: str) -> dict[str, str | None]:
@@ -426,200 +403,200 @@ def reconfigure_existing_loggers():
DEFAULT_MODULE_COLORS = {
# 核心模块
"main": "\033[1;97m", # 亮白色+粗体 (主程序)
"api": "\033[92m", # 亮绿色
"emoji": "\033[38;5;214m", # 橙黄色偏向橙色但与replyer和action_manager不同
"chat": "\033[92m", # 亮蓝色
"config": "\033[93m", # 亮黄色
"common": "\033[95m", # 亮紫色
"tools": "\033[96m", # 亮青色
"lpmm": "\033[96m",
"plugin_system": "\033[91m", # 亮红色
"person_info": "\033[32m", # 绿色
"individuality": "\033[94m", # 显眼的亮蓝色
"manager": "\033[35m", # 紫色
"llm_models": "\033[36m", # 青色
"remote": "\033[38;5;242m", # 深灰色,更不显眼
"planner": "\033[36m",
"memory": "\033[38;5;117m", # 天蓝色
"hfc": "\033[38;5;81m", # 稍微暗一些的青色,保持可读
"action_manager": "\033[38;5;208m", # 橙色不与replyer重复
"message_manager": "\033[38;5;27m", # 深蓝色,消息管理器
"chatter_manager": "\033[38;5;129m", # 紫色,聊天管理器
"chatter_interest_scoring": "\033[38;5;214m", # 橙黄色,兴趣评分
"plan_executor": "\033[38;5;172m", # 橙褐色,计划执行器
"main": "#FFFFFF", # 亮白色+粗体 (主程序)
"api": "#00FF00", # 亮绿色
"emoji": "#FFAF00", # 橙黄色偏向橙色但与replyer和action_manager不同
"chat": "#00FF00", # 亮蓝色
"config": "#FFFF00", # 亮黄色
"common": "#FF00FF", # 亮紫色
"tools": "#00FFFF", # 亮青色
"lpmm": "#00FFFF",
"plugin_system": "#FF0000", # 亮红色
"person_info": "#008000", # 绿色
"individuality": "#0000FF", # 显眼的亮蓝色
"manager": "#800080", # 紫色
"llm_models": "#008080", # 青色
"remote": "#6C6C6C", # 深灰色,更不显眼
"planner": "#008080",
"memory": "#87D7FF", # 天蓝色
"hfc": "#5FD7FF", # 稍微暗一些的青色,保持可读
"action_manager": "#FF8700", # 橙色不与replyer重复
"message_manager": "#005FFF", # 深蓝色,消息管理器
"chatter_manager": "#AF00FF", # 紫色,聊天管理器
"chatter_interest_scoring": "#FFAF00", # 橙黄色,兴趣评分
"plan_executor": "#D78700", # 橙褐色,计划执行器
# 关系系统
"relation": "\033[38;5;139m", # 柔和的紫色,不刺眼
"relation": "#AF87AF", # 柔和的紫色,不刺眼
# 聊天相关模块
"normal_chat": "\033[38;5;81m", # 亮蓝绿色
"heartflow": "\033[38;5;175m", # 柔和的粉色,不显眼但保持粉色系
"sub_heartflow": "\033[38;5;207m", # 粉紫色
"subheartflow_manager": "\033[38;5;201m", # 深粉色
"background_tasks": "\033[38;5;240m", # 灰色
"chat_message": "\033[38;5;45m", # 青色
"chat_stream": "\033[38;5;51m", # 亮青色
"sender": "\033[38;5;67m", # 稍微暗一些的蓝色,不显眼
"message_storage": "\033[38;5;33m", # 深蓝色
"expressor": "\033[38;5;166m", # 橙色
"normal_chat": "#5FD7FF", # 亮蓝绿色
"heartflow": "#D787AF", # 柔和的粉色,不显眼但保持粉色系
"sub_heartflow": "#FF5FFF", # 粉紫色
"subheartflow_manager": "#FF00FF", # 深粉色
"background_tasks": "#585858", # 灰色
"chat_message": "#00D7FF", # 青色
"chat_stream": "#00FFFF", # 亮青色
"sender": "#5F87AF", # 稍微暗一些的蓝色,不显眼
"message_storage": "#0087FF", # 深蓝色
"expressor": "#D75F00", # 橙色
# 专注聊天模块
"replyer": "\033[38;5;166m", # 橙色
"memory_activator": "\033[38;5;117m", # 天蓝色
"replyer": "#D75F00", # 橙色
"memory_activator": "#87D7FF", # 天蓝色
# 插件系统
"plugins": "\033[31m", # 红色
"plugin_api": "\033[33m", # 黄色
"plugin_manager": "\033[38;5;208m", # 红色
"base_plugin": "\033[38;5;202m", # 橙红色
"send_api": "\033[38;5;208m", # 橙色
"base_command": "\033[38;5;208m", # 橙色
"component_registry": "\033[38;5;214m", # 橙黄色
"stream_api": "\033[38;5;220m", # 黄色
"plugin_hot_reload": "\033[38;5;226m", # 品红色
"config_api": "\033[38;5;226m", # 亮黄色
"heartflow_api": "\033[38;5;154m", # 黄绿色
"action_apis": "\033[38;5;118m", # 绿色
"independent_apis": "\033[38;5;82m", # 绿色
"llm_api": "\033[38;5;46m", # 亮绿色
"database_api": "\033[38;5;10m", # 绿色
"utils_api": "\033[38;5;14m", # 青色
"message_api": "\033[38;5;6m", # 青色
"plugins": "#800000", # 红色
"plugin_api": "#808000", # 黄色
"plugin_manager": "#FF8700", # 红色
"base_plugin": "#FF5F00", # 橙红色
"send_api": "#FF8700", # 橙色
"base_command": "#FF8700", # 橙色
"component_registry": "#FFAF00", # 橙黄色
"stream_api": "#FFD700", # 黄色
"plugin_hot_reload": "#FFFF00", # 品红色
"config_api": "#FFFF00", # 亮黄色
"heartflow_api": "#AFFF00", # 黄绿色
"action_apis": "#87FF00", # 绿色
"independent_apis": "#5FFF00", # 绿色
"llm_api": "#00FF00", # 亮绿色
"database_api": "#00FF00", # 绿色
"utils_api": "#00FFFF", # 青色
"message_api": "#008080", # 青色
# 管理器模块
"async_task_manager": "\033[38;5;129m", # 紫色
"mood": "\033[38;5;135m", # 紫红色
"local_storage": "\033[38;5;141m", # 紫色
"willing": "\033[38;5;147m", # 浅紫色
"async_task_manager": "#AF00FF", # 紫色
"mood": "#AF5FFF", # 紫红色
"local_storage": "#AF87FF", # 紫色
"willing": "#AFAFFF", # 浅紫色
# 工具模块
"tool_use": "\033[38;5;172m", # 橙褐色
"tool_executor": "\033[38;5;172m", # 橙褐色
"base_tool": "\033[38;5;178m", # 金黄色
"tool_use": "#D78700", # 橙褐色
"tool_executor": "#D78700", # 橙褐色
"base_tool": "#D7AF00", # 金黄色
# 工具和实用模块
"prompt_build": "\033[38;5;105m", # 紫色
"chat_utils": "\033[38;5;111m", # 蓝色
"chat_image": "\033[38;5;117m", # 浅蓝色
"maibot_statistic": "\033[38;5;129m", # 紫色
"prompt_build": "#8787FF", # 紫色
"chat_utils": "#87AFFF", # 蓝色
"chat_image": "#87D7FF", # 浅蓝色
"maibot_statistic": "#AF00FF", # 紫色
# 特殊功能插件
"mute_plugin": "\033[38;5;240m", # 灰色
"core_actions": "\033[38;5;117m", # 深红色
"tts_action": "\033[38;5;58m", # 深黄色
"doubao_pic_plugin": "\033[38;5;64m", # 深绿色
"mute_plugin": "#585858", # 灰色
"core_actions": "#87D7FF", # 深红色
"tts_action": "#5F5F00", # 深黄色
"doubao_pic_plugin": "#5F8700", # 深绿色
# Action组件
"no_reply_action": "\033[38;5;214m", # 亮橙色,显眼但不像警告
"reply_action": "\033[38;5;46m", # 亮绿色
"base_action": "\033[38;5;250m", # 浅灰色
"no_reply_action": "#FFAF00", # 亮橙色,显眼但不像警告
"reply_action": "#00FF00", # 亮绿色
"base_action": "#BCBCBC", # 浅灰色
# 数据库和消息
"database_model": "\033[38;5;94m", # 橙褐色
"database": "\033[38;5;46m", # 橙褐色
"maim_message": "\033[38;5;140m", # 紫褐色
"database_model": "#875F00", # 橙褐色
"database": "#00FF00", # 橙褐色
"maim_message": "#AF87D7", # 紫褐色
# 日志系统
"logger": "\033[38;5;8m", # 深灰色
"confirm": "\033[1;93m", # 黄色+粗体
"logger": "#808080", # 深灰色
"confirm": "#FFFF00", # 黄色+粗体
# 模型相关
"model_utils": "\033[38;5;164m", # 紫红色
"relationship_fetcher": "\033[38;5;170m", # 浅紫色
"relationship_builder": "\033[38;5;93m", # 浅蓝色
"sqlalchemy_init": "\033[38;5;105m", #
"sqlalchemy_models": "\033[38;5;105m",
"sqlalchemy_database_api": "\033[38;5;105m",
"model_utils": "#D700D7", # 紫红色
"relationship_fetcher": "#D75FD7", # 浅紫色
"relationship_builder": "#8700FF", # 浅蓝色
"sqlalchemy_init": "#8787FF", #
"sqlalchemy_models": "#8787FF",
"sqlalchemy_database_api": "#8787FF",
# s4u
"context_web_api": "\033[38;5;240m", # 深灰色
"S4U_chat": "\033[92m", # 亮绿色
"context_web_api": "#585858", # 深灰色
"S4U_chat": "#00FF00", # 亮绿色
# API相关扩展
"chat_api": "\033[38;5;34m", # 深绿色
"emoji_api": "\033[38;5;40m", # 亮绿色
"generator_api": "\033[38;5;28m", # 森林绿
"person_api": "\033[38;5;22m", # 深绿色
"tool_api": "\033[38;5;76m", # 绿色
"OpenAI客户端": "\033[38;5;81m",
"Gemini客户端": "\033[38;5;81m",
"chat_api": "#00AF00", # 深绿色
"emoji_api": "#00D700", # 亮绿色
"generator_api": "#008700", # 森林绿
"person_api": "#005F00", # 深绿色
"tool_api": "#5FD700", # 绿色
"OpenAI客户端": "#5FD7FF",
"Gemini客户端": "#5FD7FF",
# 插件系统扩展
"plugin_base": "\033[38;5;196m", # 红色
"base_event_handler": "\033[38;5;203m", # 粉红色
"events_manager": "\033[38;5;209m", # 橙红色
"global_announcement_manager": "\033[38;5;215m", # 浅橙色
"plugin_base": "#FF0000", # 红色
"base_event_handler": "#FF5F5F", # 粉红色
"events_manager": "#FF875F", # 橙红色
"global_announcement_manager": "#FFAF5F", # 浅橙色
# 工具和依赖管理
"dependency_config": "\033[38;5;24m", # 深蓝色
"dependency_manager": "\033[38;5;30m", # 深青色
"manifest_utils": "\033[38;5;39m", # 蓝色
"schedule_manager": "\033[38;5;27m", # 深蓝色
"monthly_plan_manager": "\033[38;5;171m",
"plan_manager": "\033[38;5;171m",
"llm_generator": "\033[38;5;171m",
"schedule_bridge": "\033[38;5;171m",
"sleep_manager": "\033[38;5;171m",
"official_configs": "\033[38;5;171m",
"mmc_com_layer": "\033[38;5;67m",
"dependency_config": "#005F87", # 深蓝色
"dependency_manager": "#008787", # 深青色
"manifest_utils": "#00AFFF", # 蓝色
"schedule_manager": "#005FFF", # 深蓝色
"monthly_plan_manager": "#D75FFF",
"plan_manager": "#D75FFF",
"llm_generator": "#D75FFF",
"schedule_bridge": "#D75FFF",
"sleep_manager": "#D75FFF",
"official_configs": "#D75FFF",
"mmc_com_layer": "#5F87AF",
# 聊天和多媒体扩展
"chat_voice": "\033[38;5;87m", # 浅青色
"typo_gen": "\033[38;5;123m", # 天蓝色
"utils_video": "\033[38;5;75m", # 亮蓝色
"ReplyerManager": "\033[38;5;173m", # 浅橙色
"relationship_builder_manager": "\033[38;5;176m", # 浅紫色
"expression_selector": "\033[38;5;176m",
"chat_message_builder": "\033[38;5;176m",
"chat_voice": "#5FFFFF", # 浅青色
"typo_gen": "#87FFFF", # 天蓝色
"utils_video": "#5FAFFF", # 亮蓝色
"ReplyerManager": "#D7875F", # 浅橙色
"relationship_builder_manager": "#D787D7", # 浅紫色
"expression_selector": "#D787D7",
"chat_message_builder": "#D787D7",
# MaiZone QQ空间相关
"MaiZone": "\033[38;5;98m", # 紫色
"MaiZone-Monitor": "\033[38;5;104m", # 深紫色
"MaiZone.ConfigLoader": "\033[38;5;110m", # 蓝紫色
"MaiZone-Scheduler": "\033[38;5;134m", # 紫红色
"MaiZone-Utils": "\033[38;5;140m", # 浅紫色
"MaiZone": "#875FD7", # 紫色
"MaiZone-Monitor": "#8787D7", # 深紫色
"MaiZone.ConfigLoader": "#87AFD7", # 蓝紫色
"MaiZone-Scheduler": "#AF5FD7", # 紫红色
"MaiZone-Utils": "#AF87D7", # 浅紫色
# MaiZone Refactored
"MaiZone.HistoryUtils": "\033[38;5;140m",
"MaiZone.SchedulerService": "\033[38;5;134m",
"MaiZone.QZoneService": "\033[38;5;98m",
"MaiZone.MonitorService": "\033[38;5;104m",
"MaiZone.ImageService": "\033[38;5;110m",
"MaiZone.CookieService": "\033[38;5;140m",
"MaiZone.ContentService": "\033[38;5;110m",
"MaiZone.Plugin": "\033[38;5;98m",
"MaiZone.SendFeedCommand": "\033[38;5;134m",
"MaiZone.SendFeedAction": "\033[38;5;134m",
"MaiZone.ReadFeedAction": "\033[38;5;134m",
"MaiZone.HistoryUtils": "#AF87D7",
"MaiZone.SchedulerService": "#AF5FD7",
"MaiZone.QZoneService": "#875FD7",
"MaiZone.MonitorService": "#8787D7",
"MaiZone.ImageService": "#87AFD7",
"MaiZone.CookieService": "#AF87D7",
"MaiZone.ContentService": "#87AFD7",
"MaiZone.Plugin": "#875FD7",
"MaiZone.SendFeedCommand": "#AF5FD7",
"MaiZone.SendFeedAction": "#AF5FD7",
"MaiZone.ReadFeedAction": "#AF5FD7",
# 网络工具
"web_surfing_tool": "\033[38;5;130m", # 棕色
"tts": "\033[38;5;136m", # 浅棕色
"poke_plugin": "\033[38;5;136m",
"set_emoji_like_plugin": "\033[38;5;136m",
"web_surfing_tool": "#AF5F00", # 棕色
"tts": "#AF8700", # 浅棕色
"poke_plugin": "#AF8700",
"set_emoji_like_plugin": "#AF8700",
# mais4u系统扩展
"s4u_config": "\033[38;5;18m", # 深蓝色
"action": "\033[38;5;52m", # 深红色mais4u的action
"context_web": "\033[38;5;58m", # 深黄色
"gift_manager": "\033[38;5;161m", # 粉红色
"prompt": "\033[38;5;99m", # 紫色mais4u的prompt
"super_chat_manager": "\033[38;5;125m", # 紫红色
"watching": "\033[38;5;131m", # 深橙色
"offline_llm": "\033[38;5;236m", # 深灰色
"s4u_stream_generator": "\033[38;5;60m", # 深紫色
"s4u_config": "#000087", # 深蓝色
"action": "#5F0000", # 深红色mais4u的action
"context_web": "#5F5F00", # 深黄色
"gift_manager": "#D7005F", # 粉红色
"prompt": "#875FFF", # 紫色mais4u的prompt
"super_chat_manager": "#AF005F", # 紫红色
"watching": "#AF5F5F", # 深橙色
"offline_llm": "#303030", # 深灰色
"s4u_stream_generator": "#5F5F87", # 深紫色
# 其他工具
"消息压缩工具": "\033[38;5;244m", # 灰色
"lpmm_get_knowledge_tool": "\033[38;5;102m", # 绿色
"message_chunker": "\033[38;5;244m",
"plan_generator": "\033[38;5;171m",
"Permission": "\033[38;5;196m",
"web_search_plugin": "\033[38;5;130m",
"url_parser_tool": "\033[38;5;130m",
"api_key_manager": "\033[38;5;130m",
"tavily_engine": "\033[38;5;130m",
"exa_engine": "\033[38;5;130m",
"ddg_engine": "\033[38;5;130m",
"bing_engine": "\033[38;5;130m",
"vector_instant_memory_v2": "\033[38;5;117m",
"async_memory_optimizer": "\033[38;5;117m",
"async_instant_memory_wrapper": "\033[38;5;117m",
"action_diagnostics": "\033[38;5;214m",
"anti_injector.message_processor": "\033[38;5;196m",
"anti_injector.user_ban": "\033[38;5;196m",
"anti_injector.statistics": "\033[38;5;196m",
"anti_injector.decision_maker": "\033[38;5;196m",
"anti_injector.counter_attack": "\033[38;5;196m",
"hfc.processor": "\033[38;5;81m",
"hfc.normal_mode": "\033[38;5;81m",
"wakeup": "\033[38;5;81m",
"cache_manager": "\033[38;5;244m",
"monthly_plan_db": "\033[38;5;94m",
"db_migration": "\033[38;5;94m",
"小彩蛋": "\033[38;5;214m",
"AioHTTP-Gemini客户端": "\033[38;5;81m",
"napcat_adapter": "\033[38;5;67m", # 柔和的灰蓝色,不刺眼且低调
"event_manager": "\033[38;5;79m", # 柔和的蓝绿色,稍微醒目但不刺眼
"消息压缩工具": "#808080", # 灰色
"lpmm_get_knowledge_tool": "#878787", # 绿色
"message_chunker": "#808080",
"plan_generator": "#D75FFF",
"Permission": "#FF0000",
"web_search_plugin": "#AF5F00",
"url_parser_tool": "#AF5F00",
"api_key_manager": "#AF5F00",
"tavily_engine": "#AF5F00",
"exa_engine": "#AF5F00",
"ddg_engine": "#AF5F00",
"bing_engine": "#AF5F00",
"vector_instant_memory_v2": "#87D7FF",
"async_memory_optimizer": "#87D7FF",
"async_instant_memory_wrapper": "#87D7FF",
"action_diagnostics": "#FFAF00",
"anti_injector.message_processor": "#FF0000",
"anti_injector.user_ban": "#FF0000",
"anti_injector.statistics": "#FF0000",
"anti_injector.decision_maker": "#FF0000",
"anti_injector.counter_attack": "#FF0000",
"hfc.processor": "#5FD7FF",
"hfc.normal_mode": "#5FD7FF",
"wakeup": "#5FD7FF",
"cache_manager": "#808080",
"monthly_plan_db": "#875F00",
"db_migration": "#875F00",
"小彩蛋": "#FFAF00",
"AioHTTP-Gemini客户端": "#5FD7FF",
"napcat_adapter": "#5F87AF", # 柔和的灰蓝色,不刺眼且低调
"event_manager": "#5FD7AF", # 柔和的蓝绿色,稍微醒目但不刺眼
}
DEFAULT_MODULE_ALIASES = {
@@ -750,7 +727,9 @@ DEFAULT_MODULE_ALIASES = {
"AioHTTP-Gemini客户端": "AioHTTP-Gemini客户端",
}
RESET_COLOR = "\033[0m"
# 创建全局 Rich Console 实例用于颜色渲染
_rich_console = Console(force_terminal=True, color_system="truecolor")
def convert_pathname_to_module(logger, method_name, event_dict):
@@ -785,21 +764,21 @@ def convert_pathname_to_module(logger, method_name, event_dict):
class ModuleColoredConsoleRenderer:
"""自定义控制台渲染器,为不同模块提供不同颜色"""
"""自定义控制台渲染器,使用 Rich 库原生支持 hex 颜色"""
def __init__(self, colors=True):
# sourcery skip: merge-duplicate-blocks, remove-redundant-if
self._colors = colors
self._config = LOG_CONFIG
# 日志级别颜色
self._level_colors = {
"debug": "\033[38;5;208m", # 橙色
"info": "\033[38;5;117m", # 天蓝色
"success": "\033[32m", # 绿色
"warning": "\033[33m", # 黄色
"error": "\033[31m", # 红色
"critical": "\033[35m", # 紫色
# 日志级别颜色 (#RRGGBB 格式)
self._level_colors_hex = {
"debug": "#D78700", # 橙色 (ANSI 208)
"info": "#87D7FF", # 天蓝色 (ANSI 117)
"success": "#00FF00", # 绿色
"warning": "#FFFF00", # 黄色
"error": "#FF0000", # 红色
"critical": "#FF00FF", # 紫色
}
# 根据配置决定是否启用颜色
@@ -822,71 +801,65 @@ class ModuleColoredConsoleRenderer:
def __call__(self, logger, method_name, event_dict):
# sourcery skip: merge-duplicate-blocks
"""渲染日志消息"""
# 获取基本信息
timestamp = event_dict.get("timestamp", "")
level = event_dict.get("level", "info")
logger_name = event_dict.get("logger_name", "")
event = event_dict.get("event", "")
# 构建输出
# 构建 Rich Text 对象列表
parts = []
# 日志级别样式配置
log_level_style = self._config.get("log_level_style", "lite")
level_color = self._level_colors.get(level.lower(), "") if self._colors else ""
level_hex_color = self._level_colors_hex.get(level.lower(), "")
# 时间戳lite模式下按级别着色
if timestamp:
if log_level_style == "lite" and level_color:
timestamp_part = f"{level_color}{timestamp}{RESET_COLOR}"
if log_level_style == "lite" and self._colors and level_hex_color:
parts.append(Text(timestamp, style=level_hex_color))
else:
timestamp_part = timestamp
parts.append(timestamp_part)
parts.append(Text(timestamp))
# 日志级别显示(根据配置样式)
if log_level_style == "full":
# 显示完整级别名并着色
level_text = level.upper()
if level_color:
level_part = f"{level_color}[{level_text:>8}]{RESET_COLOR}"
level_text = f"[{level.upper():>8}]"
if self._colors and level_hex_color:
parts.append(Text(level_text, style=level_hex_color))
else:
level_part = f"[{level_text:>8}]"
parts.append(level_part)
parts.append(Text(level_text))
elif log_level_style == "compact":
# 只显示首字母并着色
level_text = level.upper()[0]
if level_color:
level_part = f"{level_color}[{level_text:>8}]{RESET_COLOR}"
level_text = f"[{level.upper()[0]:>8}]"
if self._colors and level_hex_color:
parts.append(Text(level_text, style=level_hex_color))
else:
level_part = f"[{level_text:>8}]"
parts.append(level_part)
parts.append(Text(level_text))
# lite模式不显示级别只给时间戳着色
# 获取模块颜色用于full模式下的整体着色
module_color = ""
# 获取模块颜色
module_hex_color = ""
meta: dict[str, str | None] = {"alias": None, "color": None}
if logger_name:
meta = get_logger_meta(logger_name)
if self._colors and self._enable_module_colors and logger_name:
# 动态优先,其次默认表
meta = get_logger_meta(logger_name)
module_color = meta.get("color") or DEFAULT_MODULE_COLORS.get(logger_name, "")
module_hex_color = meta.get("color") or DEFAULT_MODULE_COLORS.get(logger_name, "")
# 模块名称(带颜色和别名支持)
if logger_name:
# 获取别名,如果没有别名则使用原名称
# 若上面条件不成立需要再次获取 meta
if "meta" not in locals():
meta = get_logger_meta(logger_name)
display_name = meta.get("alias") or DEFAULT_MODULE_ALIASES.get(logger_name, logger_name)
if self._colors and self._enable_module_colors:
if module_color:
module_part = f"{module_color}[{display_name}]{RESET_COLOR}"
module_text = f"[{display_name}]"
if self._colors and self._enable_module_colors and module_hex_color:
parts.append(Text(module_text, style=module_hex_color))
else:
module_part = f"[{display_name}]"
else:
module_part = f"[{display_name}]"
parts.append(module_part)
parts.append(Text(module_text))
# 消息内容(确保转换为字符串)
event_content = ""
@@ -902,38 +875,41 @@ class ModuleColoredConsoleRenderer:
# 其他类型直接转换为字符串
event_content = str(event)
# 在full模式下为消息内容着色
# 在 full 模式下为消息内容着色
if self._colors and self._enable_full_content_colors:
# 检查是否包含“内心思考:”
if "内心思考:" in event_content:
# 使用明亮的粉色
thought_color = "\033[38;5;218m"
# 分割消息内容
# 使用明亮的粉色用于“内心思考”段落
thought_hex_color = "#FFAFD7"
prefix, thought = event_content.split("内心思考:", 1)
# 前缀部分(“决定进行回复,”)使用模块颜色
if module_color:
prefix_colored = f"{module_color}{prefix.strip()}{RESET_COLOR}"
else:
prefix_colored = prefix.strip()
prefix = prefix.strip()
thought = thought.strip()
# “内心思考”部分换行并使用专属颜色
thought_colored = f"\n\n{thought_color}内心思考:{thought.strip()}{RESET_COLOR}\n"
# 重新组合
# parts.append(prefix_colored + thought_colored)
# 将前缀和思考内容作为独立的part添加避免它们之间出现多余的空格
if prefix_colored:
parts.append(prefix_colored)
parts.append(thought_colored)
elif module_color:
event_content = f"{module_color}{event_content}{RESET_COLOR}"
parts.append(event_content)
# 组合为一个 Text避免 join 时插入多余空格
content_text = Text()
if prefix:
if module_hex_color:
content_text.append(prefix, style=module_hex_color)
else:
parts.append(event_content)
content_text.append(prefix)
# 与“内心思考”段落之间插入空行
if prefix:
content_text.append("\n\n")
# “内心思考”标题+内容
content_text.append("内心思考:", style=thought_hex_color)
if thought:
content_text.append(thought, style=thought_hex_color)
parts.append(content_text)
else:
parts.append(event_content)
if module_hex_color:
parts.append(Text(event_content, style=module_hex_color))
else:
parts.append(Text(event_content))
else:
parts.append(Text(event_content))
# 处理其他字段
extras = []
@@ -950,15 +926,24 @@ class ModuleColoredConsoleRenderer:
# 在full模式下为额外字段着色
extra_field = f"{key}={value_str}"
if self._colors and self._enable_full_content_colors and module_color:
extra_field = f"{module_color}{extra_field}{RESET_COLOR}"
extras.append(extra_field)
# 在full模式下为额外字段着色
if self._colors and self._enable_full_content_colors and module_hex_color:
extras.append(Text(extra_field, style=module_hex_color))
else:
extras.append(Text(extra_field))
if extras:
parts.append(" ".join(extras))
parts.append(Text(" "))
parts.extend(extras)
return " ".join(parts)
# 使用 Rich 拼接并返回字符串
result = Text(" ").join(parts)
# 将 Rich Text 对象转换为带 ANSI 颜色码的字符串
from io import StringIO
string_io = StringIO()
temp_console = Console(file=string_io, force_terminal=True, color_system="truecolor", width=999)
temp_console.print(result, end="")
return string_io.getvalue()
# 配置标准logging以支持文件输出和压缩
@@ -974,8 +959,11 @@ logging.basicConfig(
)
def add_logger_metadata(logger: Any, method_name: str, event_dict: dict): # type: ignore[override]
"""structlog 自定义处理器: 注入 color / alias 字段 (用于 JSON 输出)。"""
def add_logger_metadata(logger: WrappedLogger, method_name: str, event_dict: EventDict) -> EventDict: # type: ignore[override]
"""structlog 自定义处理器: 注入 color / alias 字段 (用于 JSON 输出)。
color 使用 #RRGGBB 格式(已通过 _normalize_color 统一)。
"""
name = event_dict.get("logger_name")
if name:
meta = get_logger_meta(name)
@@ -984,7 +972,7 @@ def add_logger_metadata(logger: Any, method_name: str, event_dict: dict): # typ
meta["color"] = DEFAULT_MODULE_COLORS[name]
if meta.get("alias") is None and name in DEFAULT_MODULE_ALIASES:
meta["alias"] = DEFAULT_MODULE_ALIASES[name]
# 注入
# 注入 - color 已经是 #RRGGBB 格式
if meta.get("color"):
event_dict["color"] = meta["color"]
if meta.get("alias"):