🤖 自动格式化代码 [skip ci]

This commit is contained in:
github-actions[bot]
2025-06-12 12:30:00 +00:00
parent faa172f360
commit 9b4fa2155e
5 changed files with 458 additions and 444 deletions

2
bot.py
View File

@@ -10,6 +10,7 @@ from dotenv import load_dotenv
# 最早期初始化日志系统,确保所有后续模块都使用正确的日志格式 # 最早期初始化日志系统,确保所有后续模块都使用正确的日志格式
from src.common.logger import initialize_logging from src.common.logger import initialize_logging
initialize_logging() initialize_logging()
from src.common.logger import get_logger from src.common.logger import get_logger
@@ -46,7 +47,6 @@ app = None
loop = None loop = None
async def request_shutdown() -> bool: async def request_shutdown() -> bool:
"""请求关闭程序""" """请求关闭程序"""
try: try:

View File

@@ -8,6 +8,7 @@ import time
import toml import toml
from datetime import datetime from datetime import datetime
class LogFormatter: class LogFormatter:
"""日志格式化器同步logger.py的格式""" """日志格式化器同步logger.py的格式"""
@@ -163,8 +164,8 @@ class LogFormatter:
try: try:
# 尝试解析ISO格式时间戳 # 尝试解析ISO格式时间戳
if 'T' in timestamp: if "T" in timestamp:
dt = datetime.fromisoformat(timestamp.replace('Z', '+00:00')) dt = datetime.fromisoformat(timestamp.replace("Z", "+00:00"))
else: else:
# 假设已经是格式化的字符串 # 假设已经是格式化的字符串
return timestamp return timestamp
@@ -172,12 +173,12 @@ class LogFormatter:
# 根据配置格式化 # 根据配置格式化
date_style = self.config.get("date_style", "m-d H:i:s") date_style = self.config.get("date_style", "m-d H:i:s")
format_map = { format_map = {
'Y': '%Y', # 4位年份 "Y": "%Y", # 4位年份
'm': '%m', # 月份01-12 "m": "%m", # 月份01-12
'd': '%d', # 日期01-31 "d": "%d", # 日期01-31
'H': '%H', # 小时00-23 "H": "%H", # 小时00-23
'i': '%M', # 分钟00-59 "i": "%M", # 分钟00-59
's': '%S', # 秒数00-59 "s": "%S", # 秒数00-59
} }
python_format = date_style python_format = date_style
@@ -188,6 +189,7 @@ class LogFormatter:
except Exception: except Exception:
return timestamp return timestamp
class LogViewer: class LogViewer:
def __init__(self, root): def __init__(self, root):
self.root = root self.root = root
@@ -221,7 +223,7 @@ class LogViewer:
# 创建模块选择内部框架 # 创建模块选择内部框架
self.module_inner_frame = ttk.Frame(self.module_canvas) self.module_inner_frame = ttk.Frame(self.module_canvas)
self.module_canvas.create_window((0, 0), window=self.module_inner_frame, anchor='nw') self.module_canvas.create_window((0, 0), window=self.module_inner_frame, anchor="nw")
# 创建右侧控制区域(级别和搜索) # 创建右侧控制区域(级别和搜索)
self.right_control_frame = ttk.Frame(self.control_frame) self.right_control_frame = ttk.Frame(self.control_frame)
@@ -237,7 +239,7 @@ class LogViewer:
ttk.Label(level_frame, text="级别:").pack(side=tk.LEFT, padx=2) ttk.Label(level_frame, text="级别:").pack(side=tk.LEFT, padx=2)
self.level_var = tk.StringVar(value="全部") self.level_var = tk.StringVar(value="全部")
self.level_combo = ttk.Combobox(level_frame, textvariable=self.level_var, width=8) self.level_combo = ttk.Combobox(level_frame, textvariable=self.level_var, width=8)
self.level_combo['values'] = ['全部', 'debug', 'info', 'warning', 'error', 'critical'] self.level_combo["values"] = ["全部", "debug", "info", "warning", "error", "critical"]
self.level_combo.pack(side=tk.LEFT, padx=2) self.level_combo.pack(side=tk.LEFT, padx=2)
# 搜索框 # 搜索框
@@ -256,9 +258,15 @@ class LogViewer:
self.scrollbar = ttk.Scrollbar(self.log_frame) self.scrollbar = ttk.Scrollbar(self.log_frame)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.log_text = tk.Text(self.log_frame, wrap=tk.WORD, yscrollcommand=self.scrollbar.set, self.log_text = tk.Text(
background='#1e1e1e', foreground='#ffffff', self.log_frame,
insertbackground='#ffffff', selectbackground='#404040') wrap=tk.WORD,
yscrollcommand=self.scrollbar.set,
background="#1e1e1e",
foreground="#ffffff",
insertbackground="#ffffff",
selectbackground="#404040",
)
self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
self.scrollbar.config(command=self.log_text.yview) self.scrollbar.config(command=self.log_text.yview)
@@ -267,26 +275,26 @@ class LogViewer:
# 模块名映射 # 模块名映射
self.module_name_mapping = { self.module_name_mapping = {
'api': 'API接口', "api": "API接口",
'async_task_manager': '异步任务管理器', "async_task_manager": "异步任务管理器",
'background_tasks': '后台任务', "background_tasks": "后台任务",
'base_tool': '基础工具', "base_tool": "基础工具",
'chat_stream': '聊天流', "chat_stream": "聊天流",
'component_registry': '组件注册器', "component_registry": "组件注册器",
'config': '配置', "config": "配置",
'database_model': '数据库模型', "database_model": "数据库模型",
'emoji': '表情', "emoji": "表情",
'heartflow': '心流', "heartflow": "心流",
'local_storage': '本地存储', "local_storage": "本地存储",
'lpmm': 'LPMM', "lpmm": "LPMM",
'maibot_statistic': 'MaiBot统计', "maibot_statistic": "MaiBot统计",
'main_message': '主消息', "main_message": "主消息",
'main': '主程序', "main": "主程序",
'memory': '内存', "memory": "内存",
'mood': '情绪', "mood": "情绪",
'plugin_manager': '插件管理器', "plugin_manager": "插件管理器",
'remote': '远程', "remote": "远程",
'willing': '意愿', "willing": "意愿",
} }
# 加载自定义映射 # 加载自定义映射
@@ -304,8 +312,8 @@ class LogViewer:
self.update_module_list() self.update_module_list()
# 绑定事件 # 绑定事件
self.level_combo.bind('<<ComboboxSelected>>', self.filter_logs) self.level_combo.bind("<<ComboboxSelected>>", self.filter_logs)
self.search_var.trace('w', self.filter_logs) self.search_var.trace("w", self.filter_logs)
# 启动日志监控线程 # 启动日志监控线程
self.running = True self.running = True
@@ -322,24 +330,15 @@ class LogViewer:
"""加载配置文件""" """加载配置文件"""
# 默认配置 # 默认配置
self.default_config = { self.default_config = {
"log": { "log": {"date_style": "m-d H:i:s", "log_level_style": "lite", "color_text": "full", "log_level": "INFO"},
"date_style": "m-d H:i:s",
"log_level_style": "lite",
"color_text": "full",
"log_level": "INFO"
},
"viewer": { "viewer": {
"theme": "dark", "theme": "dark",
"font_size": 10, "font_size": 10,
"max_lines": 1000, "max_lines": 1000,
"auto_scroll": True, "auto_scroll": True,
"show_milliseconds": False, "show_milliseconds": False,
"window": { "window": {"width": 1200, "height": 800, "remember_position": True},
"width": 1200, },
"height": 800,
"remember_position": True
}
}
} }
# 从bot_config.toml加载日志配置 # 从bot_config.toml加载日志配置
@@ -349,10 +348,10 @@ class LogViewer:
try: try:
if config_path.exists(): if config_path.exists():
with open(config_path, 'r', encoding='utf-8') as f: with open(config_path, "r", encoding="utf-8") as f:
bot_config = toml.load(f) bot_config = toml.load(f)
if 'log' in bot_config: if "log" in bot_config:
self.log_config.update(bot_config['log']) self.log_config.update(bot_config["log"])
except Exception as e: except Exception as e:
print(f"加载bot配置失败: {e}") print(f"加载bot配置失败: {e}")
@@ -363,28 +362,28 @@ class LogViewer:
try: try:
if viewer_config_path.exists(): if viewer_config_path.exists():
with open(viewer_config_path, 'r', encoding='utf-8') as f: with open(viewer_config_path, "r", encoding="utf-8") as f:
viewer_config = toml.load(f) viewer_config = toml.load(f)
if 'viewer' in viewer_config: if "viewer" in viewer_config:
self.viewer_config.update(viewer_config['viewer']) self.viewer_config.update(viewer_config["viewer"])
# 加载自定义模块颜色 # 加载自定义模块颜色
if 'module_colors' in viewer_config['viewer']: if "module_colors" in viewer_config["viewer"]:
self.custom_module_colors = viewer_config['viewer']['module_colors'] self.custom_module_colors = viewer_config["viewer"]["module_colors"]
# 加载自定义级别颜色 # 加载自定义级别颜色
if 'level_colors' in viewer_config['viewer']: if "level_colors" in viewer_config["viewer"]:
self.custom_level_colors = viewer_config['viewer']['level_colors'] self.custom_level_colors = viewer_config["viewer"]["level_colors"]
if 'log' in viewer_config: if "log" in viewer_config:
self.log_config.update(viewer_config['log']) self.log_config.update(viewer_config["log"])
except Exception as e: except Exception as e:
print(f"加载查看器配置失败: {e}") print(f"加载查看器配置失败: {e}")
# 应用窗口配置 # 应用窗口配置
window_config = self.viewer_config.get('window', {}) window_config = self.viewer_config.get("window", {})
window_width = window_config.get('width', 1200) window_width = window_config.get("width", 1200)
window_height = window_config.get('height', 800) window_height = window_config.get("height", 800)
self.root.geometry(f"{window_width}x{window_height}") self.root.geometry(f"{window_width}x{window_height}")
def save_viewer_config(self): def save_viewer_config(self):
@@ -398,16 +397,13 @@ class LogViewer:
if self.custom_level_colors: if self.custom_level_colors:
viewer_config_copy["level_colors"] = self.custom_level_colors viewer_config_copy["level_colors"] = self.custom_level_colors
config_data = { config_data = {"log": self.log_config, "viewer": viewer_config_copy}
"log": self.log_config,
"viewer": viewer_config_copy
}
config_path = Path("config/log_viewer_config.toml") config_path = Path("config/log_viewer_config.toml")
config_path.parent.mkdir(exist_ok=True) config_path.parent.mkdir(exist_ok=True)
try: try:
with open(config_path, 'w', encoding='utf-8') as f: with open(config_path, "w", encoding="utf-8") as f:
toml.dump(config_data, f) toml.dump(config_data, f)
except Exception as e: except Exception as e:
print(f"保存查看器配置失败: {e}") print(f"保存查看器配置失败: {e}")
@@ -442,42 +438,49 @@ class LogViewer:
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 日期格式 # 日期格式
ttk.Label(frame, text="日期格式:").pack(anchor='w', pady=2) ttk.Label(frame, text="日期格式:").pack(anchor="w", pady=2)
date_style_var = tk.StringVar(value=self.log_config.get("date_style", "m-d H:i:s")) date_style_var = tk.StringVar(value=self.log_config.get("date_style", "m-d H:i:s"))
date_entry = ttk.Entry(frame, textvariable=date_style_var, width=30) date_entry = ttk.Entry(frame, textvariable=date_style_var, width=30)
date_entry.pack(anchor='w', pady=2) date_entry.pack(anchor="w", pady=2)
ttk.Label(frame, text="格式说明: Y=年份, m=月份, d=日期, H=小时, i=分钟, s=秒", ttk.Label(frame, text="格式说明: Y=年份, m=月份, d=日期, H=小时, i=分钟, s=秒", font=("", 8)).pack(
font=('', 8)).pack(anchor='w', pady=2) anchor="w", pady=2
)
# 日志级别样式 # 日志级别样式
ttk.Label(frame, text="日志级别样式:").pack(anchor='w', pady=(10,2)) ttk.Label(frame, text="日志级别样式:").pack(anchor="w", pady=(10, 2))
level_style_var = tk.StringVar(value=self.log_config.get("log_level_style", "lite")) level_style_var = tk.StringVar(value=self.log_config.get("log_level_style", "lite"))
level_frame = ttk.Frame(frame) level_frame = ttk.Frame(frame)
level_frame.pack(anchor='w', pady=2) level_frame.pack(anchor="w", pady=2)
ttk.Radiobutton(level_frame, text="简洁(lite)", variable=level_style_var, ttk.Radiobutton(level_frame, text="简洁(lite)", variable=level_style_var, value="lite").pack(
value="lite").pack(side='left', padx=(0,10)) side="left", padx=(0, 10)
ttk.Radiobutton(level_frame, text="紧凑(compact)", variable=level_style_var, )
value="compact").pack(side='left', padx=(0,10)) ttk.Radiobutton(level_frame, text="紧凑(compact)", variable=level_style_var, value="compact").pack(
ttk.Radiobutton(level_frame, text="完整(full)", variable=level_style_var, side="left", padx=(0, 10)
value="full").pack(side='left', padx=(0,10)) )
ttk.Radiobutton(level_frame, text="完整(full)", variable=level_style_var, value="full").pack(
side="left", padx=(0, 10)
)
# 颜色文本设置 # 颜色文本设置
ttk.Label(frame, text="文本颜色设置:").pack(anchor='w', pady=(10,2)) ttk.Label(frame, text="文本颜色设置:").pack(anchor="w", pady=(10, 2))
color_text_var = tk.StringVar(value=self.log_config.get("color_text", "full")) color_text_var = tk.StringVar(value=self.log_config.get("color_text", "full"))
color_frame = ttk.Frame(frame) color_frame = ttk.Frame(frame)
color_frame.pack(anchor='w', pady=2) color_frame.pack(anchor="w", pady=2)
ttk.Radiobutton(color_frame, text="无颜色(none)", variable=color_text_var, ttk.Radiobutton(color_frame, text="无颜色(none)", variable=color_text_var, value="none").pack(
value="none").pack(side='left', padx=(0,10)) side="left", padx=(0, 10)
ttk.Radiobutton(color_frame, text="仅标题(title)", variable=color_text_var, )
value="title").pack(side='left', padx=(0,10)) ttk.Radiobutton(color_frame, text="仅标题(title)", variable=color_text_var, value="title").pack(
ttk.Radiobutton(color_frame, text="全部(full)", variable=color_text_var, side="left", padx=(0, 10)
value="full").pack(side='left', padx=(0,10)) )
ttk.Radiobutton(color_frame, text="全部(full)", variable=color_text_var, value="full").pack(
side="left", padx=(0, 10)
)
# 按钮 # 按钮
button_frame = ttk.Frame(frame) button_frame = ttk.Frame(frame)
button_frame.pack(fill='x', pady=(20,0)) button_frame.pack(fill="x", pady=(20, 0))
def apply_format(): def apply_format():
self.log_config["date_style"] = date_style_var.get() self.log_config["date_style"] = date_style_var.get()
@@ -496,8 +499,8 @@ class LogViewer:
format_window.destroy() format_window.destroy()
ttk.Button(button_frame, text="应用", command=apply_format).pack(side='right', padx=(5,0)) ttk.Button(button_frame, text="应用", command=apply_format).pack(side="right", padx=(5, 0))
ttk.Button(button_frame, text="取消", command=format_window.destroy).pack(side='right') ttk.Button(button_frame, text="取消", command=format_window.destroy).pack(side="right")
def show_viewer_settings(self): def show_viewer_settings(self):
"""显示查看器设置窗口""" """显示查看器设置窗口"""
@@ -509,33 +512,32 @@ class LogViewer:
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# 主题设置 # 主题设置
ttk.Label(frame, text="主题:").pack(anchor='w', pady=2) ttk.Label(frame, text="主题:").pack(anchor="w", pady=2)
theme_var = tk.StringVar(value=self.viewer_config.get("theme", "dark")) theme_var = tk.StringVar(value=self.viewer_config.get("theme", "dark"))
theme_frame = ttk.Frame(frame) theme_frame = ttk.Frame(frame)
theme_frame.pack(anchor='w', pady=2) theme_frame.pack(anchor="w", pady=2)
ttk.Radiobutton(theme_frame, text="深色", variable=theme_var, value="dark").pack(side='left', padx=(0,10)) ttk.Radiobutton(theme_frame, text="深色", variable=theme_var, value="dark").pack(side="left", padx=(0, 10))
ttk.Radiobutton(theme_frame, text="浅色", variable=theme_var, value="light").pack(side='left') ttk.Radiobutton(theme_frame, text="浅色", variable=theme_var, value="light").pack(side="left")
# 字体大小 # 字体大小
ttk.Label(frame, text="字体大小:").pack(anchor='w', pady=(10,2)) ttk.Label(frame, text="字体大小:").pack(anchor="w", pady=(10, 2))
font_size_var = tk.IntVar(value=self.viewer_config.get("font_size", 10)) font_size_var = tk.IntVar(value=self.viewer_config.get("font_size", 10))
font_size_spin = ttk.Spinbox(frame, from_=8, to=20, textvariable=font_size_var, width=10) font_size_spin = ttk.Spinbox(frame, from_=8, to=20, textvariable=font_size_var, width=10)
font_size_spin.pack(anchor='w', pady=2) font_size_spin.pack(anchor="w", pady=2)
# 最大行数 # 最大行数
ttk.Label(frame, text="最大显示行数:").pack(anchor='w', pady=(10,2)) ttk.Label(frame, text="最大显示行数:").pack(anchor="w", pady=(10, 2))
max_lines_var = tk.IntVar(value=self.viewer_config.get("max_lines", 1000)) max_lines_var = tk.IntVar(value=self.viewer_config.get("max_lines", 1000))
max_lines_spin = ttk.Spinbox(frame, from_=100, to=10000, increment=100, max_lines_spin = ttk.Spinbox(frame, from_=100, to=10000, increment=100, textvariable=max_lines_var, width=10)
textvariable=max_lines_var, width=10) max_lines_spin.pack(anchor="w", pady=2)
max_lines_spin.pack(anchor='w', pady=2)
# 自动滚动 # 自动滚动
auto_scroll_var = tk.BooleanVar(value=self.viewer_config.get("auto_scroll", True)) auto_scroll_var = tk.BooleanVar(value=self.viewer_config.get("auto_scroll", True))
ttk.Checkbutton(frame, text="自动滚动到底部", variable=auto_scroll_var).pack(anchor='w', pady=(10,2)) ttk.Checkbutton(frame, text="自动滚动到底部", variable=auto_scroll_var).pack(anchor="w", pady=(10, 2))
# 按钮 # 按钮
button_frame = ttk.Frame(frame) button_frame = ttk.Frame(frame)
button_frame.pack(fill='x', pady=(20,0)) button_frame.pack(fill="x", pady=(20, 0))
def apply_viewer_settings(): def apply_viewer_settings():
self.viewer_config["theme"] = theme_var.get() self.viewer_config["theme"] = theme_var.get()
@@ -551,8 +553,8 @@ class LogViewer:
viewer_window.destroy() viewer_window.destroy()
ttk.Button(button_frame, text="应用", command=apply_viewer_settings).pack(side='right', padx=(5,0)) ttk.Button(button_frame, text="应用", command=apply_viewer_settings).pack(side="right", padx=(5, 0))
ttk.Button(button_frame, text="取消", command=viewer_window.destroy).pack(side='right') ttk.Button(button_frame, text="取消", command=viewer_window.destroy).pack(side="right")
def apply_theme(self): def apply_theme(self):
"""应用主题设置""" """应用主题设置"""
@@ -560,19 +562,16 @@ class LogViewer:
font_size = self.viewer_config.get("font_size", 10) font_size = self.viewer_config.get("font_size", 10)
if theme == "dark": if theme == "dark":
bg_color = '#1e1e1e' bg_color = "#1e1e1e"
fg_color = '#ffffff' fg_color = "#ffffff"
select_bg = '#404040' select_bg = "#404040"
else: else:
bg_color = '#ffffff' bg_color = "#ffffff"
fg_color = '#000000' fg_color = "#000000"
select_bg = '#c0c0c0' select_bg = "#c0c0c0"
self.log_text.config( self.log_text.config(
background=bg_color, background=bg_color, foreground=fg_color, selectbackground=select_bg, font=("Consolas", font_size)
foreground=fg_color,
selectbackground=select_bg,
font=('Consolas', font_size)
) )
# 重新配置标签样式 # 重新配置标签样式
@@ -582,7 +581,7 @@ class LogViewer:
"""配置文本标签样式""" """配置文本标签样式"""
# 清除现有标签 # 清除现有标签
for tag in self.log_text.tag_names(): for tag in self.log_text.tag_names():
if tag != 'sel': if tag != "sel":
self.log_text.tag_delete(tag) self.log_text.tag_delete(tag)
# 基础标签 # 基础标签
@@ -615,12 +614,11 @@ class LogViewer:
def export_logs(self): def export_logs(self):
"""导出当前显示的日志""" """导出当前显示的日志"""
filename = filedialog.asksaveasfilename( filename = filedialog.asksaveasfilename(
defaultextension=".txt", defaultextension=".txt", filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
) )
if filename: if filename:
try: try:
with open(filename, 'w', encoding='utf-8') as f: with open(filename, "w", encoding="utf-8") as f:
f.write(self.log_text.get(1.0, tk.END)) f.write(self.log_text.get(1.0, tk.END))
messagebox.showinfo("导出成功", f"日志已导出到: {filename}") messagebox.showinfo("导出成功", f"日志已导出到: {filename}")
except Exception as e: except Exception as e:
@@ -631,7 +629,7 @@ class LogViewer:
mapping_file = Path("config/module_mapping.json") mapping_file = Path("config/module_mapping.json")
if mapping_file.exists(): if mapping_file.exists():
try: try:
with open(mapping_file, 'r', encoding='utf-8') as f: with open(mapping_file, "r", encoding="utf-8") as f:
custom_mapping = json.load(f) custom_mapping = json.load(f)
self.module_name_mapping.update(custom_mapping) self.module_name_mapping.update(custom_mapping)
except Exception as e: except Exception as e:
@@ -642,7 +640,7 @@ class LogViewer:
mapping_file = Path("config/module_mapping.json") mapping_file = Path("config/module_mapping.json")
mapping_file.parent.mkdir(exist_ok=True) mapping_file.parent.mkdir(exist_ok=True)
try: try:
with open(mapping_file, 'w', encoding='utf-8') as f: with open(mapping_file, "w", encoding="utf-8") as f:
json.dump(self.module_name_mapping, f, ensure_ascii=False, indent=2) json.dump(self.module_name_mapping, f, ensure_ascii=False, indent=2)
except Exception as e: except Exception as e:
print(f"保存模块映射失败: {e}") print(f"保存模块映射失败: {e}")
@@ -668,32 +666,30 @@ class LogViewer:
# 创建内部框架 # 创建内部框架
inner_frame = ttk.Frame(canvas) inner_frame = ttk.Frame(canvas)
canvas.create_window((0, 0), window=inner_frame, anchor='nw') canvas.create_window((0, 0), window=inner_frame, anchor="nw")
# 添加日志级别颜色设置 # 添加日志级别颜色设置
ttk.Label(inner_frame, text="日志级别颜色", font=('', 10, 'bold')).pack(anchor='w', padx=5, pady=5) ttk.Label(inner_frame, text="日志级别颜色", font=("", 10, "bold")).pack(anchor="w", padx=5, pady=5)
for level in ['info', 'warning', 'error']: for level in ["info", "warning", "error"]:
frame = ttk.Frame(inner_frame) frame = ttk.Frame(inner_frame)
frame.pack(fill=tk.X, padx=5, pady=2) frame.pack(fill=tk.X, padx=5, pady=2)
ttk.Label(frame, text=level).pack(side=tk.LEFT) ttk.Label(frame, text=level).pack(side=tk.LEFT)
color_btn = ttk.Button(frame, text="选择颜色", color_btn = ttk.Button(frame, text="选择颜色", command=lambda l=level: self.choose_color(l))
command=lambda l=level: self.choose_color(l))
color_btn.pack(side=tk.RIGHT) color_btn.pack(side=tk.RIGHT)
# 显示当前颜色 # 显示当前颜色
color_label = ttk.Label(frame, text="", foreground=self.formatter.level_colors[level]) color_label = ttk.Label(frame, text="", foreground=self.formatter.level_colors[level])
color_label.pack(side=tk.RIGHT, padx=5) color_label.pack(side=tk.RIGHT, padx=5)
# 添加模块颜色设置 # 添加模块颜色设置
ttk.Label(inner_frame, text="\n模块颜色", font=('', 10, 'bold')).pack(anchor='w', padx=5, pady=5) ttk.Label(inner_frame, text="\n模块颜色", font=("", 10, "bold")).pack(anchor="w", padx=5, pady=5)
for module in sorted(self.modules): for module in sorted(self.modules):
frame = ttk.Frame(inner_frame) frame = ttk.Frame(inner_frame)
frame.pack(fill=tk.X, padx=5, pady=2) frame.pack(fill=tk.X, padx=5, pady=2)
ttk.Label(frame, text=module).pack(side=tk.LEFT) ttk.Label(frame, text=module).pack(side=tk.LEFT)
color_btn = ttk.Button(frame, text="选择颜色", color_btn = ttk.Button(frame, text="选择颜色", command=lambda m=module: self.choose_module_color(m))
command=lambda m=module: self.choose_module_color(m))
color_btn.pack(side=tk.RIGHT) color_btn.pack(side=tk.RIGHT)
# 显示当前颜色 # 显示当前颜色
color = self.formatter.module_colors.get(module, 'black') color = self.formatter.module_colors.get(module, "black")
color_label = ttk.Label(frame, text="", foreground=color) color_label = ttk.Label(frame, text="", foreground=color)
color_label.pack(side=tk.RIGHT, padx=5) color_label.pack(side=tk.RIGHT, padx=5)
@@ -716,7 +712,7 @@ class LogViewer:
def choose_module_color(self, module): def choose_module_color(self, module):
"""选择模块颜色""" """选择模块颜色"""
color = colorchooser.askcolor(color=self.formatter.module_colors.get(module, 'black'))[1] color = colorchooser.askcolor(color=self.formatter.module_colors.get(module, "black"))[1]
if color: if color:
self.formatter.module_colors[module] = color self.formatter.module_colors[module] = color
self.custom_module_colors[module] = color # 保存到自定义颜色 self.custom_module_colors[module] = color # 保存到自定义颜色
@@ -728,12 +724,12 @@ class LogViewer:
"""更新模块列表""" """更新模块列表"""
log_file = Path("logs/app.log.jsonl") log_file = Path("logs/app.log.jsonl")
if log_file.exists(): if log_file.exists():
with open(log_file, 'r', encoding='utf-8') as f: with open(log_file, "r", encoding="utf-8") as f:
for line in f: for line in f:
try: try:
log_entry = json.loads(line) log_entry = json.loads(line)
if 'logger_name' in log_entry: if "logger_name" in log_entry:
self.modules.add(log_entry['logger_name']) self.modules.add(log_entry["logger_name"])
except json.JSONDecodeError: except json.JSONDecodeError:
continue continue
@@ -755,18 +751,19 @@ class LogViewer:
# 添加"全部"选项 # 添加"全部"选项
all_frame = ttk.Frame(self.module_inner_frame) all_frame = ttk.Frame(self.module_inner_frame)
all_frame.grid(row=current_row, column=current_col, padx=3, pady=2, sticky='ew') all_frame.grid(row=current_row, column=current_col, padx=3, pady=2, sticky="ew")
all_var = tk.BooleanVar(value='全部' in self.selected_modules) all_var = tk.BooleanVar(value="全部" in self.selected_modules)
all_check = ttk.Checkbutton(all_frame, text="全部", variable=all_var, all_check = ttk.Checkbutton(
command=lambda: self.toggle_module('全部', all_var)) all_frame, text="全部", variable=all_var, command=lambda: self.toggle_module("全部", all_var)
)
all_check.pack(side=tk.LEFT) all_check.pack(side=tk.LEFT)
# 使用颜色标签替代按钮 # 使用颜色标签替代按钮
all_color = self.formatter.module_colors.get('全部', 'black') all_color = self.formatter.module_colors.get("全部", "black")
all_color_label = ttk.Label(all_frame, text="", foreground=all_color, width=2, cursor="hand2") all_color_label = ttk.Label(all_frame, text="", foreground=all_color, width=2, cursor="hand2")
all_color_label.pack(side=tk.LEFT, padx=2) all_color_label.pack(side=tk.LEFT, padx=2)
all_color_label.bind('<Button-1>', lambda e: self.choose_module_color('全部')) all_color_label.bind("<Button-1>", lambda e: self.choose_module_color("全部"))
current_col += 1 current_col += 1
@@ -777,7 +774,7 @@ class LogViewer:
current_col = 0 current_col = 0
frame = ttk.Frame(self.module_inner_frame) frame = ttk.Frame(self.module_inner_frame)
frame.grid(row=current_row, column=current_col, padx=3, pady=2, sticky='ew') frame.grid(row=current_row, column=current_col, padx=3, pady=2, sticky="ew")
var = tk.BooleanVar(value=module in self.selected_modules) var = tk.BooleanVar(value=module in self.selected_modules)
@@ -786,8 +783,9 @@ class LogViewer:
if len(display_name) > 12: if len(display_name) > 12:
display_name = display_name[:10] + "..." display_name = display_name[:10] + "..."
check = ttk.Checkbutton(frame, text=display_name, variable=var, check = ttk.Checkbutton(
command=lambda m=module, v=var: self.toggle_module(m, v)) frame, text=display_name, variable=var, command=lambda m=module, v=var: self.toggle_module(m, v)
)
check.pack(side=tk.LEFT) check.pack(side=tk.LEFT)
# 添加工具提示显示完整名称和英文名 # 添加工具提示显示完整名称和英文名
@@ -797,10 +795,10 @@ class LogViewer:
self.create_tooltip(check, full_tooltip) self.create_tooltip(check, full_tooltip)
# 使用颜色标签替代按钮 # 使用颜色标签替代按钮
color = self.formatter.module_colors.get(module, 'black') color = self.formatter.module_colors.get(module, "black")
color_label = ttk.Label(frame, text="", foreground=color, width=2, cursor="hand2") color_label = ttk.Label(frame, text="", foreground=color, width=2, cursor="hand2")
color_label.pack(side=tk.LEFT, padx=2) color_label.pack(side=tk.LEFT, padx=2)
color_label.bind('<Button-1>', lambda e, m=module: self.choose_module_color(m)) color_label.bind("<Button-1>", lambda e, m=module: self.choose_module_color(m))
current_col += 1 current_col += 1
@@ -809,42 +807,44 @@ class LogViewer:
self.module_canvas.config(scrollregion=self.module_canvas.bbox("all")) self.module_canvas.config(scrollregion=self.module_canvas.bbox("all"))
# 添加垂直滚动条 # 添加垂直滚动条
if not hasattr(self, 'module_scrollbar'): if not hasattr(self, "module_scrollbar"):
self.module_scrollbar = ttk.Scrollbar(self.module_frame, orient=tk.VERTICAL, self.module_scrollbar = ttk.Scrollbar(
command=self.module_canvas.yview) self.module_frame, orient=tk.VERTICAL, command=self.module_canvas.yview
)
self.module_scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.module_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.module_canvas.config(yscrollcommand=self.module_scrollbar.set) self.module_canvas.config(yscrollcommand=self.module_scrollbar.set)
def create_tooltip(self, widget, text): def create_tooltip(self, widget, text):
"""为控件创建工具提示""" """为控件创建工具提示"""
def on_enter(event): def on_enter(event):
tooltip = tk.Toplevel() tooltip = tk.Toplevel()
tooltip.wm_overrideredirect(True) tooltip.wm_overrideredirect(True)
tooltip.wm_geometry(f"+{event.x_root+10}+{event.y_root+10}") tooltip.wm_geometry(f"+{event.x_root + 10}+{event.y_root + 10}")
label = ttk.Label(tooltip, text=text, background="lightyellow", relief="solid", borderwidth=1) label = ttk.Label(tooltip, text=text, background="lightyellow", relief="solid", borderwidth=1)
label.pack() label.pack()
widget.tooltip = tooltip widget.tooltip = tooltip
def on_leave(event): def on_leave(event):
if hasattr(widget, 'tooltip'): if hasattr(widget, "tooltip"):
widget.tooltip.destroy() widget.tooltip.destroy()
del widget.tooltip del widget.tooltip
widget.bind('<Enter>', on_enter) widget.bind("<Enter>", on_enter)
widget.bind('<Leave>', on_leave) widget.bind("<Leave>", on_leave)
def toggle_module(self, module, var): def toggle_module(self, module, var):
"""切换模块选择状态""" """切换模块选择状态"""
if module == '全部': if module == "全部":
if var.get(): if var.get():
self.selected_modules = {'全部'} self.selected_modules = {"全部"}
else: else:
self.selected_modules.clear() self.selected_modules.clear()
else: else:
if var.get(): if var.get():
self.selected_modules.add(module) self.selected_modules.add(module)
if '全部' in self.selected_modules: if "全部" in self.selected_modules:
self.selected_modules.remove('全部') self.selected_modules.remove("全部")
else: else:
self.selected_modules.discard(module) self.selected_modules.discard(module)
@@ -858,7 +858,7 @@ class LogViewer:
while self.running: while self.running:
if log_file.exists(): if log_file.exists():
try: try:
with open(log_file, 'r', encoding='utf-8') as f: with open(log_file, "r", encoding="utf-8") as f:
f.seek(last_position) f.seek(last_position)
new_lines = f.readlines() new_lines = f.readlines()
last_position = f.tell() last_position = f.tell()
@@ -870,8 +870,8 @@ class LogViewer:
self.log_cache.append(log_entry) self.log_cache.append(log_entry)
# 检查是否有新模块 # 检查是否有新模块
if 'logger_name' in log_entry: if "logger_name" in log_entry:
logger_name = log_entry['logger_name'] logger_name = log_entry["logger_name"]
if logger_name not in self.modules: if logger_name not in self.modules:
self.modules.add(logger_name) self.modules.add(logger_name)
# 在主线程中更新模块列表UI # 在主线程中更新模块列表UI
@@ -909,7 +909,7 @@ class LogViewer:
"""添加格式化的日志行到文本框""" """添加格式化的日志行到文本框"""
# 控制最大行数 # 控制最大行数
max_lines = self.viewer_config.get("max_lines", 1000) max_lines = self.viewer_config.get("max_lines", 1000)
current_lines = int(self.log_text.index('end-1c').split('.')[0]) current_lines = int(self.log_text.index("end-1c").split(".")[0])
if current_lines > max_lines: if current_lines > max_lines:
# 删除前面的行 # 删除前面的行
@@ -951,20 +951,20 @@ class LogViewer:
"""检查日志是否应该显示""" """检查日志是否应该显示"""
# 检查模块过滤 # 检查模块过滤
if self.selected_modules: if self.selected_modules:
if '全部' not in self.selected_modules: if "全部" not in self.selected_modules:
if log_entry.get('logger_name') not in self.selected_modules: if log_entry.get("logger_name") not in self.selected_modules:
return False return False
# 检查级别过滤 # 检查级别过滤
if self.level_var.get() != '全部': if self.level_var.get() != "全部":
if log_entry.get('level') != self.level_var.get(): if log_entry.get("level") != self.level_var.get():
return False return False
# 检查搜索过滤 # 检查搜索过滤
search_text = self.search_var.get().lower() search_text = self.search_var.get().lower()
if search_text: if search_text:
event = str(log_entry.get('event', '')).lower() event = str(log_entry.get("event", "")).lower()
logger_name = str(log_entry.get('logger_name', '')).lower() logger_name = str(log_entry.get("logger_name", "")).lower()
if search_text not in event and search_text not in logger_name: if search_text not in event and search_text not in logger_name:
return False return False
@@ -1013,11 +1013,11 @@ class LogViewer:
# 创建内部框架 # 创建内部框架
inner_frame = ttk.Frame(canvas) inner_frame = ttk.Frame(canvas)
canvas.create_window((0, 0), window=inner_frame, anchor='nw') canvas.create_window((0, 0), window=inner_frame, anchor="nw")
# 添加标题 # 添加标题
ttk.Label(inner_frame, text="模块映射编辑", font=('', 12, 'bold')).pack(anchor='w', padx=5, pady=5) ttk.Label(inner_frame, text="模块映射编辑", font=("", 12, "bold")).pack(anchor="w", padx=5, pady=5)
ttk.Label(inner_frame, text="英文名 -> 中文名", font=('', 10)).pack(anchor='w', padx=5, pady=2) ttk.Label(inner_frame, text="英文名 -> 中文名", font=("", 10)).pack(anchor="w", padx=5, pady=2)
# 映射编辑字典 # 映射编辑字典
mapping_vars = {} mapping_vars = {}
@@ -1061,10 +1061,12 @@ class LogViewer:
ttk.Button(button_frame, text="保存", command=save_mappings).pack(side=tk.RIGHT, padx=5) ttk.Button(button_frame, text="保存", command=save_mappings).pack(side=tk.RIGHT, padx=5)
ttk.Button(button_frame, text="取消", command=mapping_window.destroy).pack(side=tk.RIGHT, padx=5) ttk.Button(button_frame, text="取消", command=mapping_window.destroy).pack(side=tk.RIGHT, padx=5)
def main(): def main():
root = tk.Tk() root = tk.Tk()
app = LogViewer(root) LogViewer(root)
root.mainloop() root.mainloop()
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -494,7 +494,6 @@ class HeartFChatting:
"observed_messages": plan_result.get("observed_messages", ""), "observed_messages": plan_result.get("observed_messages", ""),
} }
with Timer("执行动作", cycle_timers): with Timer("执行动作", cycle_timers):
action_type, action_data, reasoning = ( action_type, action_data, reasoning = (
plan_result.get("action_result", {}).get("action_type", "error"), plan_result.get("action_result", {}).get("action_type", "error"),
@@ -502,7 +501,6 @@ class HeartFChatting:
plan_result.get("action_result", {}).get("reasoning", "未提供理由"), plan_result.get("action_result", {}).get("reasoning", "未提供理由"),
) )
if action_type == "reply": if action_type == "reply":
action_str = "回复" action_str = "回复"
elif action_type == "no_reply": elif action_type == "no_reply":

View File

@@ -5,13 +5,13 @@ from typing import Callable, Optional
import json import json
import structlog import structlog
from structlog.dev import ConsoleRenderer
import toml import toml
# 创建logs目录 # 创建logs目录
LOG_DIR = Path("logs") LOG_DIR = Path("logs")
LOG_DIR.mkdir(exist_ok=True) LOG_DIR.mkdir(exist_ok=True)
# 读取日志配置 # 读取日志配置
def load_log_config(): def load_log_config():
"""从配置文件加载日志设置""" """从配置文件加载日志设置"""
@@ -22,32 +22,34 @@ def load_log_config():
"color_text": "title", "color_text": "title",
"log_level": "INFO", "log_level": "INFO",
"suppress_libraries": [], "suppress_libraries": [],
"library_log_levels": {} "library_log_levels": {},
} }
try: try:
if config_path.exists(): if config_path.exists():
with open(config_path, 'r', encoding='utf-8') as f: with open(config_path, "r", encoding="utf-8") as f:
config = toml.load(f) config = toml.load(f)
return config.get('log', default_config) return config.get("log", default_config)
except Exception: except Exception:
pass pass
return default_config return default_config
LOG_CONFIG = load_log_config() LOG_CONFIG = load_log_config()
def get_timestamp_format(): def get_timestamp_format():
"""将配置中的日期格式转换为Python格式""" """将配置中的日期格式转换为Python格式"""
date_style = LOG_CONFIG.get("date_style", "Y-m-d H:i:s") date_style = LOG_CONFIG.get("date_style", "Y-m-d H:i:s")
# 转换PHP风格的日期格式到Python格式 # 转换PHP风格的日期格式到Python格式
format_map = { format_map = {
'Y': '%Y', # 4位年份 "Y": "%Y", # 4位年份
'm': '%m', # 月份01-12 "m": "%m", # 月份01-12
'd': '%d', # 日期01-31 "d": "%d", # 日期01-31
'H': '%H', # 小时00-23 "H": "%H", # 小时00-23
'i': '%M', # 分钟00-59 "i": "%M", # 分钟00-59
's': '%S', # 秒数00-59 "s": "%S", # 秒数00-59
} }
python_format = date_style python_format = date_style
@@ -56,6 +58,7 @@ def get_timestamp_format():
return python_format return python_format
def configure_third_party_loggers(): def configure_third_party_loggers():
"""配置第三方库的日志级别""" """配置第三方库的日志级别"""
# 设置全局日志级别 # 设置全局日志级别
@@ -128,6 +131,7 @@ def reconfigure_existing_loggers():
handler.setFormatter(console_formatter) handler.setFormatter(console_formatter)
logger_obj.addHandler(handler) logger_obj.addHandler(handler)
# 定义模块颜色映射 # 定义模块颜色映射
MODULE_COLORS = { MODULE_COLORS = {
"api": "\033[92m", # 亮绿色 "api": "\033[92m", # 亮绿色
@@ -288,6 +292,7 @@ logging.basicConfig(
], ],
) )
def configure_structlog(): def configure_structlog():
"""配置structlog""" """配置structlog"""
structlog.configure( structlog.configure(
@@ -306,6 +311,7 @@ def configure_structlog():
cache_logger_on_first_use=True, cache_logger_on_first_use=True,
) )
# 配置structlog # 配置structlog
configure_structlog() configure_structlog()
@@ -343,6 +349,7 @@ for handler in root_logger.handlers:
else: else:
handler.setFormatter(console_formatter) handler.setFormatter(console_formatter)
# 立即配置日志系统,确保最早期的日志也使用正确格式 # 立即配置日志系统,确保最早期的日志也使用正确格式
def _immediate_setup(): def _immediate_setup():
"""立即设置日志系统,在模块导入时就生效""" """立即设置日志系统,在模块导入时就生效"""
@@ -355,12 +362,14 @@ def _immediate_setup():
root_logger.removeHandler(handler) root_logger.removeHandler(handler)
# 重新添加配置好的handler # 重新添加配置好的handler
root_logger.addHandler(logging.handlers.RotatingFileHandler( root_logger.addHandler(
logging.handlers.RotatingFileHandler(
LOG_DIR / "app.log.jsonl", LOG_DIR / "app.log.jsonl",
maxBytes=10 * 1024 * 1024, maxBytes=10 * 1024 * 1024,
backupCount=5, backupCount=5,
encoding="utf-8", encoding="utf-8",
)) )
)
root_logger.addHandler(logging.StreamHandler()) root_logger.addHandler(logging.StreamHandler())
# 设置格式化器 # 设置格式化器
@@ -376,6 +385,7 @@ def _immediate_setup():
# 重新配置所有已存在的logger # 重新配置所有已存在的logger
reconfigure_existing_loggers() reconfigure_existing_loggers()
# 立即执行配置 # 立即执行配置
_immediate_setup() _immediate_setup()
@@ -441,7 +451,8 @@ def reload_log_config():
for handler in root_logger.handlers: for handler in root_logger.handlers:
if isinstance(handler, logging.StreamHandler) and not isinstance(handler, logging.handlers.RotatingFileHandler): if isinstance(handler, logging.StreamHandler) and not isinstance(handler, logging.handlers.RotatingFileHandler):
# 这是控制台处理器,更新其格式化器 # 这是控制台处理器,更新其格式化器
handler.setFormatter(structlog.stdlib.ProcessorFormatter( handler.setFormatter(
structlog.stdlib.ProcessorFormatter(
processor=ModuleColoredConsoleRenderer(colors=True), processor=ModuleColoredConsoleRenderer(colors=True),
foreign_pre_chain=[ foreign_pre_chain=[
structlog.stdlib.add_logger_name, structlog.stdlib.add_logger_name,
@@ -451,7 +462,8 @@ def reload_log_config():
structlog.processors.StackInfoRenderer(), structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info, structlog.processors.format_exc_info,
], ],
)) )
)
# 重新配置第三方库日志 # 重新配置第三方库日志
configure_third_party_loggers() configure_third_party_loggers()
@@ -475,12 +487,14 @@ def force_reset_all_loggers():
root_logger.handlers.clear() root_logger.handlers.clear()
# 重新添加我们的handler # 重新添加我们的handler
root_logger.addHandler(logging.handlers.RotatingFileHandler( root_logger.addHandler(
logging.handlers.RotatingFileHandler(
LOG_DIR / "app.log.jsonl", LOG_DIR / "app.log.jsonl",
maxBytes=10 * 1024 * 1024, maxBytes=10 * 1024 * 1024,
backupCount=5, backupCount=5,
encoding="utf-8", encoding="utf-8",
)) )
)
root_logger.addHandler(logging.StreamHandler()) root_logger.addHandler(logging.StreamHandler())
# 设置格式化器 # 设置格式化器
@@ -533,10 +547,10 @@ def force_initialize_logging():
def show_module_colors(): def show_module_colors():
"""显示所有模块的颜色效果""" """显示所有模块的颜色效果"""
logger = get_logger("demo") get_logger("demo")
print("\n=== 模块颜色展示 ===") print("\n=== 模块颜色展示 ===")
for module_name, color_code in MODULE_COLORS.items(): for module_name, _color_code in MODULE_COLORS.items():
# 临时创建一个该模块的logger来展示颜色 # 临时创建一个该模块的logger来展示颜色
demo_logger = structlog.get_logger(module_name).bind(logger_name=module_name) demo_logger = structlog.get_logger(module_name).bind(logger_name=module_name)
demo_logger.info(f"这是 {module_name} 模块的颜色效果") demo_logger.info(f"这是 {module_name} 模块的颜色效果")