feat:可配置log,解决可读性问题
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -16,6 +16,7 @@ MaiBot-Napcat-Adapter
|
||||
/log_debug
|
||||
/src/test
|
||||
nonebot-maibot-adapter/
|
||||
MaiMBot-LPMM
|
||||
*.zip
|
||||
run.bat
|
||||
log_debug/
|
||||
|
||||
8
bot.py
8
bot.py
@@ -7,9 +7,12 @@ import time
|
||||
import platform
|
||||
import traceback
|
||||
from dotenv import load_dotenv
|
||||
from src.common.logger import get_logger
|
||||
|
||||
# from src.common.logger import LogConfig, CONFIRM_STYLE_CONFIG
|
||||
# 最早期初始化日志系统,确保所有后续模块都使用正确的日志格式
|
||||
from src.common.logger import initialize_logging
|
||||
initialize_logging()
|
||||
|
||||
from src.common.logger import get_logger
|
||||
from src.common.crash_logger import install_crash_handler
|
||||
from src.main import MainSystem
|
||||
from rich.traceback import install
|
||||
@@ -42,7 +45,6 @@ driver = None
|
||||
app = None
|
||||
loop = None
|
||||
|
||||
# shutdown_requested = False # 新增全局变量
|
||||
|
||||
|
||||
async def request_shutdown() -> bool:
|
||||
|
||||
17
reload_config_now.py
Normal file
17
reload_config_now.py
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
立即重新加载日志配置
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# 添加src目录到路径
|
||||
src_path = Path(__file__).parent / "src"
|
||||
sys.path.insert(0, str(src_path))
|
||||
|
||||
from common.logger import reload_log_config
|
||||
|
||||
print("🔄 重新加载日志配置...")
|
||||
reload_log_config()
|
||||
print("✅ 配置已重新加载!faiss日志已被屏蔽。")
|
||||
560
scripts/log_viewer.py
Normal file
560
scripts/log_viewer.py
Normal file
@@ -0,0 +1,560 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, colorchooser
|
||||
import json
|
||||
from pathlib import Path
|
||||
import threading
|
||||
import queue
|
||||
import time
|
||||
|
||||
class LogViewer:
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.root.title("MaiBot日志查看器")
|
||||
self.root.geometry("1200x800")
|
||||
|
||||
# 创建主框架
|
||||
self.main_frame = ttk.Frame(root)
|
||||
self.main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
# 创建控制面板
|
||||
self.control_frame = ttk.Frame(self.main_frame)
|
||||
self.control_frame.pack(fill=tk.X, pady=(0, 5))
|
||||
|
||||
# 模块选择框架
|
||||
self.module_frame = ttk.LabelFrame(self.control_frame, text="模块")
|
||||
self.module_frame.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
|
||||
|
||||
# 创建模块选择滚动区域
|
||||
self.module_canvas = tk.Canvas(self.module_frame, height=80)
|
||||
self.module_canvas.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
||||
|
||||
# 创建模块选择内部框架
|
||||
self.module_inner_frame = ttk.Frame(self.module_canvas)
|
||||
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.pack(side=tk.RIGHT, padx=5)
|
||||
|
||||
# 映射编辑按钮
|
||||
mapping_btn = ttk.Button(self.right_control_frame, text="模块映射", command=self.edit_module_mapping)
|
||||
mapping_btn.pack(side=tk.TOP, fill=tk.X, pady=1)
|
||||
|
||||
# 日志级别选择
|
||||
level_frame = ttk.Frame(self.right_control_frame)
|
||||
level_frame.pack(side=tk.TOP, fill=tk.X, pady=1)
|
||||
ttk.Label(level_frame, text="级别:").pack(side=tk.LEFT, padx=2)
|
||||
self.level_var = tk.StringVar(value="全部")
|
||||
self.level_combo = ttk.Combobox(level_frame, textvariable=self.level_var, width=8)
|
||||
self.level_combo['values'] = ['全部', 'info', 'warning', 'error']
|
||||
self.level_combo.pack(side=tk.LEFT, padx=2)
|
||||
|
||||
# 搜索框
|
||||
search_frame = ttk.Frame(self.right_control_frame)
|
||||
search_frame.pack(side=tk.TOP, fill=tk.X, pady=1)
|
||||
ttk.Label(search_frame, text="搜索:").pack(side=tk.LEFT, padx=2)
|
||||
self.search_var = tk.StringVar()
|
||||
self.search_entry = ttk.Entry(search_frame, textvariable=self.search_var, width=15)
|
||||
self.search_entry.pack(side=tk.LEFT, padx=2)
|
||||
|
||||
# 创建日志显示区域
|
||||
self.log_frame = ttk.Frame(self.main_frame)
|
||||
self.log_frame.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# 创建文本框和滚动条
|
||||
self.scrollbar = ttk.Scrollbar(self.log_frame)
|
||||
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.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
self.scrollbar.config(command=self.log_text.yview)
|
||||
|
||||
# 设置默认标签颜色
|
||||
self.colors = {
|
||||
'info': 'black',
|
||||
'warning': 'orange',
|
||||
'error': 'red'
|
||||
}
|
||||
self.module_colors = {}
|
||||
|
||||
# 模块名映射
|
||||
self.module_name_mapping = {
|
||||
'api': 'API接口',
|
||||
'async_task_manager': '异步任务管理器',
|
||||
'background_tasks': '后台任务',
|
||||
'base_tool': '基础工具',
|
||||
'chat_stream': '聊天流',
|
||||
'component_registry': '组件注册器',
|
||||
'config': '配置',
|
||||
'database_model': '数据库模型',
|
||||
'emoji': '表情',
|
||||
'heartflow': '心流',
|
||||
'local_storage': '本地存储',
|
||||
'lpmm': 'LPMM',
|
||||
'maibot_statistic': 'MaiBot统计',
|
||||
'main_message': '主消息',
|
||||
'main': '主程序',
|
||||
'memory': '内存',
|
||||
'mood': '情绪',
|
||||
'plugin_manager': '插件管理器',
|
||||
'remote': '远程',
|
||||
'willing': '意愿',
|
||||
}
|
||||
|
||||
# 加载自定义映射
|
||||
self.load_module_mapping()
|
||||
|
||||
# 创建日志队列和缓存
|
||||
self.log_queue = queue.Queue()
|
||||
self.log_cache = []
|
||||
|
||||
# 选中的模块集合
|
||||
self.selected_modules = set()
|
||||
|
||||
# 初始化模块列表
|
||||
self.modules = set()
|
||||
self.update_module_list()
|
||||
|
||||
# 绑定事件
|
||||
self.level_combo.bind('<<ComboboxSelected>>', self.filter_logs)
|
||||
self.search_var.trace('w', self.filter_logs)
|
||||
|
||||
# 启动日志监控线程
|
||||
self.running = True
|
||||
self.monitor_thread = threading.Thread(target=self.monitor_log_file)
|
||||
self.monitor_thread.daemon = True
|
||||
self.monitor_thread.start()
|
||||
|
||||
# 启动日志更新线程
|
||||
self.update_thread = threading.Thread(target=self.update_logs)
|
||||
self.update_thread.daemon = True
|
||||
self.update_thread.start()
|
||||
|
||||
def show_color_settings(self):
|
||||
"""显示颜色设置窗口"""
|
||||
color_window = tk.Toplevel(self.root)
|
||||
color_window.title("颜色设置")
|
||||
color_window.geometry("300x400")
|
||||
|
||||
# 创建滚动框架
|
||||
frame = ttk.Frame(color_window)
|
||||
frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
# 创建滚动条
|
||||
scrollbar = ttk.Scrollbar(frame)
|
||||
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
|
||||
# 创建颜色设置列表
|
||||
canvas = tk.Canvas(frame, yscrollcommand=scrollbar.set)
|
||||
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
scrollbar.config(command=canvas.yview)
|
||||
|
||||
# 创建内部框架
|
||||
inner_frame = ttk.Frame(canvas)
|
||||
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)
|
||||
for level in ['info', 'warning', 'error']:
|
||||
frame = ttk.Frame(inner_frame)
|
||||
frame.pack(fill=tk.X, padx=5, pady=2)
|
||||
ttk.Label(frame, text=level).pack(side=tk.LEFT)
|
||||
color_btn = ttk.Button(frame, text="选择颜色",
|
||||
command=lambda l=level: self.choose_color(l))
|
||||
color_btn.pack(side=tk.RIGHT)
|
||||
# 显示当前颜色
|
||||
color_label = ttk.Label(frame, text="■", foreground=self.colors[level])
|
||||
color_label.pack(side=tk.RIGHT, padx=5)
|
||||
|
||||
# 添加模块颜色设置
|
||||
ttk.Label(inner_frame, text="\n模块颜色", font=('', 10, 'bold')).pack(anchor='w', padx=5, pady=5)
|
||||
for module in sorted(self.modules):
|
||||
frame = ttk.Frame(inner_frame)
|
||||
frame.pack(fill=tk.X, padx=5, pady=2)
|
||||
ttk.Label(frame, text=module).pack(side=tk.LEFT)
|
||||
color_btn = ttk.Button(frame, text="选择颜色",
|
||||
command=lambda m=module: self.choose_module_color(m))
|
||||
color_btn.pack(side=tk.RIGHT)
|
||||
# 显示当前颜色
|
||||
color = self.module_colors.get(module, 'black')
|
||||
color_label = ttk.Label(frame, text="■", foreground=color)
|
||||
color_label.pack(side=tk.RIGHT, padx=5)
|
||||
|
||||
# 更新画布滚动区域
|
||||
inner_frame.update_idletasks()
|
||||
canvas.config(scrollregion=canvas.bbox("all"))
|
||||
|
||||
# 添加确定按钮
|
||||
ttk.Button(color_window, text="确定", command=color_window.destroy).pack(pady=5)
|
||||
|
||||
def choose_color(self, level):
|
||||
"""选择日志级别颜色"""
|
||||
color = colorchooser.askcolor(color=self.colors[level])[1]
|
||||
if color:
|
||||
self.colors[level] = color
|
||||
self.log_text.tag_configure(level, foreground=color)
|
||||
self.filter_logs()
|
||||
|
||||
def choose_module_color(self, module):
|
||||
"""选择模块颜色"""
|
||||
color = colorchooser.askcolor(color=self.module_colors.get(module, 'black'))[1]
|
||||
if color:
|
||||
self.module_colors[module] = color
|
||||
self.log_text.tag_configure(f"module_{module}", foreground=color)
|
||||
# 更新模块列表中的颜色显示
|
||||
self.update_module_color_display(module, color)
|
||||
self.filter_logs()
|
||||
|
||||
def update_module_color_display(self, module, color):
|
||||
"""更新模块列表中的颜色显示"""
|
||||
# 遍历模块框架中的所有子控件,找到对应模块的颜色标签并更新
|
||||
for widget in self.module_inner_frame.winfo_children():
|
||||
if isinstance(widget, ttk.Frame):
|
||||
# 检查这个框架是否包含目标模块
|
||||
for child in widget.winfo_children():
|
||||
if isinstance(child, ttk.Checkbutton):
|
||||
if child.cget('text') == module:
|
||||
# 找到了对应的模块,更新其颜色标签
|
||||
for sibling in widget.winfo_children():
|
||||
if isinstance(sibling, ttk.Label) and sibling.cget('text') == '■':
|
||||
sibling.config(foreground=color)
|
||||
return
|
||||
|
||||
def update_module_list(self):
|
||||
"""更新模块列表"""
|
||||
log_file = Path("logs/app.log.jsonl")
|
||||
if log_file.exists():
|
||||
with open(log_file, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
try:
|
||||
log_entry = json.loads(line)
|
||||
if 'logger_name' in log_entry:
|
||||
self.modules.add(log_entry['logger_name'])
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
|
||||
# 清空现有选项
|
||||
for widget in self.module_inner_frame.winfo_children():
|
||||
widget.destroy()
|
||||
|
||||
# 计算总模块数(包括"全部")
|
||||
total_modules = len(self.modules) + 1
|
||||
max_cols = min(4, max(2, total_modules)) # 减少最大列数,避免超出边界
|
||||
|
||||
# 配置网格列权重,让每列平均分配空间
|
||||
for i in range(max_cols):
|
||||
self.module_inner_frame.grid_columnconfigure(i, weight=1, uniform="module_col")
|
||||
|
||||
# 创建一个多行布局
|
||||
current_row = 0
|
||||
current_col = 0
|
||||
|
||||
# 添加"全部"选项
|
||||
all_frame = ttk.Frame(self.module_inner_frame)
|
||||
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_check = ttk.Checkbutton(all_frame, text="全部", variable=all_var,
|
||||
command=lambda: self.toggle_module('全部', all_var))
|
||||
all_check.pack(side=tk.LEFT)
|
||||
|
||||
# 使用颜色标签替代按钮
|
||||
all_color = self.module_colors.get('全部', 'black')
|
||||
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.bind('<Button-1>', lambda e: self.choose_module_color('全部'))
|
||||
|
||||
current_col += 1
|
||||
|
||||
# 添加其他模块选项
|
||||
for module in sorted(self.modules):
|
||||
if current_col >= max_cols:
|
||||
current_row += 1
|
||||
current_col = 0
|
||||
|
||||
frame = ttk.Frame(self.module_inner_frame)
|
||||
frame.grid(row=current_row, column=current_col, padx=3, pady=2, sticky='ew')
|
||||
|
||||
var = tk.BooleanVar(value=module in self.selected_modules)
|
||||
|
||||
# 使用中文映射名称显示
|
||||
display_name = self.get_display_name(module)
|
||||
if len(display_name) > 12:
|
||||
display_name = display_name[:10] + "..."
|
||||
|
||||
check = ttk.Checkbutton(frame, text=display_name, variable=var,
|
||||
command=lambda m=module, v=var: self.toggle_module(m, v))
|
||||
check.pack(side=tk.LEFT)
|
||||
|
||||
# 添加工具提示显示完整名称和英文名
|
||||
full_tooltip = f"{self.get_display_name(module)}"
|
||||
if module != self.get_display_name(module):
|
||||
full_tooltip += f"\n({module})"
|
||||
self.create_tooltip(check, full_tooltip)
|
||||
|
||||
# 使用颜色标签替代按钮
|
||||
color = self.module_colors.get(module, 'black')
|
||||
color_label = ttk.Label(frame, text="■", foreground=color, width=2, cursor="hand2")
|
||||
color_label.pack(side=tk.LEFT, padx=2)
|
||||
color_label.bind('<Button-1>', lambda e, m=module: self.choose_module_color(m))
|
||||
|
||||
current_col += 1
|
||||
|
||||
# 更新画布滚动区域
|
||||
self.module_inner_frame.update_idletasks()
|
||||
self.module_canvas.config(scrollregion=self.module_canvas.bbox("all"))
|
||||
|
||||
# 添加垂直滚动条
|
||||
if not hasattr(self, 'module_scrollbar'):
|
||||
self.module_scrollbar = ttk.Scrollbar(self.module_frame, orient=tk.VERTICAL,
|
||||
command=self.module_canvas.yview)
|
||||
self.module_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
self.module_canvas.config(yscrollcommand=self.module_scrollbar.set)
|
||||
|
||||
def create_tooltip(self, widget, text):
|
||||
"""为控件创建工具提示"""
|
||||
def on_enter(event):
|
||||
tooltip = tk.Toplevel()
|
||||
tooltip.wm_overrideredirect(True)
|
||||
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.pack()
|
||||
widget.tooltip = tooltip
|
||||
|
||||
def on_leave(event):
|
||||
if hasattr(widget, 'tooltip'):
|
||||
widget.tooltip.destroy()
|
||||
del widget.tooltip
|
||||
|
||||
widget.bind('<Enter>', on_enter)
|
||||
widget.bind('<Leave>', on_leave)
|
||||
|
||||
def toggle_module(self, module, var):
|
||||
"""切换模块选择状态"""
|
||||
if module == '全部':
|
||||
if var.get():
|
||||
self.selected_modules = {'全部'}
|
||||
else:
|
||||
self.selected_modules.clear()
|
||||
else:
|
||||
if var.get():
|
||||
self.selected_modules.add(module)
|
||||
if '全部' in self.selected_modules:
|
||||
self.selected_modules.remove('全部')
|
||||
else:
|
||||
self.selected_modules.discard(module)
|
||||
|
||||
self.filter_logs()
|
||||
|
||||
def monitor_log_file(self):
|
||||
"""监控日志文件变化"""
|
||||
log_file = Path("logs/app.log.jsonl")
|
||||
last_position = 0
|
||||
|
||||
while self.running:
|
||||
if log_file.exists():
|
||||
try:
|
||||
with open(log_file, 'r', encoding='utf-8') as f:
|
||||
f.seek(last_position)
|
||||
new_lines = f.readlines()
|
||||
last_position = f.tell()
|
||||
|
||||
for line in new_lines:
|
||||
try:
|
||||
log_entry = json.loads(line)
|
||||
self.log_queue.put(log_entry)
|
||||
self.log_cache.append(log_entry)
|
||||
|
||||
# 检查是否有新模块
|
||||
if 'logger_name' in log_entry:
|
||||
logger_name = log_entry['logger_name']
|
||||
if logger_name not in self.modules:
|
||||
self.modules.add(logger_name)
|
||||
# 在主线程中更新模块列表UI
|
||||
self.root.after(0, self.update_module_list)
|
||||
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f"Error reading log file: {e}")
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
def update_logs(self):
|
||||
"""更新日志显示"""
|
||||
while self.running:
|
||||
try:
|
||||
log_entry = self.log_queue.get(timeout=0.1)
|
||||
self.process_log_entry(log_entry)
|
||||
except queue.Empty:
|
||||
continue
|
||||
|
||||
def process_log_entry(self, log_entry):
|
||||
"""处理日志条目"""
|
||||
# 检查过滤条件
|
||||
if not self.should_show_log(log_entry):
|
||||
return
|
||||
|
||||
# 格式化日志
|
||||
timestamp = log_entry.get('timestamp', '')
|
||||
level = log_entry.get('level', 'info')
|
||||
logger_name = log_entry.get('logger_name', '')
|
||||
event = log_entry.get('event', '')
|
||||
|
||||
log_line = f"{timestamp} [{level}] {logger_name}: {event}\n"
|
||||
|
||||
# 在主线程中更新UI
|
||||
self.root.after(0, lambda: self.add_log_line(log_line, level, logger_name))
|
||||
|
||||
def add_log_line(self, line, level, logger_name):
|
||||
"""添加日志行到文本框"""
|
||||
self.log_text.insert(tk.END, line, (level, f"module_{logger_name}"))
|
||||
# 只有在用户没有手动滚动时才自动滚动到底部
|
||||
if self.log_text.yview()[1] >= 0.99:
|
||||
self.log_text.see(tk.END)
|
||||
|
||||
def should_show_log(self, log_entry):
|
||||
"""检查日志是否应该显示"""
|
||||
# 检查模块过滤
|
||||
if self.selected_modules:
|
||||
if '全部' not in self.selected_modules:
|
||||
if log_entry.get('logger_name') not in self.selected_modules:
|
||||
return False
|
||||
|
||||
# 检查级别过滤
|
||||
if self.level_var.get() != '全部':
|
||||
if log_entry.get('level') != self.level_var.get():
|
||||
return False
|
||||
|
||||
# 检查搜索过滤
|
||||
search_text = self.search_var.get().lower()
|
||||
if search_text:
|
||||
event = str(log_entry.get('event', '')).lower()
|
||||
logger_name = str(log_entry.get('logger_name', '')).lower()
|
||||
if search_text not in event and search_text not in logger_name:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def filter_logs(self, *args):
|
||||
"""过滤日志"""
|
||||
# 保存当前滚动位置
|
||||
scroll_position = self.log_text.yview()
|
||||
|
||||
# 清空显示
|
||||
self.log_text.delete(1.0, tk.END)
|
||||
|
||||
# 重新显示所有符合条件的日志
|
||||
for log_entry in self.log_cache:
|
||||
if self.should_show_log(log_entry):
|
||||
timestamp = log_entry.get('timestamp', '')
|
||||
level = log_entry.get('level', 'info')
|
||||
logger_name = log_entry.get('logger_name', '')
|
||||
event = log_entry.get('event', '')
|
||||
log_line = f"{timestamp} [{level}] {logger_name}: {event}\n"
|
||||
self.log_text.insert(tk.END, log_line, (level, f"module_{logger_name}"))
|
||||
|
||||
# 恢复滚动位置
|
||||
self.log_text.yview_moveto(scroll_position[0])
|
||||
|
||||
def get_display_name(self, module_name):
|
||||
"""获取模块的显示名称"""
|
||||
return self.module_name_mapping.get(module_name, module_name)
|
||||
|
||||
def load_module_mapping(self):
|
||||
"""加载自定义模块映射"""
|
||||
mapping_file = Path("config/module_mapping.json")
|
||||
if mapping_file.exists():
|
||||
try:
|
||||
with open(mapping_file, 'r', encoding='utf-8') as f:
|
||||
custom_mapping = json.load(f)
|
||||
self.module_name_mapping.update(custom_mapping)
|
||||
except Exception as e:
|
||||
print(f"加载模块映射失败: {e}")
|
||||
|
||||
def save_module_mapping(self):
|
||||
"""保存自定义模块映射"""
|
||||
mapping_file = Path("config/module_mapping.json")
|
||||
mapping_file.parent.mkdir(exist_ok=True)
|
||||
try:
|
||||
with open(mapping_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(self.module_name_mapping, f, ensure_ascii=False, indent=2)
|
||||
except Exception as e:
|
||||
print(f"保存模块映射失败: {e}")
|
||||
|
||||
def edit_module_mapping(self):
|
||||
"""编辑模块映射"""
|
||||
mapping_window = tk.Toplevel(self.root)
|
||||
mapping_window.title("编辑模块映射")
|
||||
mapping_window.geometry("500x600")
|
||||
|
||||
# 创建滚动框架
|
||||
frame = ttk.Frame(mapping_window)
|
||||
frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
# 创建滚动条
|
||||
scrollbar = ttk.Scrollbar(frame)
|
||||
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
|
||||
# 创建映射编辑列表
|
||||
canvas = tk.Canvas(frame, yscrollcommand=scrollbar.set)
|
||||
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
scrollbar.config(command=canvas.yview)
|
||||
|
||||
# 创建内部框架
|
||||
inner_frame = ttk.Frame(canvas)
|
||||
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=('', 10)).pack(anchor='w', padx=5, pady=2)
|
||||
|
||||
# 映射编辑字典
|
||||
mapping_vars = {}
|
||||
|
||||
# 添加现有模块的映射编辑
|
||||
all_modules = sorted(self.modules)
|
||||
for module in all_modules:
|
||||
frame_row = ttk.Frame(inner_frame)
|
||||
frame_row.pack(fill=tk.X, padx=5, pady=2)
|
||||
|
||||
ttk.Label(frame_row, text=module, width=20).pack(side=tk.LEFT, padx=5)
|
||||
ttk.Label(frame_row, text="->").pack(side=tk.LEFT, padx=5)
|
||||
|
||||
var = tk.StringVar(value=self.module_name_mapping.get(module, module))
|
||||
mapping_vars[module] = var
|
||||
entry = ttk.Entry(frame_row, textvariable=var, width=25)
|
||||
entry.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# 更新画布滚动区域
|
||||
inner_frame.update_idletasks()
|
||||
canvas.config(scrollregion=canvas.bbox("all"))
|
||||
|
||||
def save_mappings():
|
||||
# 更新映射
|
||||
for module, var in mapping_vars.items():
|
||||
new_name = var.get().strip()
|
||||
if new_name and new_name != module:
|
||||
self.module_name_mapping[module] = new_name
|
||||
elif module in self.module_name_mapping and not new_name:
|
||||
del self.module_name_mapping[module]
|
||||
|
||||
# 保存到文件
|
||||
self.save_module_mapping()
|
||||
# 更新模块列表显示
|
||||
self.update_module_list()
|
||||
mapping_window.destroy()
|
||||
|
||||
# 添加按钮
|
||||
button_frame = ttk.Frame(mapping_window)
|
||||
button_frame.pack(fill=tk.X, padx=5, pady=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)
|
||||
|
||||
def main():
|
||||
root = tk.Tk()
|
||||
app = LogViewer(root)
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -14,7 +14,6 @@ from src.chat.focus_chat.heartFC_Cycleinfo import CycleDetail
|
||||
from src.chat.focus_chat.info.info_base import InfoBase
|
||||
from src.chat.focus_chat.info_processors.chattinginfo_processor import ChattingInfoProcessor
|
||||
from src.chat.focus_chat.info_processors.relationship_processor import RelationshipProcessor
|
||||
from src.chat.focus_chat.info_processors.mind_processor import MindProcessor
|
||||
from src.chat.focus_chat.info_processors.working_memory_processor import WorkingMemoryProcessor
|
||||
|
||||
# from src.chat.focus_chat.info_processors.action_processor import ActionProcessor
|
||||
@@ -47,7 +46,6 @@ OBSERVATION_CLASSES = {
|
||||
# 定义处理器映射:键是处理器名称,值是 (处理器类, 可选的配置键名)
|
||||
PROCESSOR_CLASSES = {
|
||||
"ChattingInfoProcessor": (ChattingInfoProcessor, None),
|
||||
"MindProcessor": (MindProcessor, "mind_processor"),
|
||||
"ToolProcessor": (ToolProcessor, "tool_use_processor"),
|
||||
"WorkingMemoryProcessor": (WorkingMemoryProcessor, "working_memory_processor"),
|
||||
"SelfProcessor": (SelfProcessor, "self_identify_processor"),
|
||||
@@ -189,7 +187,6 @@ class HeartFChatting:
|
||||
processor_actual_class = processor_info[0] # 获取实际的类定义
|
||||
# 根据处理器类名判断是否需要 subheartflow_id
|
||||
if name in [
|
||||
"MindProcessor",
|
||||
"ToolProcessor",
|
||||
"WorkingMemoryProcessor",
|
||||
"SelfProcessor",
|
||||
@@ -494,10 +491,10 @@ class HeartFChatting:
|
||||
|
||||
loop_plan_info = {
|
||||
"action_result": plan_result.get("action_result", {}),
|
||||
"current_mind": plan_result.get("current_mind", ""),
|
||||
"observed_messages": plan_result.get("observed_messages", ""),
|
||||
}
|
||||
|
||||
|
||||
with Timer("执行动作", cycle_timers):
|
||||
action_type, action_data, reasoning = (
|
||||
plan_result.get("action_result", {}).get("action_type", "error"),
|
||||
@@ -505,6 +502,7 @@ class HeartFChatting:
|
||||
plan_result.get("action_result", {}).get("reasoning", "未提供理由"),
|
||||
)
|
||||
|
||||
|
||||
if action_type == "reply":
|
||||
action_str = "回复"
|
||||
elif action_type == "no_reply":
|
||||
|
||||
@@ -5,11 +5,272 @@ from typing import Callable, Optional
|
||||
import json
|
||||
|
||||
import structlog
|
||||
from structlog.dev import ConsoleRenderer
|
||||
import toml
|
||||
|
||||
# 创建logs目录
|
||||
LOG_DIR = Path("logs")
|
||||
LOG_DIR.mkdir(exist_ok=True)
|
||||
|
||||
# 读取日志配置
|
||||
def load_log_config():
|
||||
"""从配置文件加载日志设置"""
|
||||
config_path = Path("config/bot_config.toml")
|
||||
default_config = {
|
||||
"date_style": "Y-m-d H:i:s",
|
||||
"log_level_style": "lite",
|
||||
"color_text": "title",
|
||||
"log_level": "INFO",
|
||||
"suppress_libraries": [],
|
||||
"library_log_levels": {}
|
||||
}
|
||||
|
||||
try:
|
||||
if config_path.exists():
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
config = toml.load(f)
|
||||
return config.get('log', default_config)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return default_config
|
||||
|
||||
LOG_CONFIG = load_log_config()
|
||||
|
||||
def get_timestamp_format():
|
||||
"""将配置中的日期格式转换为Python格式"""
|
||||
date_style = LOG_CONFIG.get("date_style", "Y-m-d H:i:s")
|
||||
# 转换PHP风格的日期格式到Python格式
|
||||
format_map = {
|
||||
'Y': '%Y', # 4位年份
|
||||
'm': '%m', # 月份(01-12)
|
||||
'd': '%d', # 日期(01-31)
|
||||
'H': '%H', # 小时(00-23)
|
||||
'i': '%M', # 分钟(00-59)
|
||||
's': '%S', # 秒数(00-59)
|
||||
}
|
||||
|
||||
python_format = date_style
|
||||
for php_char, python_char in format_map.items():
|
||||
python_format = python_format.replace(php_char, python_char)
|
||||
|
||||
return python_format
|
||||
|
||||
def configure_third_party_loggers():
|
||||
"""配置第三方库的日志级别"""
|
||||
# 设置全局日志级别
|
||||
global_log_level = LOG_CONFIG.get("log_level", "INFO")
|
||||
root_logger = logging.getLogger()
|
||||
root_logger.setLevel(getattr(logging, global_log_level.upper(), logging.INFO))
|
||||
|
||||
# 完全屏蔽的库
|
||||
suppress_libraries = LOG_CONFIG.get("suppress_libraries", [])
|
||||
for lib_name in suppress_libraries:
|
||||
lib_logger = logging.getLogger(lib_name)
|
||||
lib_logger.setLevel(logging.CRITICAL + 1) # 设置为比CRITICAL更高的级别,基本屏蔽所有日志
|
||||
lib_logger.propagate = False # 阻止向上传播
|
||||
|
||||
# 设置特定级别的库
|
||||
library_log_levels = LOG_CONFIG.get("library_log_levels", {})
|
||||
for lib_name, level_name in library_log_levels.items():
|
||||
lib_logger = logging.getLogger(lib_name)
|
||||
level = getattr(logging, level_name.upper(), logging.WARNING)
|
||||
lib_logger.setLevel(level)
|
||||
|
||||
|
||||
def reconfigure_existing_loggers():
|
||||
"""重新配置所有已存在的logger,解决加载顺序问题"""
|
||||
# 获取根logger
|
||||
root_logger = logging.getLogger()
|
||||
|
||||
# 重新设置根logger的所有handler的格式化器
|
||||
for handler in root_logger.handlers:
|
||||
if isinstance(handler, logging.handlers.RotatingFileHandler):
|
||||
handler.setFormatter(file_formatter)
|
||||
elif isinstance(handler, logging.StreamHandler):
|
||||
handler.setFormatter(console_formatter)
|
||||
|
||||
# 遍历所有已存在的logger并重新配置
|
||||
logger_dict = logging.getLogger().manager.loggerDict
|
||||
for name, logger_obj in logger_dict.items():
|
||||
if isinstance(logger_obj, logging.Logger):
|
||||
# 检查是否是第三方库logger
|
||||
suppress_libraries = LOG_CONFIG.get("suppress_libraries", [])
|
||||
library_log_levels = LOG_CONFIG.get("library_log_levels", {})
|
||||
|
||||
# 如果在屏蔽列表中
|
||||
if any(name.startswith(lib) for lib in suppress_libraries):
|
||||
logger_obj.setLevel(logging.CRITICAL + 1)
|
||||
logger_obj.propagate = False
|
||||
continue
|
||||
|
||||
# 如果在特定级别设置中
|
||||
for lib_name, level_name in library_log_levels.items():
|
||||
if name.startswith(lib_name):
|
||||
level = getattr(logging, level_name.upper(), logging.WARNING)
|
||||
logger_obj.setLevel(level)
|
||||
break
|
||||
|
||||
# 强制清除并重新设置所有handler
|
||||
original_handlers = logger_obj.handlers[:]
|
||||
for handler in original_handlers:
|
||||
logger_obj.removeHandler(handler)
|
||||
|
||||
# 如果logger没有handler,让它使用根logger的handler(propagate=True)
|
||||
if not logger_obj.handlers:
|
||||
logger_obj.propagate = True
|
||||
|
||||
# 如果logger有自己的handler,重新配置它们
|
||||
for handler in original_handlers:
|
||||
if isinstance(handler, logging.handlers.RotatingFileHandler):
|
||||
handler.setFormatter(file_formatter)
|
||||
elif isinstance(handler, logging.StreamHandler):
|
||||
handler.setFormatter(console_formatter)
|
||||
logger_obj.addHandler(handler)
|
||||
|
||||
# 定义模块颜色映射
|
||||
MODULE_COLORS = {
|
||||
"api": "\033[92m", # 亮绿色
|
||||
"emoji": "\033[92m", # 亮绿色
|
||||
"chat": "\033[94m", # 亮蓝色
|
||||
"config": "\033[93m", # 亮黄色
|
||||
"common": "\033[95m", # 亮紫色
|
||||
"tools": "\033[96m", # 亮青色
|
||||
"lpmm": "\033[96m",
|
||||
"plugin_system": "\033[91m", # 亮红色
|
||||
"experimental": "\033[97m", # 亮白色
|
||||
"person_info": "\033[32m", # 绿色
|
||||
"individuality": "\033[34m", # 蓝色
|
||||
"manager": "\033[35m", # 紫色
|
||||
"llm_models": "\033[36m", # 青色
|
||||
"plugins": "\033[31m", # 红色
|
||||
"plugin_api": "\033[33m", # 黄色
|
||||
"remote": "\033[38;5;93m", # 紫蓝色
|
||||
}
|
||||
|
||||
RESET_COLOR = "\033[0m"
|
||||
|
||||
|
||||
class ModuleColoredConsoleRenderer:
|
||||
"""自定义控制台渲染器,为不同模块提供不同颜色"""
|
||||
|
||||
def __init__(self, colors=True):
|
||||
self._colors = colors
|
||||
self._config = LOG_CONFIG
|
||||
|
||||
# 日志级别颜色
|
||||
self._level_colors = {
|
||||
"debug": "\033[38;5;208m", # 橙色
|
||||
"info": "\033[34m", # 蓝色
|
||||
"success": "\033[32m", # 绿色
|
||||
"warning": "\033[33m", # 黄色
|
||||
"error": "\033[31m", # 红色
|
||||
"critical": "\033[35m", # 紫色
|
||||
}
|
||||
|
||||
# 根据配置决定是否启用颜色
|
||||
color_text = self._config.get("color_text", "title")
|
||||
if color_text == "none":
|
||||
self._colors = False
|
||||
elif color_text == "title":
|
||||
self._enable_module_colors = True
|
||||
self._enable_level_colors = False
|
||||
elif color_text == "full":
|
||||
self._enable_module_colors = True
|
||||
self._enable_level_colors = True
|
||||
else:
|
||||
self._enable_module_colors = True
|
||||
self._enable_level_colors = False
|
||||
|
||||
def __call__(self, logger, method_name, event_dict):
|
||||
"""渲染日志消息"""
|
||||
# 获取基本信息
|
||||
timestamp = event_dict.get("timestamp", "")
|
||||
level = event_dict.get("level", "info")
|
||||
logger_name = event_dict.get("logger_name", "")
|
||||
event = event_dict.get("event", "")
|
||||
|
||||
# 构建输出
|
||||
parts = []
|
||||
|
||||
# 日志级别样式配置
|
||||
log_level_style = self._config.get("log_level_style", "lite")
|
||||
level_color = self._level_colors.get(level.lower(), "") if self._colors else ""
|
||||
|
||||
# 时间戳(lite模式下按级别着色)
|
||||
if timestamp:
|
||||
if log_level_style == "lite" and level_color:
|
||||
timestamp_part = f"{level_color}{timestamp}{RESET_COLOR}"
|
||||
else:
|
||||
timestamp_part = timestamp
|
||||
parts.append(timestamp_part)
|
||||
|
||||
# 日志级别显示(根据配置样式)
|
||||
if log_level_style == "full":
|
||||
# 显示完整级别名并着色
|
||||
level_text = level.upper()
|
||||
if level_color:
|
||||
level_part = f"{level_color}[{level_text:>8}]{RESET_COLOR}"
|
||||
else:
|
||||
level_part = f"[{level_text:>8}]"
|
||||
parts.append(level_part)
|
||||
|
||||
elif log_level_style == "compact":
|
||||
# 只显示首字母并着色
|
||||
level_text = level.upper()[0]
|
||||
if level_color:
|
||||
level_part = f"{level_color}[{level_text:>8}]{RESET_COLOR}"
|
||||
else:
|
||||
level_part = f"[{level_text:>8}]"
|
||||
parts.append(level_part)
|
||||
|
||||
# lite模式不显示级别,只给时间戳着色
|
||||
|
||||
# 模块名称(带颜色)
|
||||
if logger_name:
|
||||
if self._colors and self._enable_module_colors:
|
||||
module_color = MODULE_COLORS.get(logger_name, "")
|
||||
if module_color:
|
||||
module_part = f"{module_color}[{logger_name}]{RESET_COLOR}"
|
||||
else:
|
||||
module_part = f"[{logger_name}]"
|
||||
else:
|
||||
module_part = f"[{logger_name}]"
|
||||
parts.append(module_part)
|
||||
|
||||
# 消息内容(确保转换为字符串)
|
||||
if isinstance(event, str):
|
||||
parts.append(event)
|
||||
elif isinstance(event, dict):
|
||||
# 如果是字典,格式化为可读字符串
|
||||
try:
|
||||
parts.append(json.dumps(event, ensure_ascii=False, indent=None))
|
||||
except (TypeError, ValueError):
|
||||
parts.append(str(event))
|
||||
else:
|
||||
# 其他类型直接转换为字符串
|
||||
parts.append(str(event))
|
||||
|
||||
# 处理其他字段
|
||||
extras = []
|
||||
for key, value in event_dict.items():
|
||||
if key not in ("timestamp", "level", "logger_name", "event"):
|
||||
# 确保值也转换为字符串
|
||||
if isinstance(value, (dict, list)):
|
||||
try:
|
||||
value_str = json.dumps(value, ensure_ascii=False, indent=None)
|
||||
except (TypeError, ValueError):
|
||||
value_str = str(value)
|
||||
else:
|
||||
value_str = str(value)
|
||||
extras.append(f"{key}={value_str}")
|
||||
|
||||
if extras:
|
||||
parts.append(" ".join(extras))
|
||||
|
||||
return " ".join(parts)
|
||||
|
||||
|
||||
# 配置标准logging以支持文件输出和压缩
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
@@ -27,21 +288,26 @@ logging.basicConfig(
|
||||
],
|
||||
)
|
||||
|
||||
structlog.configure(
|
||||
processors=[
|
||||
structlog.contextvars.merge_contextvars,
|
||||
structlog.processors.add_log_level,
|
||||
structlog.processors.StackInfoRenderer(),
|
||||
structlog.dev.set_exc_info,
|
||||
structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S", utc=False),
|
||||
# 根据输出类型选择不同的渲染器
|
||||
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
|
||||
],
|
||||
wrapper_class=structlog.stdlib.BoundLogger,
|
||||
context_class=dict,
|
||||
logger_factory=structlog.stdlib.LoggerFactory(),
|
||||
cache_logger_on_first_use=True,
|
||||
)
|
||||
def configure_structlog():
|
||||
"""配置structlog"""
|
||||
structlog.configure(
|
||||
processors=[
|
||||
structlog.contextvars.merge_contextvars,
|
||||
structlog.processors.add_log_level,
|
||||
structlog.processors.StackInfoRenderer(),
|
||||
structlog.dev.set_exc_info,
|
||||
structlog.processors.TimeStamper(fmt=get_timestamp_format(), utc=False),
|
||||
# 根据输出类型选择不同的渲染器
|
||||
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
|
||||
],
|
||||
wrapper_class=structlog.stdlib.BoundLogger,
|
||||
context_class=dict,
|
||||
logger_factory=structlog.stdlib.LoggerFactory(),
|
||||
cache_logger_on_first_use=True,
|
||||
)
|
||||
|
||||
# 配置structlog
|
||||
configure_structlog()
|
||||
|
||||
# 为文件输出配置JSON格式
|
||||
file_formatter = structlog.stdlib.ProcessorFormatter(
|
||||
@@ -58,12 +324,12 @@ file_formatter = structlog.stdlib.ProcessorFormatter(
|
||||
|
||||
# 为控制台输出配置可读格式
|
||||
console_formatter = structlog.stdlib.ProcessorFormatter(
|
||||
processor=structlog.dev.ConsoleRenderer(colors=True),
|
||||
processor=ModuleColoredConsoleRenderer(colors=True),
|
||||
foreign_pre_chain=[
|
||||
structlog.stdlib.add_logger_name,
|
||||
structlog.stdlib.add_log_level,
|
||||
structlog.stdlib.PositionalArgumentsFormatter(),
|
||||
structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S", utc=False),
|
||||
structlog.processors.TimeStamper(fmt=get_timestamp_format(), utc=False),
|
||||
structlog.processors.StackInfoRenderer(),
|
||||
structlog.processors.format_exc_info,
|
||||
],
|
||||
@@ -77,6 +343,42 @@ for handler in root_logger.handlers:
|
||||
else:
|
||||
handler.setFormatter(console_formatter)
|
||||
|
||||
# 立即配置日志系统,确保最早期的日志也使用正确格式
|
||||
def _immediate_setup():
|
||||
"""立即设置日志系统,在模块导入时就生效"""
|
||||
# 重新配置structlog
|
||||
configure_structlog()
|
||||
|
||||
# 清除所有已有的handler,重新配置
|
||||
root_logger = logging.getLogger()
|
||||
for handler in root_logger.handlers[:]:
|
||||
root_logger.removeHandler(handler)
|
||||
|
||||
# 重新添加配置好的handler
|
||||
root_logger.addHandler(logging.handlers.RotatingFileHandler(
|
||||
LOG_DIR / "app.log.jsonl",
|
||||
maxBytes=10 * 1024 * 1024,
|
||||
backupCount=5,
|
||||
encoding="utf-8",
|
||||
))
|
||||
root_logger.addHandler(logging.StreamHandler())
|
||||
|
||||
# 设置格式化器
|
||||
for handler in root_logger.handlers:
|
||||
if isinstance(handler, logging.handlers.RotatingFileHandler):
|
||||
handler.setFormatter(file_formatter)
|
||||
else:
|
||||
handler.setFormatter(console_formatter)
|
||||
|
||||
# 配置第三方库日志
|
||||
configure_third_party_loggers()
|
||||
|
||||
# 重新配置所有已存在的logger
|
||||
reconfigure_existing_loggers()
|
||||
|
||||
# 立即执行配置
|
||||
_immediate_setup()
|
||||
|
||||
raw_logger = structlog.get_logger()
|
||||
|
||||
binds: dict[str, Callable] = {}
|
||||
@@ -114,6 +416,134 @@ def configure_logging(
|
||||
root_logger.setLevel(getattr(logging, level.upper()))
|
||||
|
||||
|
||||
def set_module_color(module_name: str, color_code: str):
|
||||
"""为指定模块设置颜色
|
||||
|
||||
Args:
|
||||
module_name: 模块名称
|
||||
color_code: ANSI颜色代码,例如 '\033[92m' 表示亮绿色
|
||||
"""
|
||||
MODULE_COLORS[module_name] = color_code
|
||||
|
||||
|
||||
def get_module_colors():
|
||||
"""获取当前模块颜色配置"""
|
||||
return MODULE_COLORS.copy()
|
||||
|
||||
|
||||
def reload_log_config():
|
||||
"""重新加载日志配置"""
|
||||
global LOG_CONFIG
|
||||
LOG_CONFIG = load_log_config()
|
||||
|
||||
# 重新配置console渲染器
|
||||
root_logger = logging.getLogger()
|
||||
for handler in root_logger.handlers:
|
||||
if isinstance(handler, logging.StreamHandler) and not isinstance(handler, logging.handlers.RotatingFileHandler):
|
||||
# 这是控制台处理器,更新其格式化器
|
||||
handler.setFormatter(structlog.stdlib.ProcessorFormatter(
|
||||
processor=ModuleColoredConsoleRenderer(colors=True),
|
||||
foreign_pre_chain=[
|
||||
structlog.stdlib.add_logger_name,
|
||||
structlog.stdlib.add_log_level,
|
||||
structlog.stdlib.PositionalArgumentsFormatter(),
|
||||
structlog.processors.TimeStamper(fmt=get_timestamp_format(), utc=False),
|
||||
structlog.processors.StackInfoRenderer(),
|
||||
structlog.processors.format_exc_info,
|
||||
],
|
||||
))
|
||||
|
||||
# 重新配置第三方库日志
|
||||
configure_third_party_loggers()
|
||||
|
||||
# 重新配置所有已存在的logger
|
||||
reconfigure_existing_loggers()
|
||||
|
||||
|
||||
def get_log_config():
|
||||
"""获取当前日志配置"""
|
||||
return LOG_CONFIG.copy()
|
||||
|
||||
|
||||
def force_reset_all_loggers():
|
||||
"""强制重置所有logger,解决格式不一致问题"""
|
||||
# 清除所有现有的logger配置
|
||||
logging.getLogger().manager.loggerDict.clear()
|
||||
|
||||
# 重新配置根logger
|
||||
root_logger = logging.getLogger()
|
||||
root_logger.handlers.clear()
|
||||
|
||||
# 重新添加我们的handler
|
||||
root_logger.addHandler(logging.handlers.RotatingFileHandler(
|
||||
LOG_DIR / "app.log.jsonl",
|
||||
maxBytes=10 * 1024 * 1024,
|
||||
backupCount=5,
|
||||
encoding="utf-8",
|
||||
))
|
||||
root_logger.addHandler(logging.StreamHandler())
|
||||
|
||||
# 设置格式化器
|
||||
for handler in root_logger.handlers:
|
||||
if isinstance(handler, logging.handlers.RotatingFileHandler):
|
||||
handler.setFormatter(file_formatter)
|
||||
else:
|
||||
handler.setFormatter(console_formatter)
|
||||
|
||||
# 设置级别
|
||||
global_log_level = LOG_CONFIG.get("log_level", "INFO")
|
||||
root_logger.setLevel(getattr(logging, global_log_level.upper(), logging.INFO))
|
||||
|
||||
|
||||
def initialize_logging():
|
||||
"""手动初始化日志系统,确保所有logger都使用正确的配置
|
||||
|
||||
在应用程序的早期调用此函数,确保所有模块都使用统一的日志配置
|
||||
"""
|
||||
global LOG_CONFIG
|
||||
LOG_CONFIG = load_log_config()
|
||||
configure_third_party_loggers()
|
||||
reconfigure_existing_loggers()
|
||||
|
||||
# 输出初始化信息
|
||||
logger = get_logger("logger")
|
||||
log_level = LOG_CONFIG.get("log_level", "INFO")
|
||||
logger.info(f"日志系统已重新初始化,日志级别: {log_level},所有logger已统一配置")
|
||||
|
||||
|
||||
def force_initialize_logging():
|
||||
"""强制重新初始化整个日志系统,解决格式不一致问题"""
|
||||
global LOG_CONFIG
|
||||
LOG_CONFIG = load_log_config()
|
||||
|
||||
# 强制重置所有logger
|
||||
force_reset_all_loggers()
|
||||
|
||||
# 重新配置structlog
|
||||
configure_structlog()
|
||||
|
||||
# 配置第三方库
|
||||
configure_third_party_loggers()
|
||||
|
||||
# 输出初始化信息
|
||||
logger = get_logger("logger")
|
||||
log_level = LOG_CONFIG.get("log_level", "INFO")
|
||||
logger.info(f"日志系统已强制重新初始化,日志级别: {log_level},所有logger格式已统一")
|
||||
|
||||
|
||||
def show_module_colors():
|
||||
"""显示所有模块的颜色效果"""
|
||||
logger = get_logger("demo")
|
||||
print("\n=== 模块颜色展示 ===")
|
||||
|
||||
for module_name, color_code in MODULE_COLORS.items():
|
||||
# 临时创建一个该模块的logger来展示颜色
|
||||
demo_logger = structlog.get_logger(module_name).bind(logger_name=module_name)
|
||||
demo_logger.info(f"这是 {module_name} 模块的颜色效果")
|
||||
|
||||
print("=== 颜色展示结束 ===\n")
|
||||
|
||||
|
||||
def format_json_for_logging(data, indent=2, ensure_ascii=False):
|
||||
"""将JSON数据格式化为可读字符串
|
||||
|
||||
@@ -126,17 +556,9 @@ def format_json_for_logging(data, indent=2, ensure_ascii=False):
|
||||
str: 格式化后的JSON字符串
|
||||
"""
|
||||
if isinstance(data, str):
|
||||
try:
|
||||
# 如果是JSON字符串,先解析再格式化
|
||||
parsed_data = json.loads(data)
|
||||
return json.dumps(parsed_data, indent=indent, ensure_ascii=ensure_ascii)
|
||||
except json.JSONDecodeError:
|
||||
# 如果不是有效JSON,直接返回
|
||||
return data
|
||||
# 如果是JSON字符串,先解析再格式化
|
||||
parsed_data = json.loads(data)
|
||||
return json.dumps(parsed_data, indent=indent, ensure_ascii=ensure_ascii)
|
||||
else:
|
||||
# 如果是对象,直接格式化
|
||||
try:
|
||||
return json.dumps(data, indent=indent, ensure_ascii=ensure_ascii)
|
||||
except (TypeError, ValueError):
|
||||
# 如果无法序列化,返回字符串表示
|
||||
return str(data)
|
||||
return json.dumps(data, indent=indent, ensure_ascii=ensure_ascii)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[inner]
|
||||
version = "2.18.0"
|
||||
version = "2.19.0"
|
||||
|
||||
#----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读----
|
||||
#如果你想要修改配置文件,请在修改后将version的值进行变更
|
||||
@@ -183,7 +183,15 @@ max_length = 512 # 回复允许的最大长度
|
||||
max_sentence_num = 8 # 回复允许的最大句子数
|
||||
enable_kaomoji_protection = false # 是否启用颜文字保护
|
||||
|
||||
[log]
|
||||
date_style = "Y-m-d H:i:s" # 日期格式
|
||||
log_level_style = "lite" # 日志级别样式,可选FULL,compact,lite
|
||||
color_text = "title" # 日志文本颜色,可选none,title,full
|
||||
log_level = "INFO"
|
||||
|
||||
# 第三方库日志控制
|
||||
suppress_libraries = ["faiss","httpx", "urllib3", "asyncio", "websockets", "httpcore", "requests", "peewee", "openai"] # 完全屏蔽的库
|
||||
library_log_levels = { "aiohttp" = "WARNING"} # 设置特定库的日志级别
|
||||
|
||||
#下面的模型若使用硅基流动则不需要更改,使用ds官方则改成.env自定义的宏,使用自定义模型则选择定位相似的模型自己填写
|
||||
|
||||
|
||||
@@ -11,21 +11,4 @@ xxxxxxx_BASE_URL=https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
DEEP_SEEK_KEY=
|
||||
CHAT_ANY_WHERE_KEY=
|
||||
SILICONFLOW_KEY=
|
||||
xxxxxxx_KEY=
|
||||
|
||||
# 定义日志相关配置
|
||||
|
||||
# 精简控制台输出格式
|
||||
SIMPLE_OUTPUT=true
|
||||
|
||||
# 自定义日志的默认控制台输出日志级别
|
||||
CONSOLE_LOG_LEVEL=INFO
|
||||
|
||||
# 自定义日志的默认文件输出日志级别
|
||||
FILE_LOG_LEVEL=DEBUG
|
||||
|
||||
# 原生日志的控制台输出日志级别
|
||||
DEFAULT_CONSOLE_LOG_LEVEL=SUCCESS
|
||||
|
||||
# 原生日志的默认文件输出日志级别
|
||||
DEFAULT_FILE_LOG_LEVEL=DEBUG
|
||||
xxxxxxx_KEY=
|
||||
Reference in New Issue
Block a user