云计算百科
云计算领域专业知识百科平台

Python 3.12 logging - 15 - fileConfig - JSON

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时,最好设置为不传播,或者即使传播也要控制好传播的父级。

赞(0)
未经允许不得转载:网硕互联帮助中心 » Python 3.12 logging - 15 - fileConfig - JSON
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!