You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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还不是一个完整的可导入包,从而抛出错误。

解决办法

  1. 延迟导入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
    
  2. 重构配置结构:将LOG_CONFIG单独放到一个独立的配置文件中,避免logger模块与settings的直接耦合,或者让logger模块通过参数接收配置,而非主动导入settings。

内容的提问来源于stack exchange,提问作者btonasse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 10:07:37