diff --git a/app/api/endpoints/system.py b/app/api/endpoints/system.py index 2db037f9..3a80b672 100644 --- a/app/api/endpoints/system.py +++ b/app/api/endpoints/system.py @@ -160,7 +160,7 @@ def get_message(token: str): @router.get("/logging", summary="实时日志") -def get_logging(token: str, length: int = 50): +def get_logging(token: str, length: int = 50, logfile: str = "moviepilot.log"): """ 实时获取系统日志 length = -1 时, 返回text/plain @@ -172,7 +172,7 @@ def get_logging(token: str, length: int = 50): detail="认证失败!", ) - log_path = settings.LOG_PATH / 'moviepilot.log' + log_path = settings.LOG_PATH / logfile def log_generator(): # 读取文件末尾50行,不使用tailer模块 diff --git a/app/log.py b/app/log.py index 085dadbb..53ddfa7a 100644 --- a/app/log.py +++ b/app/log.py @@ -1,6 +1,8 @@ +import inspect import logging from logging.handlers import RotatingFileHandler from pathlib import Path +from typing import Dict, Any import click @@ -31,27 +33,111 @@ class CustomFormatter(logging.Formatter): return super().format(record) -# DEBUG -logger = logging.getLogger() -if settings.DEBUG: - logger.setLevel(logging.DEBUG) -else: - logger.setLevel(logging.INFO) +class LoggerManager: + """ + 日志管理 + """ + # 管理所有的Logger + _loggers: Dict[str, Any] = {} + # 默认日志文件 + _default_log_file = "moviepilot.log" -# 终端日志 -console_handler = logging.StreamHandler() -console_handler.setLevel(logging.DEBUG) -console_formatter = CustomFormatter("%(leveltext)s%(filename)s - %(message)s") -console_handler.setFormatter(console_formatter) -logger.addHandler(console_handler) + @staticmethod + def __setup_logger(log_file: str): + """ + 设置日志 + log_file:日志文件相对路径 + """ + log_file_path = settings.LOG_PATH / log_file + if not log_file_path.parent.exists(): + log_file_path.parent.mkdir(parents=True, exist_ok=True) -# 文件日志 -file_handler = RotatingFileHandler(filename=settings.LOG_PATH / 'moviepilot.log', - mode='w', - maxBytes=5 * 1024 * 1024, - backupCount=3, - encoding='utf-8') -file_handler.setLevel(logging.INFO) -file_formater = CustomFormatter("【%(levelname)s】%(asctime)s - %(filename)s - %(message)s") -file_handler.setFormatter(file_formater) -logger.addHandler(file_handler) + # 创建新实例 + _logger = logging.getLogger(log_file_path.stem) + + # DEBUG + if settings.DEBUG: + _logger.setLevel(logging.DEBUG) + else: + _logger.setLevel(logging.INFO) + + # 移除已有的 handler,避免重复添加 + for handler in _logger.handlers: + _logger.removeHandler(handler) + + # 终端日志 + console_handler = logging.StreamHandler() + console_handler.setLevel(logging.DEBUG) + console_formatter = CustomFormatter("%(leveltext)s%(filename)s - %(message)s") + console_handler.setFormatter(console_formatter) + _logger.addHandler(console_handler) + + # 文件日志 + file_handler = RotatingFileHandler(filename=log_file_path, + mode='w', + maxBytes=5 * 1024 * 1024, + backupCount=3, + encoding='utf-8') + file_handler.setLevel(logging.INFO) + file_formater = CustomFormatter("【%(levelname)s】%(asctime)s - %(filename)s - %(message)s") + file_handler.setFormatter(file_formater) + _logger.addHandler(file_handler) + + return _logger + + def logger(self, path: str) -> logging.Logger: + """ + 获取模块的logger + :param path: 当前运行程序路径 + """ + filepath = Path(path) + + # 区分插件日志 + if "plugins" in filepath.parts: + # 使用插件日志文件 + plugin_name = filepath.parts[filepath.parts.index("plugins") + 1] + logfile = Path("plugins") / f"{plugin_name}.log" + else: + # 使用默认日志文件 + logfile = self._default_log_file + + # 获取调用者的模块的logger + _logger = self._loggers.get(logfile) + if not _logger: + _logger = self.__setup_logger(logfile) + self._loggers[logfile] = _logger + return _logger + + def info(self, msg, *args, **kwargs): + """ + 重载info方法,按模块区分输出 + """ + self.logger(inspect.stack()[1].filename).info(msg, *args, **kwargs) + + def debug(self, msg, *args, **kwargs): + """ + 重载debug方法,按模块区分输出 + """ + self.logger(inspect.stack()[1].filename).debug(msg, *args, **kwargs) + + def warning(self, msg, *args, **kwargs): + """ + 重载warning方法,按模块区分输出 + """ + self.logger(inspect.stack()[1].filename).warning(msg, *args, **kwargs) + + def error(self, msg, *args, **kwargs): + """ + 重载error方法,按模块区分输出 + """ + self.logger(inspect.stack()[1].filename).error(msg, *args, **kwargs) + + def critical(self, msg, *args, **kwargs): + """ + 重载critical方法,按模块区分输出 + """ + self.logger(inspect.stack()[1].filename).critical(msg, *args, **kwargs) + + +# 初始化公共日志 +logger = LoggerManager()