Python 现代开发学习笔记

(六)记录器、处理器与输出位置

1. 跟踪一条记录经过的筛选

一条 INFO 记录从 readingapp.services 发出,先检查记录器的有效等级;通过后交给 handler。handler 再按自己的等级筛选,由 formatter 排版,最后送到终端或文件。propagate=True 会把记录交给祖先记录器的 handler。

INFO 和 DEBUG 分别经过记录器、终端处理器与文件处理器的过程

例如记录器等级为 DEBUG,终端 handler 为 WARNING,文件 handler 为 DEBUG:INFO 只进文件;WARNING 同时进终端和文件;DEBUG 也只进文件。向祖先传播时,不会再按祖先记录器自身的等级重新筛一次,handler 的等级仍然参与判断。把同一 handler 同时挂在子记录器和根记录器上,还会重复输出。logging 接口与传播

# 项目文件:debug_demo/log_routes.py
# 一个记录器接两个处理器:终端显示警告,内存文本接收 DEBUG 以上记录。
import io
import logging

logger = logging.getLogger("route_demo")
logger.setLevel(logging.DEBUG)
logger.propagate = False
memory = io.StringIO()
console = logging.StreamHandler()
console.setLevel(logging.WARNING)
saved = logging.StreamHandler(memory)
saved.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(levelname)s %(message)s")
for handler in (console, saved):
    handler.setFormatter(formatter)
    logger.addHandler(handler)
try:
    logger.debug("检查字段")
    logger.info("导入 2 本")
    logger.warning("标签重复")
    print(memory.getvalue().splitlines())
finally:
    for handler in (console, saved):
        logger.removeHandler(handler)
        handler.close()

终端日志只有 WARNING 标签重复;print 得到 ['DEBUG 检查字段', 'INFO 导入 2 本', 'WARNING 标签重复']。StringIO 沿用第五章的内存文本接口。这里移除并关闭自己建立的处理器,再次运行不会遗留重复的输出通道。

2. 在应用入口配置文件日志

本章项目使用一份 UTF-8 文件日志。RotatingFileHandler 达到指定大小后轮换备份文件,避免同一文件一直增长:

# 项目文件:quality_project/src/readingapp/logging_setup.py
# 入口配置根记录器;模块只调用 getLogger,不在导入时创建文件。
import logging
from logging.handlers import RotatingFileHandler
from pathlib import Path


def configure_logging(path: Path, debug: bool = False) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    handler = RotatingFileHandler(path, maxBytes=1_000_000, backupCount=2, encoding="utf-8")
    logging.basicConfig(
        level=logging.DEBUG if debug else logging.INFO,
        format="%(asctime)s %(levelname)s %(name)s %(message)s",
        handlers=[handler],
        force=True,
    )

maxBytes 是轮换的大小阈值,backupCount=2 保留两份轮换文件。force=True 移除并关闭根记录器已有的 handler,让这次入口配置生效;basicConfig 默认遇到已有 handler 时不重新配置。因此这里由应用入口调用,测试普通业务函数时不调用它,以便 pytest 接收日志。

笔记正文与全部题解 · Ctrl / ⌘ K