
本文详解如何在 Python 包中通过 dictConfig 正确初始化日志配置,解决子模块日志不输出的问题,涵盖配置文件位置、字典键名规范、格式字段合法性及 uvicorn 兼容性等关键细节。
本文详解如何在 python 包中通过 `dictconfig` 正确初始化日志配置,解决子模块日志不输出的问题,涵盖配置文件位置、字典键名规范、格式字段合法性及 `uvicorn` 兼容性等关键细节。
在 Python 包开发中,若希望所有子模块(如 core.utils)自动继承统一的日志配置,必须确保日志系统在包被首次导入时即完成初始化——而这要求配置代码必须置于 core/__init__.py 中,而非 core/__main__.py。后者仅在执行 python -m core 时触发,对常规 import core 无效,导致后续模块调用 logging.getLogger(__name__) 时仍使用未配置的默认 logger,从而静默丢弃日志。
其次,logging.config.dictConfig() 对配置字典的键名有严格要求。常见错误是将 formatter 的格式字符串键误写为 "fmt",而标准协议要求使用 "format"(注意拼写)。若使用 "fmt",该 formatter 将被忽略,handler 回退至默认格式,且可能引发无声失败。
第三,日志格式字符串中的占位符需符合 Python 标准 logging 模块规范。例如 %(levelprefix)s 并非内置字段——它是 Uvicorn 自定义 Formatter(uvicorn.logging.DefaultFormatter)引入的扩展字段,原生 logging.Formatter 不识别。若未显式指定该 formatter 类,直接使用 %(levelprefix)s 将导致格式化异常或字段被忽略。推荐做法是:
- 若不依赖 Uvicorn:改用标准字段 %(levelname)s;
- 若需保留 levelprefix(如绿色 INFO 文本):取消注释并启用 Uvicorn formatter:
"formatters": {
"default": {
"()": "uvicorn.logging.DefaultFormatter", # 启用 Uvicorn 自定义 formatter
"format": "%(levelprefix)s %(asctime)s %(name)s:%(lineno)d: %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
},
},
完整、可工作的 core/__init__.py 示例:
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
# core/__init__.py
import logging
import logging.config
from pydantic import BaseModel
class BaseLoggerConfig(BaseModel):
LOG_FORMAT: str = "%(levelname)s %(asctime)s %(name)s:%(lineno)d: %(message)s"
DATEFMT: str = "%Y-%m-%d %H:%M:%S"
LOG_LEVEL: int = logging.INFO
version: int = 1
disable_existing_loggers: bool = False
formatters: dict = {
"default": {
"format": LOG_FORMAT, # ✅ 关键:必须是 "format",不是 "fmt"
"datefmt": DATEFMT,
}
}
handlers: dict = {
"default": {
"formatter": "default",
"class": "logging.StreamHandler",
"stream": "ext://sys.stderr",
}
}
loggers: dict = {}
def __init__(self, name: str, **data):
super().__init__(**data)
# 动态注册当前包名 logger,确保 core.* 子模块可继承
self.loggers[name] = {
"handlers": ["default"],
"level": self.LOG_LEVEL,
"propagate": False,
}
LOG_CONFIG = BaseLoggerConfig("core") # ? 建议显式传入包名 "core"
logging.config.dictConfig(LOG_CONFIG.dict())
⚠️ 注意事项:
- BaseLoggerConfig 实例需调用 .dict() 方法转换为纯字典(Pydantic v2+),否则 dictConfig 会报错;
- name="core" 确保根 logger 名为 "core",使 core.utils 等子模块 logger 自动继承(因 Python 日志继承链为 core.utils → core → root);
- 所有子模块应使用 logging.getLogger(__name__),而非硬编码名称,以保证层级匹配;
- 避免在多个地方重复调用 dictConfig,否则可能覆盖或冲突。
配置生效后,test.py 运行将输出结构化日志:
INFO 2024-11-30 11:06:28 core.utils:8: I am a log from core.utils
这表明日志已成功沿 core.utils → core 层级继承配置,并由 StreamHandler 输出到 stderr。掌握这一模式,即可在复杂包结构中实现集中、可维护、可复用的日志治理。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










