diff --git a/app/core/config.py b/app/core/config.py index 66e5d2dd..0b2b76b2 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -10,7 +10,7 @@ from typing import Any, Dict, List, Optional, Tuple, Type from dotenv import set_key from pydantic import BaseModel, BaseSettings, validator, Field -from app.log import logger +from app.log import logger, log_settings, LogConfigModel from app.utils.system import SystemUtils from app.utils.url import UrlUtils @@ -244,7 +244,7 @@ class ConfigModel(BaseModel): TOKENIZED_SEARCH: bool = False -class Settings(BaseSettings, ConfigModel): +class Settings(BaseSettings, ConfigModel, LogConfigModel): """ 系统配置类 """ @@ -406,6 +406,8 @@ class Settings(BaseSettings, ConfigModel): # 仅成功更新配置时,才更新内存 if success: setattr(self, key, converted_value) + if hasattr(log_settings, key): + setattr(log_settings, key, converted_value) return success, message return True, "" except Exception as e: @@ -416,8 +418,14 @@ class Settings(BaseSettings, ConfigModel): 更新多个配置项 """ results = {} + log_updated = False for k, v in env.items(): results[k] = self.update_setting(k, v) + if hasattr(log_settings, k): + log_updated = True + # 本次更新存在日志配置项更新,需要重新加载日志配置 + if log_updated: + logger.update_loggers() return results @property diff --git a/app/log.py b/app/log.py index 26a27ae2..54d5d1d6 100644 --- a/app/log.py +++ b/app/log.py @@ -1,19 +1,24 @@ import inspect import logging +import os from logging.handlers import RotatingFileHandler from pathlib import Path from typing import Dict, Any, Optional import click -from pydantic import BaseSettings +from pydantic import BaseSettings, BaseModel from app.utils.system import SystemUtils -class LogSettings(BaseSettings): +class LogConfigModel(BaseModel): """ - 日志设置 + Pydantic 配置模型,描述所有配置项及其类型和默认值 """ + + class Config: + extra = "ignore" # 忽略未定义的配置项 + # 配置文件目录 CONFIG_DIR: Optional[str] = None # 是否为调试模式 @@ -29,6 +34,12 @@ class LogSettings(BaseSettings): # 文件日志格式 LOG_FILE_FORMAT: str = "【%(levelname)s】%(asctime)s - %(message)s" + +class LogSettings(BaseSettings, LogConfigModel): + """ + 日志设置类 + """ + @property def CONFIG_PATH(self): return SystemUtils.get_config_path(self.CONFIG_DIR) @@ -121,10 +132,12 @@ class LoggerManager: return caller_name or "log.py", plugin_name @staticmethod - def __setup_logger(log_file: str): + def __setup_logger(log_file: str, plugin_name: str = None): """ 设置日志 - log_file:日志文件相对路径 + + :param log_file:日志文件相对路径 + :param plugin_name: 插件名称 """ log_file_path = log_settings.LOG_PATH / log_file log_file_path.parent.mkdir(parents=True, exist_ok=True) @@ -134,6 +147,22 @@ class LoggerManager: if log_settings.DEBUG: _logger.setLevel(logging.DEBUG) + + # Todo: 允许每个插件设置独立的日志级别,用于单个插件的调试,不必使用全局影响全局日志级别,产生大量无用日志 + # elif plugin_name: + # # 从数据库中获取插件的日志等级,不存在则用None + # plugin_log_level = + # + # # 插件日志等级存在且合法,则使用插件日志等级 + # if plugin_log_level and plugin_log_level.upper() in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']: + # loglevel = getattr(logging, plugin_log_level.upper(), logging.INFO) + # _logger.setLevel(loglevel) + # # 插件日志等级不存在,则使用全局日志等级 + # else: + # loglevel = getattr(logging, log_settings.LOG_LEVEL.upper(), logging.INFO) + # _logger.setLevel(loglevel) + + # 全局日志等级 else: loglevel = getattr(logging, log_settings.LOG_LEVEL.upper(), logging.INFO) _logger.setLevel(loglevel) @@ -162,6 +191,26 @@ class LoggerManager: return _logger + def update_loggers(self): + """ + 更新日志实例 + """ + _new_loggers: Dict[str, Any] = {} + for log_file, _logger in self._loggers.items(): + # 移除已有的 handler,避免重复添加 + for handler in _logger.handlers: + _logger.removeHandler(handler) + plugin_name = None + # 如果不是默认日志文件,则拆分出插件名称 + if log_file != self._default_log_file: + log_file_name = os.path.basename(log_file) + plugin_name = os.path.splitext(log_file_name)[0] + # 重新设置日志实例 + _new_logger = self.__setup_logger(log_file=log_file, plugin_name=plugin_name) + _new_loggers[log_file] = _new_logger + + self._loggers = _new_loggers + def logger(self, method: str, msg: str, *args, **kwargs): """ 获取模块的logger @@ -181,7 +230,7 @@ class LoggerManager: # 获取调用者的模块的logger _logger = self._loggers.get(logfile) if not _logger: - _logger = self.__setup_logger(logfile) + _logger = self.__setup_logger(log_file=logfile, plugin_name=plugin_name) self._loggers[logfile] = _logger # 调用logger的方法打印日志 if hasattr(_logger, method):