Python 日志最佳实践:从 print 到结构化、可观测的生产级日志

举报
yd_268144123 发表于 2026/10/29 09:52:24 2026/10/29
【摘要】 一、为什么该告别 print很多脚本起步时都用 print 打日志,简单直接。但一旦进入生产环境,print 的问题就暴露了:它没有级别、没法按需开关、不能自动切分文件、无法带上时间/位置信息,更无法把日志结构化后交给采集系统检索。Python 标准库自带 logging,只要用对,就能做到:分级输出、按大小/时间轮转、输出 JSON 便于检索、给每条日志附加请求上下文。本文把这些实践一次...

一、为什么该告别 print

很多脚本起步时都用 print 打日志,简单直接。但一旦进入生产环境,print 的问题就暴露了:它没有级别、没法按需开关、不能自动切分文件、无法带上时间/位置信息,更无法把日志结构化后交给采集系统检索。

Python 标准库自带 logging,只要用对,就能做到:分级输出、按大小/时间轮转、输出 JSON 便于检索、给每条日志附加请求上下文。本文把这些实践一次讲清楚。

二、logging 的四个核心对象

  • Logger:日志入口,代码里通过 logging.getLogger(__name__) 获取,按名字组织成树。
  • Handler:决定日志写到哪里(控制台、文件、Syslog…),一个 Logger 可挂多个 Handler。
  • Formatter:决定日志长什么样(纯文本或 JSON)。
  • Filter:在日志真正输出前做过滤或注入额外字段(例如注入 request_id)。

记住一句话:Logger 负责“要不要记”,Handler 负责“记到哪”,Formatter 负责“长什么样”。

三、最小可用配置

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger(__name__)

logger.info("service started")
logger.warning("disk usage %.1f%%", 86.5)

注意:logger.info("disk usage %s", value) 采用惰性格式化,只有该级别真正输出时才会拼字符串,比 f-string 更省。

四、结构化日志:直接输出 JSON

线上做检索、聚合、告警时,纯文本很难解析。自定义一个 JSON Formatter 即可:

import json, logging

class JsonFormatter(logging.Formatter):
    def format(self, record: logging.LogRecord) -> str:
        payload = {
            "ts": self.formatTime(record, "%Y-%m-%dT%H:%M:%S%z"),
            "level": record.levelname,
            "logger": record.name,
            "msg": record.getMessage(),
            "file": record.filename,
            "line": record.lineno,
        }
        if record.exc_info:
            payload["exc"] = self.formatException(record.exc_info)
        return json.dumps(payload, ensure_ascii=False)

handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
logger = logging.getLogger("app")
logger.addHandler(handler)
logger.setLevel(logging.INFO)

结构化之后,日志平台就能按 level、logger、ts 直接聚合与检索。

五、日志轮转:别让磁盘被写满

单文件一直写迟早撑爆磁盘。用 RotatingFileHandler(按大小)或 TimedRotatingFileHandler(按时间):

from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler

# 按大小:单文件 50MB,保留 5 个备份
h1 = RotatingFileHandler("app.log", maxBytes=50 * 1024 * 1024,
                         backupCount=5, encoding="utf-8")

# 按天:每天切分,保留 14 天
h2 = TimedRotatingFileHandler("app.log", when="midnight",
                              backupCount=14, encoding="utf-8")

实务上:容器环境优先把日志打到 stdout,交给采集侧处理;物理机/裸进程才需要文件轮转。

六、上下文追踪:给每条日志带上 request_id

排查一个请求的多条日志时,靠 request_id 串起来最省事。用 contextvars + Filter 实现:

import contextvars, uuid, logging

request_id = contextvars.ContextVar("request_id", default="-")

class RequestIdFilter(logging.Filter):
    def filter(self, record):
        record.request_id = request_id.get()
        return True

# 在请求入口设置
token = request_id.set(uuid.uuid4().hex[:8])

fmt = logging.Formatter("%(asctime)s %(levelname)s [%(request_id)s] %(message)s")
h = logging.StreamHandler()
h.setFormatter(fmt)
h.addFilter(RequestIdFilter())
logger = logging.getLogger("app")
logger.addHandler(h)
logger.setLevel(logging.INFO)

之后同一请求链路里的每条日志都会带同一个 request_id,检索时按它过滤即可。

七、分级与命名规范

  • DEBUG:仅本地排查用,线上不开。
  • INFO:关键业务节点(启动、请求完成、任务成功)。
  • WARNING:可恢复的异常(重试成功、降级)。
  • ERROR:需要人工介入的失败。
  • CRITICAL:服务不可用。

Logger 命名用 logging.getLogger(__name__),自然形成 package.module 的层级,便于按模块单独调级别。

八、采集到华为云 LTS

把日志打到 stdout / 文件后,可用华为云日志服务(LTS)做采集:在 ECS 上安装 ICAgent,配置采集路径或容器 stdout,即可在 LTS 控制台按关键字、level、request_id 检索,并配置告警规则。JSON 结构化日志在这里的优势最明显——字段可直接作为检索维度。

九、几个常见坑

  1. Handler 重复添加:模块被多次 import 或函数里反复 addHandler,会导致一条日志打印多次。初始化只做一次。
  2. root logger 乱用:业务代码用 getLogger(__name__),不要直接 logging.info() 往 root 打。
  3. 多进程写同一文件:多个 worker 同时写会错乱,容器环境统一写 stdout 更稳。
  4. 格式化开销:用 %s 惰性格式化,别在 info() 里先 f-string 拼好再传。
  5. 异常只打 message:用 logger.exception(...) 或在 exc_info=True 时保留堆栈,否则排查时没有调用栈。

十、总结

一套可落地的 Python 日志方案 = 分层 Logger + 轮转 Handler + JSON Formatter + request_id 上下文注入 + 采集平台。把这几块标准化成项目模板后,日志就从“调试用的 print”变成了真正的可观测性资产:出问题时能按级别和请求链路快速定位,而不是在一堆文本里肉眼 grep。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。