123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- import traceback
- from datetime import datetime
- import os
- from pathlib import Path
- from unitls.settings import FilePath
- path = FilePath("save_url")
- class ErrorLogger:
- """
- 错误日志记录器(单例模式,固定配置)
- 功能:
- - 自动创建日志目录和文件
- - 限制单个日志文件大小
- - 自动滚动保留最近备份
- - 最新错误显示在最前面
- - 包含详细时间戳和错误堆栈
- """
- # 固定配置(类属性)
- _DEFAULT_FILENAME = "app_errors.log"
- _DEFAULT_LOG_DIR = Path(f"{path}\logs") # 默认存储目录
- _DEFAULT_MAX_SIZE_KB = 1024 # 默认1MB大小限制
- _DEFAULT_BACKUP_COUNT = 5 # 默认保留5个备份
- _instance = None # 单例实例
- def __new__(cls):
- if cls._instance is None:
- cls._instance = super().__new__(cls)
- cls._instance.__init__() # 确保初始化只执行一次
- return cls._instance
- def __init__(self):
- if not hasattr(self, '_initialized'): # 防止重复初始化
- self.filename = self._DEFAULT_LOG_DIR / self._DEFAULT_FILENAME
- self.max_size = self._DEFAULT_MAX_SIZE_KB * 1024
- self.backup_count = self._DEFAULT_BACKUP_COUNT
- self._ensure_directory_exists()
- self._ensure_file_exists()
- self._initialized = True
- def _ensure_directory_exists(self):
- """确保日志目录存在"""
- try:
- self._DEFAULT_LOG_DIR.mkdir(parents=True, exist_ok=True)
- except Exception as e:
- raise RuntimeError(f"无法创建日志目录 {self._DEFAULT_LOG_DIR}: {e}")
- def _ensure_file_exists(self):
- """确保日志文件存在"""
- try:
- if not self.filename.exists():
- with open(self.filename, 'w', encoding='utf-8') as f:
- f.write("") # 创建空文件
- except Exception as e:
- raise RuntimeError(f"无法创建日志文件 {self.filename}: {e}")
- def _rotate_files(self):
- """执行日志文件滚动"""
- if self.filename.exists() and os.path.getsize(self.filename) >= self.max_size:
- try:
- # 删除最旧的备份
- oldest = f"{self.filename}.{self.backup_count}"
- if os.path.exists(oldest):
- os.remove(oldest)
- # 重命名其他备份
- for i in range(self.backup_count - 1, 0, -1):
- src = f"{self.filename}.{i}"
- if os.path.exists(src):
- os.rename(src, f"{self.filename}.{i + 1}")
- # 重命名当前日志
- os.rename(self.filename, f"{self.filename}.1")
- # 创建新日志文件
- self._ensure_file_exists()
- except Exception as e:
- print(f"日志滚动失败: {e}")
- def log_error(self, error_msg=None):
- """
- 记录错误信息
- :param error_msg: 可以是Exception对象或任意可转换为字符串的内容
- """
- self._rotate_files()
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] # 带毫秒的时间戳
- if isinstance(error_msg, Exception):
- error_details = "".join(traceback.format_exception(
- type(error_msg), error_msg, error_msg.__traceback__))
- else:
- error_details = str(error_msg) if error_msg else "Unknown error"
- log_entry = f"[{timestamp}]\n{error_details}\n{'=' * 50}\n"
- try:
- if os.path.exists(self.filename):
- with open(self.filename, 'r+', encoding='utf-8') as f:
- content = f.read()
- f.seek(0)
- f.write(log_entry + content)
- else:
- with open(self.filename, 'w', encoding='utf-8') as f:
- f.write(log_entry)
- except Exception as e:
- print(f"无法写入错误日志: {e}+\n+{error_msg}")
- # 创建单例实例(推荐直接使用这个实例)
- app_logger = ErrorLogger()
|