| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- import logging
- import sys
- import traceback
- from datetime import datetime
- from pathlib import Path
- from Config.Config import Config
- class Logger:
- def __init__(self, log_path: str = None, log_name: str = None):
- """
- 初始化日志记录器
- Args:
- log_path: 日志存储路径,默认为项目根目录下的Log文件夹
- log_name: 日志文件名,默认为当前日期时间
- """
- # 设置默认日志路径为项目根目录下的Log文件夹
- if log_path is None:
- project_root = self._find_project_root()
- log_path = project_root / "Log"
- # 创建日志目录(如果不存在)
- self.log_path = Path(log_path)
- self.log_path.mkdir(parents=True, exist_ok=True)
- # 设置日志文件名
- if log_name is None:
- log_name = f"log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
- self.log_file = self.log_path / log_name
- # 配置日志记录器
- self.logger = logging.getLogger(__name__)
- self.logger.setLevel(Config.log_level)
- # 清除现有处理器(避免重复记录)
- self.logger.handlers.clear()
- # 创建格式化器
- formatter = logging.Formatter(Config.log_format)
- # 文件处理器(记录所有级别日志)
- file_handler = logging.FileHandler(self.log_file, encoding='utf-8')
- file_handler.setLevel(logging.DEBUG)
- file_handler.setFormatter(formatter)
- self.logger.addHandler(file_handler)
- # 控制台处理器(仅记录INFO及以上级别)
- console_handler = logging.StreamHandler()
- console_handler.setLevel(logging.INFO)
- console_handler.setFormatter(formatter)
- self.logger.addHandler(console_handler)
- # 设置异常钩子,捕获未处理的异常
- sys.excepthook = self._handle_exception
- # 重定向标准输出和标准错误
- self._redirect_stdout_stderr()
- def _find_project_root(self):
- """查找项目根目录"""
- # 查找包含.git、.project、setup.py或requirements.txt等标记文件的目录
- current_dir = Path(__file__).resolve().parent
- # 向上查找直到找到项目根目录标记
- for parent in [current_dir] + list(current_dir.parents):
- if any((parent / marker).exists() for marker in
- ['.git', '.project', 'setup.py', 'requirements.txt', 'pyproject.toml']):
- return parent
- # 如果找不到标记文件,则使用当前工作目录
- return Path.cwd()
- def _redirect_stdout_stderr(self):
- """
- 重定向标准输出和标准错误到日志
- :return:
- """
- class StreamToLogger:
- def __init__(self, logger, level):
- self.logger = logger
- self.level = level
- self.linebuf = ''
- def write(self, buf):
- for line in buf.rstrip().splitlines():
- if line.strip(): # 忽略空行
- self.logger.log(self.level, line.rstrip())
- def flush(self):
- pass
- # 重定向标准输出到INFO级别
- sys.stdout = StreamToLogger(self.logger, logging.INFO)
- # 重定向标准错误到ERROR级别
- sys.stderr = StreamToLogger(self.logger, logging.ERROR)
- def _handle_exception(self, exc_type, exc_value, exc_traceback):
- """处理未捕获的异常"""
- # 忽略KeyboardInterrupt,以便控制台可以正常退出
- if issubclass(exc_type, KeyboardInterrupt):
- sys.__excepthook__(exc_type, exc_value, exc_traceback)
- return
- self.logger.error(
- "未捕获的异常:",
- exc_info=(exc_type, exc_value, exc_traceback)
- )
- def debug(self, msg):
- self.logger.debug(msg)
- def info(self, msg):
- self.logger.info(msg)
- def warning(self, msg):
- self.logger.warning(msg)
- def error(self, msg):
- self.logger.error(msg)
- def critical(self, msg):
- self.logger.critical(msg)
- def exception(self, msg, exc_info=True):
- """记录异常信息,包括堆栈跟踪"""
- self.logger.exception(msg, exc_info=exc_info)
- def log_exception(self, e, context=""):
- """记录异常及其上下文信息"""
- error_msg = f"{context}: {type(e).__name__}: {str(e)}"
- self.error(error_msg)
- self.error("堆栈跟踪:\n" + "".join(traceback.format_tb(e.__traceback__)))
- # 使用示例
- if __name__ == "__main__":
- # 创建日志记录器实例(使用默认路径)
- logger = Logger(log_name="my_app.log")
- # 记录不同级别的日志
- logger.debug("这是一条调试信息")
- logger.info("程序启动成功")
- logger.warning("磁盘空间不足")
- # 模拟一个异常
- try:
- result = 1 / 0
- except Exception as e:
- logger.log_exception(e, "除法运算时发生错误")
- # 模拟控制台输出
- print("这是一条普通的控制台输出")
- # 模拟控制台错误输出
- sys.stderr.write("这是一条错误输出\n")
- # 模拟未捕获的异常
- def cause_exception():
- raise ValueError("这是一个未捕获的异常")
- # 调用会抛出异常的函数
- cause_exception()
- logger.info("程序执行完成")
|