Files
FRAISEMOE-Addons-Installer-…/source/utils/logger.py
hyb-oyqq 7d71ffe099 feat(core): 增强卸载处理程序的UI反馈和异常日志记录
- 在卸载处理程序中使用UI管理器显示和隐藏加载对话框,提升用户体验。
- 增加异常钩子,确保未捕获的异常能够记录到日志文件中,增强系统的稳定性和可追溯性。
2025-08-11 17:54:14 +08:00

188 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import logging
import datetime
import sys
import glob
import time
import traceback
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
from config.config import LOG_DIR, LOG_FILE, LOG_LEVEL, LOG_MAX_SIZE, LOG_BACKUP_COUNT, LOG_RETENTION_DAYS
from .url_censor import censor_url
class URLCensorFormatter(logging.Formatter):
"""自定义的日志格式化器用于隐藏日志消息中的URL"""
def format(self, record):
# 先使用原始的format方法格式化日志
formatted_message = super().format(record)
# 临时禁用URL隐藏直接返回原始消息
return formatted_message
# 然后对格式化后的消息进行URL审查已禁用
# return censor_url(formatted_message)
class Logger:
def __init__(self, filename, stream):
self.terminal = stream
try:
# 确保目录存在
log_dir = os.path.dirname(filename)
if log_dir and not os.path.exists(log_dir):
os.makedirs(log_dir, exist_ok=True)
print(f"已创建日志目录: {log_dir}")
# 以追加模式打开,避免覆盖现有内容
self.log = open(filename, "a", encoding="utf-8", errors="replace")
self.log.write("\n\n--- New logging session started ---\n\n")
except (IOError, OSError) as e:
# 如果打开文件失败,记录错误并使用空的写入操作
print(f"Error opening log file {filename}: {e}")
self.log = None
def write(self, message):
try:
# 临时禁用URL隐藏
# censored_message = censor_url(message)
censored_message = message # 直接使用原始消息
self.terminal.write(censored_message)
if self.log:
self.log.write(censored_message)
self.flush()
except Exception as e:
# 发生错误时记录到控制台
self.terminal.write(f"Error writing to log: {e}\n")
def flush(self):
try:
self.terminal.flush()
if self.log:
self.log.flush()
except Exception:
pass
def close(self):
try:
if self.log:
self.log.write("\n--- Logging session ended ---\n")
self.log.close()
self.log = None
except Exception:
pass
# 增加异常钩子,确保未捕获的异常也会记录到日志文件中
def log_uncaught_exceptions(exc_type, exc_value, exc_traceback):
"""处理未捕获的异常,记录到日志中"""
if issubclass(exc_type, KeyboardInterrupt):
# 对于键盘中断,使用默认处理
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
# 获取主日志记录器
logger = logging.getLogger('main')
# 格式化异常信息
lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
error_message = '未捕获的异常:\n' + ''.join(lines)
# 记录到日志中
logger.critical(error_message)
# 同时也显示在控制台
sys.__excepthook__(exc_type, exc_value, exc_traceback)
# 设置全局异常处理器
sys.excepthook = log_uncaught_exceptions
def cleanup_old_logs(retention_days=7):
"""清理超过指定天数的旧日志文件
Args:
retention_days: 日志保留天数默认7天
"""
try:
now = time.time()
cutoff = now - (retention_days * 86400) # 86400秒 = 1天
# 获取所有日志文件
log_files = glob.glob(os.path.join(LOG_DIR, "log-*.txt"))
for log_file in log_files:
# 检查文件修改时间
if os.path.getmtime(log_file) < cutoff:
try:
os.remove(log_file)
print(f"已删除过期日志: {log_file}")
except Exception as e:
print(f"删除日志文件失败 {log_file}: {e}")
except Exception as e:
print(f"清理旧日志文件时出错: {e}")
def setup_logger(name):
"""设置并返回一个命名的logger
使用统一的日志文件,添加日志轮转功能,实现自动清理过期日志
Args:
name: logger的名称
Returns:
logging.Logger: 配置好的logger对象
"""
# 创建logger
logger = logging.getLogger(name)
# 避免重复添加处理器
if logger.hasHandlers():
return logger
# 根据配置设置日志级别
log_level = getattr(logging, LOG_LEVEL.upper(), logging.DEBUG)
logger.setLevel(log_level)
# 确保日志目录存在
os.makedirs(LOG_DIR, exist_ok=True)
# 清理过期日志文件
cleanup_old_logs(LOG_RETENTION_DAYS)
# 创建主日志文件的轮转处理器
try:
# 确保主日志文件目录存在
log_file_dir = os.path.dirname(LOG_FILE)
if log_file_dir and not os.path.exists(log_file_dir):
os.makedirs(log_file_dir, exist_ok=True)
print(f"已创建主日志目录: {log_file_dir}")
# 使用RotatingFileHandler实现日志轮转
main_file_handler = RotatingFileHandler(
LOG_FILE,
maxBytes=LOG_MAX_SIZE,
backupCount=LOG_BACKUP_COUNT,
encoding="utf-8"
)
main_file_handler.setLevel(log_level)
except (IOError, OSError) as e:
print(f"无法创建主日志文件处理器: {e}")
main_file_handler = None
# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO) # 控制台只显示INFO以上级别
# 创建更详细的格式器,包括模块名、文件名和行号
formatter = URLCensorFormatter('%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s')
# 设置处理器的格式化器
console_handler.setFormatter(formatter)
if main_file_handler:
main_file_handler.setFormatter(formatter)
# 添加处理器到logger
logger.addHandler(console_handler)
if main_file_handler:
logger.addHandler(main_file_handler)
# 确保异常可以被正确记录
logger.propagate = True
return logger