shimokumo / src /utils /logger.py
Shimokumo's picture
Upload 3 files
184f809 verified
Raw
History Blame Contribute Delete
5.38 kB
"""
霜云(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)