mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-21 12:52:26 +00:00
refactor(frontend): 统一表格高度属性绑定方式
fix(backend): 修复租户管理菜单图标错误 style(backend): 优化启动banner显示格式 refactor(backend): 重构日志系统使用loguru替代原生logging feat(backend): 增加租户名称校验规则 refactor(backend): 优化应用启动流程和日志初始化 docs(frontend): 更新租户管理页面标题和描述
This commit is contained in:
+99
-73
@@ -1,90 +1,116 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import re
|
||||
from loguru import logger
|
||||
|
||||
from app.config.setting import settings
|
||||
from app.utils.common_util import worship
|
||||
|
||||
class InterceptHandler(logging.Handler):
|
||||
"""
|
||||
日志拦截处理器:将所有 Python 标准日志重定向到 Loguru
|
||||
|
||||
工作原理:
|
||||
1. 继承自 logging.Handler
|
||||
2. 重写 emit 方法处理日志记录
|
||||
3. 将标准库日志转换为 Loguru 格式
|
||||
"""
|
||||
def emit(self, record: logging.LogRecord) -> None:
|
||||
# 尝试获取日志级别名称
|
||||
try:
|
||||
level = logger.level(record.levelname).name
|
||||
except ValueError:
|
||||
level = record.levelno
|
||||
|
||||
class AppLogger:
|
||||
"""应用级日志管理器:一次性配置 + 获取。"""
|
||||
# 获取调用帧信息,增加None检查
|
||||
frame, depth = logging.currentframe(), 2
|
||||
if frame is not None:
|
||||
while frame and frame.f_code.co_filename == logging.__file__:
|
||||
frame = frame.f_back
|
||||
depth += 1
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._logger = logging.getLogger(__name__)
|
||||
self._configured = False
|
||||
|
||||
def _create_file_handler(self, stem: str, level: int, log_dir: Path, formatter: logging.Formatter) -> TimedRotatingFileHandler:
|
||||
file_path = log_dir / f"{stem}.log"
|
||||
handler = TimedRotatingFileHandler(
|
||||
filename=str(file_path),
|
||||
when=settings.WHEN,
|
||||
interval=settings.INTERVAL,
|
||||
backupCount=settings.BACKUPCOUNT,
|
||||
encoding=settings.ENCODING,
|
||||
# 使用 Loguru 记录日志
|
||||
logger.opt(depth=depth, exception=record.exc_info).log(
|
||||
level,
|
||||
record.getMessage()
|
||||
)
|
||||
handler.setLevel(level)
|
||||
handler.setFormatter(formatter)
|
||||
handler.suffix = "_%Y-%m-%d.log" # 设置正确的后缀格式
|
||||
|
||||
def namer(default_name: str) -> str:
|
||||
# 统一处理轮转后的文件名,确保格式为stem_YYYY-MM-DD.log
|
||||
file_name = Path(default_name).name
|
||||
# 提取日期部分
|
||||
base_name = file_name.split('.')[0] # 获取基本名称(不包含扩展名)
|
||||
date_match = re.search(r'(\d{4}-\d{2}-\d{2})', base_name)
|
||||
if date_match:
|
||||
date_part = date_match.group(1)
|
||||
return f"{stem}_{date_part}.log"
|
||||
|
||||
# 如果没有找到日期部分,返回原始名称
|
||||
return default_name
|
||||
def setup_logging():
|
||||
"""
|
||||
配置日志系统
|
||||
|
||||
功能:
|
||||
1. 控制台彩色输出
|
||||
2. 文件日志轮转
|
||||
3. 错误日志单独存储
|
||||
4. 异步日志记录
|
||||
"""
|
||||
# 添加上下文信息
|
||||
logger.configure(extra={"app_name": "FastapiAdmin"})
|
||||
# 步骤1:移除默认处理器
|
||||
logger.remove()
|
||||
|
||||
def rotator(source: str, dest: str) -> None:
|
||||
# 确保目录存在
|
||||
Path(dest).parent.mkdir(parents=True, exist_ok=True)
|
||||
# 重命名文件
|
||||
Path(source).rename(dest)
|
||||
|
||||
handler.namer = namer
|
||||
handler.rotator = rotator
|
||||
return handler
|
||||
# 步骤2:定义日志格式
|
||||
log_format = (
|
||||
# 时间信息
|
||||
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
|
||||
# 日志级别,居中对齐
|
||||
"<level>{level: <8}</level> | "
|
||||
# 文件、函数和行号
|
||||
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - "
|
||||
# 日志消息
|
||||
"<level>{message}</level>"
|
||||
)
|
||||
|
||||
def configure(self) -> logging.Logger:
|
||||
if self._configured:
|
||||
return self._logger
|
||||
# 步骤3:配置控制台输出
|
||||
logger.add(
|
||||
sys.stdout,
|
||||
format=log_format,
|
||||
level="DEBUG" if settings.DEBUG else "INFO",
|
||||
enqueue=True, # 启用异步写入
|
||||
backtrace=True, # 显示完整的异常回溯
|
||||
diagnose=True, # 显示变量值等诊断信息
|
||||
colorize=True # 启用彩色输出
|
||||
)
|
||||
|
||||
# 基础设置
|
||||
self._logger.setLevel(settings.LOGGER_LEVEL)
|
||||
self._logger.handlers.clear()
|
||||
self._logger.propagate = False
|
||||
# 步骤4:创建日志目录
|
||||
log_dir = Path(settings.LOGGER_DIR)
|
||||
# 确保日志目录存在,如果不存在则创建
|
||||
log_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 目录与格式
|
||||
log_dir = Path(settings.LOGGER_DIR)
|
||||
log_dir.mkdir(parents=True, exist_ok=True)
|
||||
formatter = logging.Formatter(settings.LOGGER_FORMAT)
|
||||
# 步骤5:配置常规日志文件
|
||||
logger.add(
|
||||
str(log_dir / "info.log"),
|
||||
format=log_format,
|
||||
level="INFO",
|
||||
rotation="00:00", # 每天午夜轮转
|
||||
retention=settings.LOG_RETENTION_DAYS,
|
||||
compression="gz",
|
||||
encoding=settings.ENCODING,
|
||||
enqueue=True
|
||||
)
|
||||
|
||||
# 文件处理器
|
||||
self._logger.addHandler(self._create_file_handler("info", logging.INFO, log_dir, formatter))
|
||||
self._logger.addHandler(self._create_file_handler("error", logging.ERROR, log_dir, formatter))
|
||||
# 步骤6:配置错误日志文件
|
||||
logger.add(
|
||||
str(log_dir / "error.log"),
|
||||
format=log_format,
|
||||
level="ERROR",
|
||||
rotation="00:00", # 每天午夜轮转
|
||||
retention=settings.LOG_RETENTION_DAYS,
|
||||
compression="gz",
|
||||
encoding=settings.ENCODING,
|
||||
enqueue=True,
|
||||
backtrace=True,
|
||||
diagnose=True
|
||||
)
|
||||
|
||||
# 控制台处理器
|
||||
console = logging.StreamHandler()
|
||||
console.setLevel(settings.LOGGER_LEVEL)
|
||||
console.setFormatter(formatter)
|
||||
self._logger.addHandler(console)
|
||||
|
||||
self._configured = True
|
||||
return self._logger
|
||||
|
||||
def get_logger(self) -> logging.Logger:
|
||||
return self.configure()
|
||||
|
||||
def get_logger() -> logging.Logger:
|
||||
AL = AppLogger()
|
||||
return AL.get_logger()
|
||||
|
||||
|
||||
# 模块级兼容实例
|
||||
logger = get_logger()
|
||||
# 步骤7:配置标准库日志
|
||||
logging.basicConfig(handlers=[InterceptHandler()], level=settings.LOGGER_LEVEL, force=True)
|
||||
logger_name_list = [name for name in logging.root.manager.loggerDict]
|
||||
# 步骤8:配置第三方库日志
|
||||
for logger_name in logger_name_list:
|
||||
_logger = logging.getLogger(logger_name)
|
||||
_logger.handlers = [InterceptHandler()]
|
||||
_logger.propagate = False
|
||||
|
||||
Reference in New Issue
Block a user