使用dictConfig注册自定义Handler遇循环导入错误,Formatter却正常
自定义Handler循环导入报错,但自定义Formatter却正常的原因分析
我正尝试通过dictConfig为项目注册自定义Handler和自定义Formatter,仅注册自定义Handler时出现循环导入导致的错误,但Formatter可正常注册。这不符合逻辑,若存在循环导入两者都应失败,我遗漏了什么?
shared.logger.py 代码
import logging import logging.config import logging.handlers import settings class MyFormatter(logging.Formatter): def format(self, record: logging.LogRecord) -> str: original_fmt = self._style._fmt # 如果记录中添加了"caller_info"额外属性,修改格式以替换默认名称和函数名 if hasattr(record, "caller_info"): caller_info: dict = getattr(record, "caller_info") module = caller_info["module"] func_name = caller_info["funcname"] self._style._fmt = f"%(asctime)s - %(levelname)s - myapi.{module} - {func_name} - %(message)s" result = logging.Formatter.format(self, record) self._style._fmt = original_fmt return result class MyTimedRotatingFileHander(logging.handlers.TimedRotatingFileHandler): def rotation_filename(self, default_name: str) -> str: new_name = super().rotation_filename(default_name) return new_name.replace(".log", "") + ".log" def get_logger(name: str) -> logging.Logger: logging.config.dictConfig(settings.LOG_CONFIG) logger = logging.getLogger(f"myapi.{name}") return logger
settings.py 代码
import logging LOG_CONFIG = { "version": 1, "formatters": { "default": { "class": "shared.logger.MyFormatter", "format": "%(asctime)s - %(levelname)s - %(name)s - %(funcName)s - %(message)s", }, }, "handlers": { "file": { "class": "shared.logger.MyTimedRotatingFileHandler", "level": logging.DEBUG, "formatter": "default", "filename": "logs/myapi.log", "when": "d", "interval": 1, "encoding": "utf-8", "backupCount": 10, } }, "loggers": { "myapi": { "level": logging.DEBUG, "handlers": ["file"], "propagate": False, } }, }
报错信息
ModuleNotFoundError: No module named 'shared.logger.MyTimedRotatingFileHandler'; 'shared.logger' is not a package
原因解析
核心在于logging模块加载Formatter和Handler的时机差异:
- Formatter的加载逻辑:Formatter只会在实际输出日志、需要格式化记录时才会被实例化。也就是说,调用
get_logger初始化配置后,只要还没触发日志打印,Formatter就不会被真正加载,循环导入的问题不会暴露。 - Handler的加载逻辑:Handler在调用
dictConfig时就会被立即实例化——因为Handler需要提前完成资源初始化(比如打开日志文件)。此时shared.logger模块还处于加载过程中(它导入了settings,而settings又引用了shared.logger中的Handler),Python会认为shared.logger还不是一个完整的可导入包,从而抛出错误。
解决办法
- 延迟导入settings:把
import settings移到get_logger函数内部,避免模块加载阶段就触发循环依赖:def get_logger(name: str) -> logging.Logger: import settings # 内部导入,延迟加载配置 logging.config.dictConfig(settings.LOG_CONFIG) logger = logging.getLogger(f"myapi.{name}") return logger - 重构配置结构:将LOG_CONFIG单独放到一个独立的配置文件中,避免logger模块与settings的直接耦合,或者让logger模块通过参数接收配置,而非主动导入settings。
内容的提问来源于stack exchange,提问作者btonasse
相关产品推荐
相关产品推荐

