| """ |
| 霜云(Shimokumo) - 日志工具模块 |
| |
| 支持彩色终端日志输出和文件日志记录。 |
| 提供DEBUG、INFO、WARNING、ERROR四个级别。 |
| """ |
|
|
| import logging |
| import os |
| import sys |
| from datetime import datetime |
| from typing import Optional |
|
|
|
|
| class ColoredFormatter(logging.Formatter): |
| """彩色日志格式化器""" |
|
|
| |
| 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) |
| |
| 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) |
| |
| logger.handlers.clear() |
|
|
| |
| 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) |
|
|
| |
| 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) |
|
|