Python 结构化日志实践:从 print 调试到可检索的日志系统

举报
yd_257599996 发表于 2026/11/01 06:13:14 2026/11/01
【摘要】 用 Python 标准库 logging 搭建结构化日志:统一 JSON 格式、按模块分级、通过 contextvars 携带请求标识,并做好轮转与敏感信息脱敏,让线上问题可检索、可定位。

上线后排查问题时,散落在各处的 print 往往帮不上忙:没有时间、没有级别、没有请求上下文,出了问题只能凭印象翻代码。本文用 Python 标准库 logging,从零搭建一套可检索的结构化日志方案,适合中小型服务直接复用。

一、为什么 print 不够用

print 只能输出文本,无法区分 DEBUG、INFO、WARNING、ERROR;无法统一格式;无法按模块控制级别;更难把日志交给日志平台按字段检索。logging 的核心是三件套:Logger 负责发日志,Handler 负责输出到哪里,Formatter 负责输出成什么样子。

二、最小可用的结构化配置

先定义一个 JSON 友好的 Formatter,把时间、级别、模块、消息和关键字段固定下来:

import json
import logging

class JsonFormatter(logging.Formatter):
    def format(self, record):
        payload = {
            "time": self.formatTime(record, "%Y-%m-%dT%H:%M:%S"),
            "level": record.levelname,
            "module": record.name,
            "message": record.getMessage(),
        }
        payload.update(getattr(record, "fields", {}) or {})
        return json.dumps(payload, ensure_ascii=False)

def setup_logging(level=logging.INFO):
    handler = logging.StreamHandler()
    handler.setFormatter(JsonFormatter())
    root = logging.getLogger()
    root.handlers = [handler]
    root.setLevel(level)

调用时用 extra 携带业务字段,而不是把变量拼进消息里:

setup_logging()
logger = logging.getLogger("order")
logger.info("订单创建成功", extra={"fields": {"order_id": "20261101001", "user_id": 1001}})

这样日志平台可以直接按 order_id 过滤,而不用写正则从一句话里抠字段。

三、级别与模块划分的实用规则

  • DEBUG:只在本地和联调时打开,记录分支、入参摘要和中间结果,注意不要输出密码、Token 等敏感信息。
  • INFO:记录业务里程碑,例如订单创建、支付回调收到、任务开始与结束。
  • WARNING:服务还能继续,但出现了值得注意的异常,例如重试、降级、配置缺失用了默认值。
  • ERROR:当前请求或任务失败,需要人工介入;务必带上可定位的业务标识。

按模块取 Logger,例如 logging.getLogger(“order”)、logging.getLogger(“payment”),上线后可以只把某个模块调到 DEBUG,而不用全量打开。

四、请求上下文:一次请求一个标识

多用户并发时,只看时间很难把日志串起来。可以用 contextvars 给每个请求分配 request_id,并在 Formatter 中自动带上:

import contextvars

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

class ContextFormatter(JsonFormatter):
    def format(self, record):
        record.fields = {"request_id": request_id.get(), **(getattr(record, "fields", {}) or {})}
        return super().format(record)

在 Web 框架的中间件里设置 request_id,之后该请求产生的所有日志都会自动带上它,排查时按 request_id 一搜就是完整链路。

五、轮转与输出:不要让日志写爆磁盘

本地开发用控制台即可;生产环境建议用 RotatingFileHandler 或 TimedRotatingFileHandler,例如按天轮转、保留 7 天,并把级别设为 INFO 以上。更进一步,可以把日志直接发往集中日志服务,由平台做检索、告警和统计,应用本身不再操心文件清理。

六、常见坑

  1. 在库代码里调用 basicConfig 会干扰主程序配置,库应该只 getLogger,不配置 Handler。
  2. 不要在日志里输出完整请求体、身份证号、银行卡号等敏感数据,必要时先脱敏。
  3. 异常不要只记一句话,ERROR 级别建议用 logger.exception 记录堆栈,方便复现。
  4. 日志级别不是越多越好,INFO 应该是“读一遍能讲清业务走向”的密度。

结语

结构化日志的关键不是格式多漂亮,而是每条日志都能回答三个问题:什么时候、哪个模块、哪个业务对象出了什么事。先把时间、级别、模块和业务标识固定下来,再补请求上下文和轮转策略,一套能长期维护的日志系统就成型了。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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