Python日志格式设计规范_可读性与分析友好方案【教程】

Python日志格式需兼顾人可读与机器可解析,开发用彩色精简格式(asctime、levelname首字母、name、message),生产用ISO时间JSON格式并统一字段命名。

Python 日志格式不是越花哨越好,关键在两点:人一眼能看懂,机器容易切分解析。默认的 %(message)s 太简陋,但加一堆字段又让终端日志臃肿难读。平衡点在于「按场景分层设计」——开发时用可读格式,生产时用结构化格式,且两者字段对齐。

开发环境用彩色 + 精简字段(logging.basicConfig 直接生效)

终端调试最怕翻屏找时间、看不出哪条是 ERROR、分不清模块来源。推荐用 colorlog 配合 4 个核心字段:

  • %(asctime)s:带毫秒,格式设为 %H:%M:%S(不显日期,避免行太长)
  • %(levelname).1s:只取首字母(E/W/I),配合颜色更直观
  • %(name)s:模块名,不带完整路径(避免 myproject.api.v2.handlers.user 这种长串)
  • %(message)s:消息主体,不加前缀

示例配置:

import logging
import colorlog

handler = colorlog.StreamHandler() handler.setFormatter(colorlog.ColoredFormatter( '%(log_color)s%(asctime)s %(levelname).1s %(name)s: %(message)s', datefmt='%H:%M:%S', log_colors={'DEBUG': 'cyan', 'INFO': 'green', 'WARNING': 'yellow', 'ERROR': 'red', 'CRITICAL': 'bold_red'} )) logging.basicConfig(level=logging.INFO, handlers=[handler])

生产环境必须用 JSON 格式(json.dumps 输出到文件)

日志进 ELK 或 Loki 后,字段必须可提取、可聚合、无歧义。文本日志靠正则硬切,一改格式就崩。JSON 是唯一稳妥选择,但要注意三点:

  • 时间必须用 ISO 8601 字符串(%(asctime)s 默认不是,得用 %(created)f + 自定义 formatter 转)
  • 不能直接用 %(message)s,要保留原始 argsexc_info 结构,否则无法还原异常上下文
  • 字段名统一小写+下划线(如 log_level 而非 levelname),避免和不同采集器默认字段冲突

关键字段建议: timestamplog_levellogger_namemessagemodulefunctionlinetraceback(仅 ERROR 及以上才填)

别让 %(pathname)s 暴露绝对路径(安全 & 可移植性)

日志里出现 /home/deploy/app/src/api/handler.py 有风险:暴露部署结构、换服务器路径就变、本地开发和容器环境不一致。解决方法只有两个:

  • %(filename)s 替代 %(pathname)s(只留文件名,丢掉路径)
  • 如果真需要相对路径,启动时设置 LOG_ROOT = Path(__file__).parent.parent,然后在 formatter 中用 str(pathlib.Path(record.pathname).relative_to(LOG_ROOT))

注意:%(funcName)s%(lineno)d 安全可用,它们不带路径信息。

自定义 LogRecord 字段要注册,否则 extra 会丢

想加 request_iduser_id?不能只靠 logger.info("msg", extra={"request_id": "abc"})。因为默认 LogRecord 类不认识这些 key,输出时会被过滤。必须提前注册:

import logging

注册新字段(只做一次)

for key in ['request_id', 'user_id']: if not hasattr(logging.LogRecord, key): logging.LogRecord.dict[key] = None

然后在 Formatter 中就能用 %(request_id)s

更稳妥的做法是继承 logging.Formatter,重写 format() 方法,在里面动态注入字段,避免污染全局 LogRecord

最常被忽略的是:日志格式变更后,旧日志解析规则立刻失效。哪怕只是多加一个空格,Logstash 的 grok 模式就匹配不上。上线前务必拿真实日志样本跑一遍解析链路。