跳到主要内容
Cowers://
全部文章
Python

Python Logging:从基础配置到工程实践

理解 Python logging 的 Logger、Handler、Formatter、Filter 以及日志轮转与性能实践。

logging 模块有四个核心对象:Logger(入口)、Handler(去哪)、Formatter(长什么样)、Filter(要不要)。一条日志从 logger.info() 出发,经 Filter 筛选,分发给多个 Handler,每个 Handler 再用自己的 Formatter 格式化后输出。


一、日志级别

从低到高:DEBUG(10)INFO(20)WARNING(30)ERROR(40)CRITICAL(50)

Logger 和 Handler 各自有独立的级别阈值,消息必须同时通过两道关卡才能输出。


二、最简用法(basicConfig)

import logging
 
logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s [%(levelname)s] %(name)s - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
    filename="app.log",   # 不写则输出到控制台
    filemode="a"
)
 
logging.debug("调试信息")
logging.info("普通信息")
logging.warning("警告")
logging.error("错误")
logging.critical("严重错误")

basicConfig 只在 root logger 没有 handler 时生效,适合脚本级快速使用。


三、生产级用法:自己创建 Logger

import logging
 
# 1. 创建 logger(名字通常用 __name__,形成层级关系)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
 
# 2. 创建 Handler
console_handler = logging.StreamHandler()        # 控制台
file_handler = logging.FileHandler("app.log")    # 文件
 
console_handler.setLevel(logging.INFO)   # 控制台只看 INFO+
file_handler.setLevel(logging.DEBUG)     # 文件记录所有
 
# 3. 创建 Formatter
fmt = logging.Formatter(
    "%(asctime)s [%(levelname)-8s] %(name)s:%(lineno)d - %(message)s"
)
console_handler.setFormatter(fmt)
file_handler.setFormatter(fmt)
 
# 4. 绑定到 logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)
 
logger.info("服务启动")
logger.debug("详细调试数据")

四、Formatter 常用字段

占位符 含义
%(asctime)s 时间
%(levelname)s 级别名(DEBUG/INFO…)
%(name)s logger 名称
%(filename)s / %(lineno)d 文件名 / 行号
%(message)s 日志内容
%(funcName)s 所在函数名
%(process)d / %(thread)d 进程/线程 ID

五、Logger 的层级与 propagate

Logger 名字里的 . 表示父子关系:

root
 └── myapp
      ├── myapp.db
      └── myapp.web
child = logging.getLogger("myapp.db")
# 默认 propagate=True,消息会向上传递到 myapp、再到 root
# 避免重复输出:
child.propagate = False

六、日志轮转(RotatingFileHandler)

from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
 
# 按大小轮转:最大 5MB,保留 3 个备份
handler = RotatingFileHandler(
    "app.log", maxBytes=5*1024*1024, backupCount=3
)
 
# 按时间轮转:每天午夜切割,保留 7 天
handler = TimedRotatingFileHandler(
    "app.log", when="midnight", interval=1, backupCount=7
)

七、用字典配置(推荐生产使用)

import logging
import logging.config
 
LOGGING_CONFIG = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "standard": {
            "format": "%(asctime)s [%(levelname)s] %(name)s - %(message)s"
        },
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "level": "INFO",
            "formatter": "standard",
        },
        "file": {
            "class": "logging.handlers.RotatingFileHandler",
            "level": "DEBUG",
            "formatter": "standard",
            "filename": "app.log",
            "maxBytes": 10485760,
            "backupCount": 5,
        },
    },
    "loggers": {
        "myapp": {
            "handlers": ["console", "file"],
            "level": "DEBUG",
            "propagate": False,
        }
    }
}
 
logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger("myapp")

这种方式把配置和代码分离,可以轻松从文件(JSON/YAML)加载。


八、记录异常信息

try:
    result = 1 / 0
except ZeroDivisionError:
    logger.exception("发生除零错误")   # 自动附带完整 traceback
    # 等价于:logger.error("...", exc_info=True)

九、一个常见坑

# ❌ 错误:字符串拼接在日志过滤前就执行了
logger.debug("用户数据: " + str(big_object))
 
# ✅ 正确:用 % 占位,只有真正要输出时才格式化
logger.debug("用户数据: %s", big_object)

整体用下来,小脚本用 basicConfig,库/框架只创建 Logger 不加 Handler(交给调用者配置),应用程序用 dictConfig 统一管理。有哪一块想深入聊?比如多进程日志安全、结构化日志(JSON),或者怎么在 Django/FastAPI 里集成?