mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 07:27:15 +08:00
refactor(log): add support for configurable log settings in .env
This commit is contained in:
+79
-35
@@ -5,29 +5,69 @@ from pathlib import Path
|
|||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
from pydantic import BaseSettings
|
||||||
|
|
||||||
from app.core.config import settings
|
from app.utils.system import SystemUtils
|
||||||
|
|
||||||
# 日志级别颜色
|
|
||||||
|
class LogSettings(BaseSettings):
|
||||||
|
"""
|
||||||
|
日志设置
|
||||||
|
"""
|
||||||
|
# 是否为调试模式
|
||||||
|
DEBUG: bool = False
|
||||||
|
# 日志级别(DEBUG、INFO、WARNING、ERROR等)
|
||||||
|
LOG_LEVEL: str = "INFO"
|
||||||
|
# 日志文件最大大小(单位:MB)
|
||||||
|
LOG_MAX_FILE_SIZE: int = 5
|
||||||
|
# 备份的日志文件数量
|
||||||
|
LOG_BACKUP_COUNT: int = 3
|
||||||
|
# 控制台日志格式
|
||||||
|
LOG_CONSOLE_FORMAT: str = "%(leveltext)s%(message)s"
|
||||||
|
# 文件日志格式
|
||||||
|
LOG_FILE_FORMAT: str = "【%(levelname)s】%(asctime)s - %(message)s"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def LOG_PATH(self):
|
||||||
|
"""
|
||||||
|
获取日志存储路径
|
||||||
|
"""
|
||||||
|
return SystemUtils.get_config_path() / "logs"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def LOG_MAX_FILE_SIZE_BYTES(self):
|
||||||
|
"""
|
||||||
|
将日志文件大小转换为字节(MB -> Bytes)
|
||||||
|
"""
|
||||||
|
return self.LOG_MAX_FILE_SIZE * 1024 * 1024
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
case_sensitive = True
|
||||||
|
env_file = SystemUtils.get_config_path() / "app.env"
|
||||||
|
env_file_encoding = "utf-8"
|
||||||
|
|
||||||
|
|
||||||
|
# 日志级别颜色映射
|
||||||
level_name_colors = {
|
level_name_colors = {
|
||||||
logging.DEBUG: lambda level_name: click.style(str(level_name), fg="cyan"),
|
logging.DEBUG: lambda level_name: click.style(str(level_name), fg="cyan"),
|
||||||
logging.INFO: lambda level_name: click.style(str(level_name), fg="green"),
|
logging.INFO: lambda level_name: click.style(str(level_name), fg="green"),
|
||||||
logging.WARNING: lambda level_name: click.style(str(level_name), fg="yellow"),
|
logging.WARNING: lambda level_name: click.style(str(level_name), fg="yellow"),
|
||||||
logging.ERROR: lambda level_name: click.style(str(level_name), fg="red"),
|
logging.ERROR: lambda level_name: click.style(str(level_name), fg="red"),
|
||||||
logging.CRITICAL: lambda level_name: click.style(
|
logging.CRITICAL: lambda level_name: click.style(str(level_name), fg="bright_red"),
|
||||||
str(level_name), fg="bright_red"
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class CustomFormatter(logging.Formatter):
|
class CustomFormatter(logging.Formatter):
|
||||||
"""
|
"""
|
||||||
定义日志输出格式
|
自定义日志输出格式
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(self, fmt=None):
|
||||||
|
super().__init__(fmt)
|
||||||
|
|
||||||
def format(self, record):
|
def format(self, record):
|
||||||
seperator = " " * (8 - len(record.levelname))
|
separator = " " * (8 - len(record.levelname))
|
||||||
record.leveltext = level_name_colors[record.levelno](record.levelname + ":") + seperator
|
record.leveltext = level_name_colors[record.levelno](record.levelname + ":") + separator
|
||||||
return super().format(record)
|
return super().format(record)
|
||||||
|
|
||||||
|
|
||||||
@@ -35,15 +75,16 @@ class LoggerManager:
|
|||||||
"""
|
"""
|
||||||
日志管理
|
日志管理
|
||||||
"""
|
"""
|
||||||
# 管理所有的Logger
|
# 管理所有的 Logger
|
||||||
_loggers: Dict[str, Any] = {}
|
_loggers: Dict[str, Any] = {}
|
||||||
# 默认日志文件
|
# 默认日志文件名称
|
||||||
_default_log_file = "moviepilot.log"
|
_default_log_file = "moviepilot.log"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __get_caller():
|
def __get_caller():
|
||||||
"""
|
"""
|
||||||
获取调用者的文件名称与插件名称(如果是插件调用内置的模块, 也能写入到插件日志文件中)
|
获取调用者的文件名称与插件名称
|
||||||
|
如果是插件调用内置的模块, 也能写入到插件日志文件中
|
||||||
"""
|
"""
|
||||||
# 调用者文件名称
|
# 调用者文件名称
|
||||||
caller_name = None
|
caller_name = None
|
||||||
@@ -79,18 +120,17 @@ class LoggerManager:
|
|||||||
设置日志
|
设置日志
|
||||||
log_file:日志文件相对路径
|
log_file:日志文件相对路径
|
||||||
"""
|
"""
|
||||||
log_file_path = settings.LOG_PATH / log_file
|
log_file_path = log_settings.LOG_PATH / log_file
|
||||||
if not log_file_path.parent.exists():
|
log_file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
log_file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
# 创建新实例
|
# 创建新实例
|
||||||
_logger = logging.getLogger(log_file_path.stem)
|
_logger = logging.getLogger(log_file_path.stem)
|
||||||
|
|
||||||
# DEBUG
|
if log_settings.DEBUG:
|
||||||
if settings.DEBUG:
|
|
||||||
_logger.setLevel(logging.DEBUG)
|
_logger.setLevel(logging.DEBUG)
|
||||||
else:
|
else:
|
||||||
_logger.setLevel(logging.INFO)
|
loglevel = getattr(logging, log_settings.LOG_LEVEL.upper(), logging.INFO)
|
||||||
|
_logger.setLevel(loglevel)
|
||||||
|
|
||||||
# 移除已有的 handler,避免重复添加
|
# 移除已有的 handler,避免重复添加
|
||||||
for handler in _logger.handlers:
|
for handler in _logger.handlers:
|
||||||
@@ -98,18 +138,20 @@ class LoggerManager:
|
|||||||
|
|
||||||
# 终端日志
|
# 终端日志
|
||||||
console_handler = logging.StreamHandler()
|
console_handler = logging.StreamHandler()
|
||||||
console_formatter = CustomFormatter(f"%(leveltext)s%(message)s")
|
console_formatter = CustomFormatter(log_settings.LOG_CONSOLE_FORMAT)
|
||||||
console_handler.setFormatter(console_formatter)
|
console_handler.setFormatter(console_formatter)
|
||||||
_logger.addHandler(console_handler)
|
_logger.addHandler(console_handler)
|
||||||
|
|
||||||
# 文件日志
|
# 文件日志
|
||||||
file_handler = RotatingFileHandler(filename=log_file_path,
|
file_handler = RotatingFileHandler(
|
||||||
mode='w',
|
filename=log_file_path,
|
||||||
maxBytes=5 * 1024 * 1024,
|
mode="a",
|
||||||
backupCount=3,
|
maxBytes=log_settings.LOG_MAX_FILE_SIZE_BYTES,
|
||||||
encoding='utf-8')
|
backupCount=log_settings.LOG_BACKUP_COUNT,
|
||||||
file_formater = CustomFormatter(f"【%(levelname)s】%(asctime)s - %(message)s")
|
encoding="utf-8"
|
||||||
file_handler.setFormatter(file_formater)
|
)
|
||||||
|
file_formatter = CustomFormatter(log_settings.LOG_FILE_FORMAT)
|
||||||
|
file_handler.setFormatter(file_formatter)
|
||||||
_logger.addHandler(file_handler)
|
_logger.addHandler(file_handler)
|
||||||
|
|
||||||
return _logger
|
return _logger
|
||||||
@@ -120,7 +162,6 @@ class LoggerManager:
|
|||||||
:param method: 日志方法
|
:param method: 日志方法
|
||||||
:param msg: 日志信息
|
:param msg: 日志信息
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 获取调用者文件名和插件名
|
# 获取调用者文件名和插件名
|
||||||
caller_name, plugin_name = self.__get_caller()
|
caller_name, plugin_name = self.__get_caller()
|
||||||
# 区分插件日志
|
# 区分插件日志
|
||||||
@@ -138,45 +179,48 @@ class LoggerManager:
|
|||||||
self._loggers[logfile] = _logger
|
self._loggers[logfile] = _logger
|
||||||
# 调用logger的方法打印日志
|
# 调用logger的方法打印日志
|
||||||
if hasattr(_logger, method):
|
if hasattr(_logger, method):
|
||||||
method = getattr(_logger, method)
|
log_method = getattr(_logger, method)
|
||||||
method(f"{caller_name} - {msg}", *args, **kwargs)
|
log_method(f"{caller_name} - {msg}", *args, **kwargs)
|
||||||
|
|
||||||
def info(self, msg: str, *args, **kwargs):
|
def info(self, msg: str, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
重载info方法
|
输出信息级别日志
|
||||||
"""
|
"""
|
||||||
self.logger("info", msg, *args, **kwargs)
|
self.logger("info", msg, *args, **kwargs)
|
||||||
|
|
||||||
def debug(self, msg: str, *args, **kwargs):
|
def debug(self, msg: str, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
重载debug方法
|
输出调试级别日志
|
||||||
"""
|
"""
|
||||||
self.logger("debug", msg, *args, **kwargs)
|
self.logger("debug", msg, *args, **kwargs)
|
||||||
|
|
||||||
def warning(self, msg: str, *args, **kwargs):
|
def warning(self, msg: str, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
重载warning方法
|
输出警告级别日志
|
||||||
"""
|
"""
|
||||||
self.logger("warning", msg, *args, **kwargs)
|
self.logger("warning", msg, *args, **kwargs)
|
||||||
|
|
||||||
def warn(self, msg: str, *args, **kwargs):
|
def warn(self, msg: str, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
重载warn方法
|
输出警告级别日志(兼容)
|
||||||
"""
|
"""
|
||||||
self.logger("warning", msg, *args, **kwargs)
|
self.logger("warning", msg, *args, **kwargs)
|
||||||
|
|
||||||
def error(self, msg: str, *args, **kwargs):
|
def error(self, msg: str, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
重载error方法
|
输出错误级别日志
|
||||||
"""
|
"""
|
||||||
self.logger("error", msg, *args, **kwargs)
|
self.logger("error", msg, *args, **kwargs)
|
||||||
|
|
||||||
def critical(self, msg: str, *args, **kwargs):
|
def critical(self, msg: str, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
重载critical方法
|
输出严重错误级别日志
|
||||||
"""
|
"""
|
||||||
self.logger("critical", msg, *args, **kwargs)
|
self.logger("critical", msg, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
# 初始化公共日志
|
# 实例化日志设置
|
||||||
|
log_settings = LogSettings()
|
||||||
|
|
||||||
|
# 初始化日志管理
|
||||||
logger = LoggerManager()
|
logger = LoggerManager()
|
||||||
|
|||||||
@@ -471,3 +471,15 @@ class SystemUtils:
|
|||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
return src.drive == dest.drive
|
return src.drive == dest.drive
|
||||||
return os.stat(src).st_dev == os.stat(dest).st_dev
|
return os.stat(src).st_dev == os.stat(dest).st_dev
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_config_path() -> Path:
|
||||||
|
"""
|
||||||
|
获取配置路径
|
||||||
|
"""
|
||||||
|
if SystemUtils.is_docker():
|
||||||
|
return Path("/config")
|
||||||
|
elif SystemUtils.is_frozen():
|
||||||
|
return Path(sys.executable).parent / "config"
|
||||||
|
else:
|
||||||
|
return Path(__file__).parents[2] / "config"
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ HOST=0.0.0.0
|
|||||||
DEBUG=false
|
DEBUG=false
|
||||||
# 是否开发模式,打开后后台服务将不会启动
|
# 是否开发模式,打开后后台服务将不会启动
|
||||||
DEV=false
|
DEV=false
|
||||||
|
# 日志级别(DEBUG、INFO、WARNING、ERROR等),当DEBUG=true时,此配置项将被忽略,日志级别始终为DEBUG
|
||||||
|
LOG_LEVEL=INFO
|
||||||
# 数据库连接池的大小,可适当降低如10-50以减少I/O压力
|
# 数据库连接池的大小,可适当降低如10-50以减少I/O压力
|
||||||
DB_POOL_SIZE=100
|
DB_POOL_SIZE=100
|
||||||
# 数据库连接池最大溢出连接数,可适当降低如0以减少I/O压力
|
# 数据库连接池最大溢出连接数,可适当降低如0以减少I/O压力
|
||||||
|
|||||||
Reference in New Issue
Block a user