Python 3.12.10 logging fileConfig – jsonFile 配置详解
在 Python 的 logging 模块中,通过 JSON 文件配合 dictConfig 可以灵活地配置日志系统,特别是利用点号分隔的日志器名称实现父子继承关系。本文将提供两个完整的 JSON 配置示例,详细解析每个配置项,并展示父子 Logger 的行为。
Demo 1:基础父子 Logger 配置
本示例演示一个父 Logger app 和一个子 Logger app.sub。父 Logger 输出到控制台,子 Logger 输出到文件,并观察日志传播。
1.1 JSON 配置文件 demo1.json
{
"version": 1,
"disable_existing_loggers": false,
"formatters": {
"simple": {
"format": "%(asctime)s – %(name)s – %(levelname)s – %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "DEBUG",
"formatter": "simple",
"stream": "ext://sys.stdout"
},
"file": {
"class": "logging.FileHandler",
"level": "INFO",
"formatter": "simple",
"filename": "demo1.log",
"encoding": "utf8"
}
},
"loggers": {
"app": {
"level": "DEBUG",
"handlers": ["console"],
"propagate": true
},
"app.sub": {
"level": "INFO",
"handlers": ["file"],
"propagate": true
}
},
"root": {
"level": "WARNING",
"handlers": ["console"]
}
}
1.2 Python 代码 demo1.py
import logging.config
import json
def main():
with open('demo1.json', 'r', encoding='utf-8') as f:
config = json.load(f)
logging.config.dictConfig(config)
parent = logging.getLogger('app')
child = logging.getLogger('app.sub')
print("=== 父 Logger 记录 ===")
parent.debug("父 DEBUG")
parent.info("父 INFO")
print("\\n=== 子 Logger 记录 ===")
child.debug("子 DEBUG (被子级别过滤)") # 子级别 INFO,所以不输出
child.info("子 INFO")
child.warning("子 WARNING")
if __name__ == '__main__':
main()
1.3 运行结果与分析
控制台输出:
不清楚为什么这样的输出呢,以我的理解,因为RootLogger的级别是WARNING,所以传播到Root时,不应该输出DEBUG和INFO的日志,为什么这样呢,这是意外传播了吗?这个是logging的Bug吗?欢迎评论一下呀
=== 父 Logger 记录 ===
2026-02-26 xx:xx:xx – app – DEBUG – 父 DEBUG
2026-02-26 xx:xx:xx – app – DEBUG – 父 DEBUG
2026-02-26 xx:xx:xx – app – INFO – 父 INFO
2026-02-26 xx:xx:xx – app – INFO – 父 INFO
=== 子 Logger 记录 ===
2026-02-26 xx:xx:xx – app.sub – INFO – 子 INFO
2026-02-26 xx:xx:xx – app.sub – INFO – 子 INFO
2026-02-26 xx:xx:xx – app.sub – WARNING – 子 WARNING
2026-02-26 xx:xx:xx – app.sub – WARNING – 子 WARNING
文件 demo1.log 内容:
2025-02-26 xx:xx:xx – app.sub – INFO – 子 INFO
2025-02-26 xx:xx:xx – app.sub – WARNING – 子 WARNING
分析:
- 父 Logger app 的 DEBUG 和 INFO 日志均通过控制台输出(因其处理器级别为 DEBUG)。
- 子 Logger app.sub 的 DEBUG 日志被自身级别 INFO 拒绝,因此无输出。
- 子 Logger 的 INFO 和 WARNING 日志同时输出到文件(自身处理器)和控制台(因为 propagate: true,传播到父 Logger,父的控制台处理器输出它们)。注意控制台中这两条日志的 name 是 app.sub,说明日志记录的名称保持不变。
- 根 Logger 未参与,因为子 Logger 设置了 propagate: true,但父 Logger 已经处理了传播,根不会收到(除非父也传播,但本例父未设置 propagate,默认为 true,所以父也会传播给根,但根级别为 WARNING,子 INFO 低于 WARNING 不会被根处理,因此无重复)。实际上,父 Logger 的 propagate 默认为 true,它会传播给根,但根级别为 WARNING,所以子 INFO 不会在根输出。为了避免混淆,通常将父或子设置为 propagate: false 来控制传播链。 重复输出了,为什么???
Demo 2:多层父子 Logger 与自定义格式化器
本示例展示三层 Logger:company、company.dept、company.dept.team,并引入自定义格式化器和过滤器,演示更复杂的继承和控制。
2.1 JSON 配置文件 demo2.json
{
"version": 1,
"disable_existing_loggers": false,
"formatters": {
"detailed": {
"format": "%(asctime)s [%(levelname)s] %(name)s – %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S"
},
"compact": {
"format": "%(levelname)s: %(message)s"
}
},
"filters": {
"warning_only": {
"()": "__main__.WarningOnlyFilter"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "INFO",
"formatter": "detailed",
"stream": "ext://sys.stdout"
},
"dept_file": {
"class": "logging.FileHandler",
"level": "DEBUG",
"formatter": "detailed",
"filename": "dept.log",
"encoding": "utf8"
},
"team_file": {
"class": "logging.FileHandler",
"level": "DEBUG",
"formatter": "compact",
"filename": "team.log",
"encoding": "utf8",
"filters": ["warning_only"]
}
},
"loggers": {
"company": {
"level": "DEBUG",
"handlers": ["console"],
"propagate": false
},
"company.dept": {
"level": "INFO",
"handlers": ["dept_file"],
"propagate": true
},
"company.dept.team": {
"level": "DEBUG",
"handlers": ["team_file"],
"propagate": true
}
},
"root": {
"level": "ERROR",
"handlers": ["console"]
}
}
2.2 自定义过滤器类(需在 Python 代码中定义)
# demo2.py 中包含以下类定义
class WarningOnlyFilter(logging.Filter):
def filter(self, record):
return record.levelno == logging.WARNING
2.3 Python 代码 demo2.py
import logging
import logging.config
import json
# 定义过滤器类(必须在 dictConfig 前定义)
class WarningOnlyFilter(logging.Filter):
def filter(self, record):
return record.levelno == logging.WARNING
def main():
with open('demo2.json', 'r', encoding='utf-8') as f:
config = json.load(f)
logging.config.dictConfig(config)
company = logging.getLogger('company')
dept = logging.getLogger('company.dept')
team = logging.getLogger('company.dept.team')
print("=== company 日志 ===")
company.debug("company DEBUG")
company.info("company INFO")
company.warning("company WARNING")
print("\\n=== dept 日志 ===")
dept.debug("dept DEBUG (被dept级别过滤)")
dept.info("dept INFO")
dept.warning("dept WARNING")
print("\\n=== team 日志 ===")
team.debug("team DEBUG")
team.info("team INFO")
team.warning("team WARNING")
if __name__ == '__main__':
main()
2.4 运行结果与分析
控制台输出(由 company 的 console 处理器和根处理器产生):
=== company 日志 ===
2026-02-26 xx:xx:xx [INFO] company – company INFO
2026-02-26 xx:xx:xx [WARNING] company – company WARNING
=== dept 日志 ===
2026-02-26 xx:xx:xx [INFO] company.dept – dept INFO
2026-02-26 xx:xx:xx [WARNING] company.dept – dept WARNING
=== team 日志 ===
2026-02-26 xx:xx:xx [INFO] company.dept.team – team INFO
2026-02-26 xx:xx:xx [WARNING] company.dept.team – team WARNING
文件 dept.log 内容:
2026-02-26 xx:xx:xx [INFO] company.dept – dept INFO
2026-02-26 xx:xx:xx [WARNING] company.dept – dept WARNING
2026-02-26 xx:xx:xx [DEBUG] company.dept.team – team DEBUG
2026-02-26 xx:xx:xx [INFO] company.dept.team – team INFO
2026-02-26 xx:xx:xx [WARNING] company.dept.team – team WARNING
文件 team.log 内容:
WARNING: team WARNING
分析:
- company 自身级别 DEBUG,处理器 console 输出所有级别到控制台。propagate: false 阻止其日志向上传播。
- dept 级别 INFO,因此其 DEBUG 被自身过滤。INFO 和 WARNING 同时输出到文件 dept.log(自身处理器)和控制台(传播到 company,company 的 console 处理器输出)。注意 dept 的日志传播到 company 后,由 company 的处理器输出到控制台,且日志名称保持为 company.dept。
- team 级别 DEBUG,所有级别日志输出到 team.log,但由于 team_file 处理器附加了 warning_only 过滤器,只有 WARNING 级别被写入 team.log。同时,team 的日志传播到 dept,dept 的处理器 dept_file 会记录它们(因为 dept_file 级别 DEBUG,且无过滤器),因此 dept.log 中包含 team 的所有日志。传播继续到 company,再由 company 的 console 输出到控制台。
- 根 Logger 级别 ERROR,没有参与,因为所有日志传播到 company 后停止(company 的 propagate: false),根未收到。
三、配置项详细解析
3.1 顶层键
| version | 配置版本,必须为 1。 | 1 |
| disable_existing_loggers | 是否禁用调用 dictConfig 前已存在的非根日志器。默认 true,通常设为 false 避免影响第三方库。 | false |
| incremental | 是否增量应用配置,false 表示完全替换。 | false |
| formatters | 定义格式化器的字典。 | {…} |
| filters | 定义过滤器的字典。 | {…} |
| handlers | 定义处理器的字典。 | {…} |
| loggers | 定义非根日志器的字典,键为日志器名称。 | {…} |
| root | 定义根日志器的字典。 | {…} |
3.2 formatters 子字典
每个格式化器包含:
| format | 日志格式字符串,使用 LogRecord 属性占位符。 | "%(asctime)s – %(message)s" |
| datefmt | 时间格式,与 time.strftime() 兼容。 | "%Y-%m-%d %H:%M:%S" |
| style | 格式化风格,%(默认)、{ 或 $。 | "%" |
| class | 自定义格式化器类,默认为 logging.Formatter。 | "myapp.formatters.CustomFormatter" |
| () | 特殊键,用于指定可调用对象(如工厂),其他键作为参数传递。 | "()": "myapp.formatters.CustomFormatter" |
3.3 filters 子字典
每个过滤器必须包含 () 键指定可调用对象(通常是类),其他键作为参数传递。例如:
"warning_only": {
"()": "__main__.WarningOnlyFilter"
}
内置的 logging.Filter 接受一个 name 参数用于按日志器名称过滤。
3.4 handlers 子字典
每个处理器包含:
| class | 处理器类的完整导入路径。 | "logging.StreamHandler" |
| level | 处理器级别,低于此级别的日志被忽略。 | "INFO" |
| formatter | 关联的格式化器名称。 | "detailed" |
| filters | 过滤器名称列表。 | ["warning_only"] |
| 其他键 | 传递给处理器构造器的关键字参数,如 filename、maxBytes 等。 | 视具体处理器而定 |
常用处理器及其参数:
- StreamHandler:stream(如 ext://sys.stdout)
- FileHandler:filename、mode(默认 'a')、encoding
- RotatingFileHandler:filename、maxBytes、backupCount、encoding
- TimedRotatingFileHandler:filename、when、interval、backupCount、encoding
3.5 loggers 子字典
每个日志器(非根)包含:
| level | 日志器级别。 | "DEBUG" |
| handlers | 处理器名称列表。 | ["console", "file"] |
| propagate | 是否将日志传播给父级,默认为 true。 | true |
| filters | 过滤器名称列表。 | [] |
3.6 root 字典
根日志器配置,与 loggers 格式相同,但只有一个。例如:
"root": {
"level": "WARNING",
"handlers": ["console"]
}
3.7 外部对象引用 ext://
在配置值中,可以使用 ext:// 前缀引用 Python 全局对象,如 "stream": "ext://sys.stdout" 会被解析为 sys.stdout 对象。
四、注意事项
- 父子 Logger 命名:通过点号分隔的名称自动形成层级,无需额外声明。
- 传播控制:propagate 属性决定日志是否向上传递。设为 false 可避免重复或意外传播。
- 过滤器定义:自定义过滤器类必须在调用 dictConfig 前定义,否则会引发 ImportError。
- disable_existing_loggers:强烈建议设为 false,防止在配置前已创建的日志器(如第三方库)被禁用。
- JSON 注释:标准 JSON 不支持注释,但可以通过预处理去除注释或使用 YAML 格式。
通过以上两个 demo 和详细解析,你应该能够熟练使用 JSON 配置 logging 模块,并灵活运用父子 Logger 的特性来构建复杂应用的日志系统。在配置Logger时,最好设置为不传播,或者即使传播也要控制好传播的父级。
网硕互联帮助中心



评论前必须登录!
注册