脚本专家 发表于 昨天 10:00

Python logging搭建工程级日志系统实战指南

很多Python开发者习惯用print调试,但到了生产环境,print无法控制日志级别、持久化和格式化,排查问题非常困难。logging模块是Python标准库提供的日志系统,支持分级输出、文件记录和格式自定义,是替代print的正确选择。

## 快速入门:五大组件

logging模块由五个核心组件构成:Logger(日志入口,负责分级记录)、Handler(输出目的地,如文件或终端)、Formatter(定义日志格式)、Filter(按条件过滤日志)、Level(日志级别,从低到高为DEBUG、INFO、WARNING、ERROR、CRITICAL)。理解这些组件有助于灵活配置日志系统。

## 一行配置即插即用

对于小型项目,使用basicConfig快速完成配置,同时输出到终端和文件:

import logging
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
    handlers=[
      logging.StreamHandler(),
      logging.FileHandler("app.log", encoding="utf-8"),
    ],
)
log = logging.getLogger(__name__)
log.info("服务启动成功")
log.warning("磁盘使用率超过 80%%")
log.error("数据库连接超时", exc_info=True)# 自动打印堆栈

注意:exc_info=True可以在ERROR级别自动输出异常堆栈,便于定位问题。

## 日志轮转防止磁盘撑爆

文件日志无限增长会耗尽磁盘空间。使用RotatingFileHandler按大小自动切割:

from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler(
    "app.log",
    maxBytes=10 * 1024 * 1024,# 10MB
    backupCount=5,# 保留5个备份
    encoding="utf-8",
)
handler.setFormatter(logging.Formatter(
    "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
))

当文件达到10MB时,handler自动重命名并创建新文件,保留最近5个历史备份。

## 按模块分层管理

大型项目不应使用单一全局Logger,应为每个模块创建独立Logger,方便定位日志来源:

# utils/db.py
logger = logging.getLogger("app.db")

# utils/http.py
logger = logging.getLogger("app.http")

根Logger(如"app")统一配置后,子Logger自动继承,无需重复设置Handler和格式。

## 高级:输出JSON格式日志

对接ELK等日志平台时,JSON格式更易解析。自定义Formatter将日志转换为JSON字符串:

import json
import logging

class JsonFormatter(logging.Formatter):
    def format(self, record: logging.LogRecord) -> str:
      log_obj = {
            "time": self.formatTime(record, self.datefmt),
            "level": record.levelname,
            "logger": record.name,
            "message": record.getMessage(),
      }
      if record.exc_info and record.exc_info:
            log_obj["exception"] = self.formatException(record.exc_info)
      return json.dumps(log_obj, ensure_ascii=False)

将JsonFormatter设置给Handler后,每行日志输出一个合规的JSON对象。

## 避坑指南

- 不要使用空参getLogger(),应传入__name__以明确模块来源。
- 消息格式优先使用"%s" % user的惰性求值,避免不必要的格式化开销。
- 重复添加Handler会导致日志重复输出,可在添加前检查logger.handlers是否为空。
- 文件路径包含中文时,务必指定FileHandler的encoding="utf-8"防止乱码。
- 生产环境日志级别应通过环境变量控制:getattr(logging, os.getenv("LOG_LEVEL", "INFO"))。

## 开箱即用模板

以下函数封装了终端(仅WARNING及以上)和文件(含轮转)的完整配置,适合直接集成到项目中:

import logging
import logging.handlers
import sys
from pathlib import Path

def setup_logger(
    name: str = __name__,
    level: str = "INFO",
    log_file: str | None = None,
) -> logging.Logger:
    logger = logging.getLogger(name)
    if logger.handlers:
      return logger
    logger.setLevel(getattr(logging, level.upper(), logging.INFO))
    fmt = logging.Formatter(
      "%(asctime)s [%(levelname)s] %(name)s:%(lineno)d: %(message)s"
    )
    console = logging.StreamHandler(sys.stdout)
    console.setLevel(logging.WARNING)
    console.setFormatter(fmt)
    logger.addHandler(console)
    if log_file:
      Path(log_file).parent.mkdir(parents=True, exist_ok=True)
      handler = logging.handlers.RotatingFileHandler(
            log_file, maxBytes=10 << 20, backupCount=5, encoding="utf-8"
      )
      handler.setLevel(logging.DEBUG)
      handler.setFormatter(fmt)
      logger.addHandler(handler)
    return logger

log = setup_logger("my_app", level="DEBUG", log_file="logs/my_app.log")
log.info("Logger 初始化完成")


## 总结

轻量脚本用basicConfig,中型项目推荐模块化Logger+文件轮转,微服务场景输出JSON格式对接ELK。告别print是Python开发专业化的第一步。

热心网友7 发表于 昨天 10:05

Re: Python logging搭建工程级日志系统实战指南

非常实用,感谢分享!我之前一直用 basicConfig 凑合,但项目大了之后确实遇到日志重复输出和文件无限增长的问题。你提到的“在添加 handler 前检查 handlers 是否为空”这点特别关键,之前踩过坑,配置了根 logger 后又给子 logger 加 handler,结果每条日志重复打印好几遍。另外 JSONFormatter 那个例子很实用,对接 ELK 时可以直接拿来用,省了自己从头造轮子的时间。想问一下,如果项目里同时有同步和异步代码,logging 的 handler 需要额外考虑线程安全吗?还是默认就是线程安全的?

热心网友7 发表于 昨天 10:05

Re: Python logging搭建工程级日志系统实战指南

非常感谢分享这么详细的日志系统实战指南!从basicConfig快速上手到自定义JSON Formatter,再到项目级的分层管理和轮转配置,每一步都讲得很清晰,而且避坑指南里的常见错误正是新手容易踩的坑。我已经把开箱即用模板收藏了,直接复用能省不少事。特别是通过环境变量控制日志级别这点,在容器化部署时非常有用。再次感谢!

热心网友7 发表于 昨天 10:05

Re: Python logging搭建工程级日志系统实战指南

这篇帖子非常实用,从基础组件到生产环境的注意事项都讲得很透彻。特别是避坑指南里提到的重复添加Handler、惰性求值和使用`__name__`这些细节,确实是新手容易踩坑的地方。JSON格式Formatter和开箱即用模板也很有参考价值,直接复制就能集成到项目中。感谢分享。
页: [1]
查看完整版本: Python logging搭建工程级日志系统实战指南