Python 开发技巧 · logging 最佳实践 —— 告别 print,搭建工程级日志系统

1. 为什么不用 print?

生产环境需要日志分级、持久化、格式化控制。print 一梭子下去,排查问题时两眼一抹黑。

2. 快速入门:logging 五大组件

组件 职责
Logger 日志入口,分等级
Handler 输出目的地(文件、终端、网络)
Formatter 格式化输出样式
Filter 按条件过滤
Level DEBUG → INFO → WARNING → ERROR → CRITICAL

3. 一行配置,即插即用

python 复制代码
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)  # 自动打印堆栈

4. 实用技巧:日志轮转

文件无限增大会撑爆磁盘,用 RotatingFileHandler 自动切割:

python 复制代码
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"
))

5. 按模块化分层

大型项目不要全局一把梭,每个模块独立 Logger:

python 复制代码
# utils/db.py
logger = logging.getLogger("app.db")

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

配合 logging.getLogger("app") 统一配置,子 Logger 继承父配置,省心。

6. 高级:JSON 日志(对接 ELK / 日志平台)

python 复制代码
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[0]:
            log_obj["exception"] = self.formatException(record.exc_info)
        return json.dumps(log_obj, ensure_ascii=False)

7. 避坑指南

坑 正确做法
logger = logging.getLogger() 空参 用 __name__,方便定位模块
f"user={user}" 占位符用 f-string 用 "user=%s" % user 惰性求值,避免不必要开销
重复添加 Handler(多次调用 basicConfig) 使用 not logger.handlers 判空,或用单例
中文乱码 FileHandler 加上 encoding="utf-8"
生产环境打印 DEBUG 日志 用环境变量控制级别:level=getattr(logging, os.getenv("LOG_LEVEL", "INFO"))

8. 开箱即用的模板

python 复制代码
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"
    )

    # 终端 Handler(始终输出 >= WARNING)
    console = logging.StreamHandler(sys.stdout)
    console.setLevel(logging.WARNING)
    console.setFormatter(fmt)
    logger.addHandler(console)

    # 文件 Handler 带轮转(输出全部级别)
    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 初始化完成")

小结

  • 小脚本 :logging.basicConfig 一行搞定
  • 中型项目:模块化 Logger + 文件轮转
  • 大型/微服务:JSON 格式 + JSON 收集至 ELK/Loki

从现在开始,所有 Python 项目都值得用 logging 替代 print,这是专业开发者与新手的分水岭。

相关推荐
段一凡-华北理工大学11 小时前
大模型与智能体在工业的应用~系列文章12:大模型 × 数字孪生 × 智能体的融合图景
大数据·人工智能·python·深度学习·大语言模型·python开发
流形填表11 小时前
多校区题库标准化:Excel模板与校验脚本
开发语言·python
做系统的大强11 小时前
脚本说 PASS、OS 读全零 —— 到底是谁在撒谎?
后端·操作系统
梦想画家11 小时前
SQLMesh Python 模型入门(二):依赖管理、引擎实战与内存优化
数据库·python·sqlmesh
code2cat12 小时前
【随笔】MCP资源更新订阅:通知到达以后,Agent怎样刷新旧资料
java·后端·开发工具·ai agent·mcp
全栈练习生12 小时前
大模型原理之 KV Cache
python·ai
天赐范式12 小时前
天赐范式第183天:让选择开始生效——育种者方程与选择生效条件
python·数字生命·天赐范式·动态运行时·育种者方程·选择压力·遗传力
benchmark_cc12 小时前
批量行情返回后,怎样把请求失败的股票和正常数据分开?
python·数据分析·pandas·量化交易·股票数据·quantdash
Harvil_13 小时前
任务跑一半,API 突然报“对话超长“断流:我给 Agent 修的紧急逃生通道
后端
用户EasyAdminBlazor13 小时前
EasyAdminBlazor SignalR 实时消息源码解析:从 NotificationHub 到站内消息
后端