""" 霜云(Shimokumo) - 日志工具模块 支持彩色终端日志输出和文件日志记录。 提供DEBUG、INFO、WARNING、ERROR四个级别。 """ import logging import os import sys from datetime import datetime from typing import Optional class ColoredFormatter(logging.Formatter): """彩色日志格式化器""" # ANSI颜色代码 COLORS = { "DEBUG": "\033[36m", # 青色 "INFO": "\033[32m", # 绿色 "WARNING": "\033[33m", # 黄色 "ERROR": "\033[31m", # 红色 "CRITICAL": "\033[35m", # 紫色 } RESET = "\033[0m" def format(self, record: logging.LogRecord) -> str: """格式化日志记录,添加颜色""" color = self.COLORS.get(record.levelname, self.RESET) # 保存原始level名称 record.levelname_colored = f"{color}{record.levelname}{self.RESET}" return super().format(record) class ShimokumoLogger: """霜云专用日志管理器 提供统一的日志接口,支持彩色终端输出和文件记录。 用法: logger = ShimokumoLogger(log_level="DEBUG", log_file="app.log") logger.info("服务启动成功") logger.error("连接失败", exc_info=True) """ # 日志格式模板 CONSOLE_FORMAT = ( "\033[90m%(asctime)s\033[0m | " "%(levelname_colored)-8s | " "\033[90m%(name)s\033[0m | " "%(message)s" ) FILE_FORMAT = ( "%(asctime)s | %(levelname)-8s | %(name)s | %(message)s" ) DATE_FORMAT = "%Y-%m-%d %H:%M:%S" def __init__( self, name: str = "Shimokumo", log_level: str = "INFO", log_file: Optional[str] = None, console_output: bool = True, ): """ 初始化日志管理器。 Args: name: 日志器名称 log_level: 日志级别 (DEBUG/INFO/WARNING/ERROR) log_file: 日志文件路径,为None则不写入文件 console_output: 是否输出到终端 """ self.name = name self.log_level = getattr(logging, log_level.upper(), logging.INFO) self.log_file = log_file self._logger = self._setup_logger(console_output) def _setup_logger(self, console_output: bool) -> logging.Logger: """配置日志器""" logger = logging.getLogger(self.name) logger.setLevel(self.log_level) # 清除已有handler防止重复 logger.handlers.clear() # 终端输出handler if console_output: console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(self.log_level) console_formatter = ColoredFormatter( self.CONSOLE_FORMAT, datefmt=self.DATE_FORMAT, ) console_handler.setFormatter(console_formatter) logger.addHandler(console_handler) # 文件输出handler if self.log_file: log_dir = os.path.dirname(self.log_file) if log_dir: os.makedirs(log_dir, exist_ok=True) file_handler = logging.FileHandler( self.log_file, encoding="utf-8", mode="a", ) file_handler.setLevel(self.log_level) file_formatter = logging.Formatter( self.FILE_FORMAT, datefmt=self.DATE_FORMAT, ) file_handler.setFormatter(file_formatter) logger.addHandler(file_handler) return logger def debug(self, msg: str, **kwargs) -> None: """输出DEBUG级别日志""" self._logger.debug(msg, **kwargs) def info(self, msg: str, **kwargs) -> None: """输出INFO级别日志""" self._logger.info(msg, **kwargs) def warning(self, msg: str, **kwargs) -> None: """输出WARNING级别日志""" self._logger.warning(msg, **kwargs) def error(self, msg: str, **kwargs) -> None: """输出ERROR级别日志""" self._logger.error(msg, **kwargs) def critical(self, msg: str, **kwargs) -> None: """输出CRITICAL级别日志""" self._logger.critical(msg, **kwargs) def exception(self, msg: str, **kwargs) -> None: """输出异常信息(ERROR级别,包含堆栈跟踪)""" self._logger.exception(msg, **kwargs) @staticmethod def get_logger( name: str = "Shimokumo", log_level: str = "INFO", log_file: Optional[str] = None, ) -> "ShimokumoLogger": """ 工厂方法:获取或创建日志实例。 Args: name: 日志器名称 log_level: 日志级别 log_file: 日志文件路径 Returns: ShimokumoLogger实例 """ return ShimokumoLogger( name=name, log_level=log_level, log_file=log_file, ) def get_logger( name: str = "Shimokumo", log_level: str = "INFO", log_file: Optional[str] = None, ) -> ShimokumoLogger: """ 快捷函数:获取霜云日志器。 Args: name: 日志器名称 log_level: 日志级别 log_file: 日志文件路径 Returns: ShimokumoLogger实例 """ return ShimokumoLogger.get_logger(name, log_level, log_file)