日志规范
日志级别、结构化字段、敏感信息屏蔽
[Requirement] 必须按以下级别使用日志,不得滥用。
| 级别 | 使用场景 | 示例 |
|---|---|---|
| DEBUG | 开发调试细节,生产环境默认关闭 | 入参出参、循环内部变量 |
| INFO | 关键业务流程节点 | “用户登录成功”、“订单创建” |
| WARN | 可恢复的异常或非预期但不影响主流程 | “重试第 2 次”、“缓存未命中” |
| ERROR | 影响功能的错误,需要关注 | “调用支付接口失败”、“数据库写入异常” |
| CRITICAL/FATAL | 系统级故障,需要立即响应 | “服务不可用”、“数据损坏” |
[Standard] 生产环境默认日志级别为 INFO;DEBUG 级日志必须可通过配置动态开启,无需重启。
[Caution] 不要在循环中使用 INFO 级别记录每条数据,大量高频日志应降为 DEBUG 或使用采样。
示例:
logger.debug("开始处理请求: request_id=%s, params=%s", request_id, params)
logger.info("用户登录成功: user_id=%s, ip=%s", user_id, ip)
logger.warning("缓存未命中,回源数据库: key=%s", cache_key)
logger.error("支付失败: order_id=%s, error=%s", order_id, error, exc_info=True)
logger.critical("数据库连接池耗尽,服务不可用")[Requirement] 必须输出结构化日志(JSON 格式),便于采集、检索和分析。禁止使用字符串拼接的非结构化日志。
[Standard] 每条日志包含固定字段:timestamp、level、logger、message、trace_id、span_id。
日志格式示例:
{
"timestamp": "2024-01-01T12:00:00.123Z",
"level": "INFO",
"logger": "user.service",
"message": "用户登录成功",
"trace_id": "abc123",
"span_id": "def456",
"user_id": 1001,
"ip": "192.168.1.1"
}[Suggestion] 使用结构化日志库:Python 用 structlog 或 python-json-logger,Java 用 Logback + logstash-logback-encoder,Node.js 用 pino。
[Requirement] 所有日志必须包含以下字段,由日志框架统一注入,业务代码无需手动添加。
| 字段 | 说明 | 示例 |
|---|---|---|
| timestamp | ISO 8601 格式,带毫秒 | 2024-01-01T12:00:00.123Z |
| level | 日志级别 | INFO |
| logger | Logger 名称 | order.service |
| message | 日志消息(人类可读) | 订单创建成功 |
| trace_id | 全链路追踪 ID | abc123def456 |
[Standard] 业务字段使用 snake_case,保持全项目一致。
[Caution] 业务字段值必须是基本类型(字符串、数字、布尔),禁止传入复杂对象(需序列化为字符串)。
推荐字段:
| 字段 | 说明 |
|---|---|
user_id | 用户 ID |
order_id | 订单 ID |
request_id | 请求 ID |
cost_ms | 耗时(毫秒) |
status_code | HTTP 状态码 |
error_code | 业务错误码 |
示例:
# 正确:结构化字段
logger.info(
"订单创建成功",
order_id=order.id,
user_id=order.user_id,
amount=order.amount,
cost_ms=cost_ms,
)[Requirement] 日志中禁止输出以下敏感信息,必须脱敏后才能记录。
| 敏感类型 | 示例 | 脱敏方式 |
|---|---|---|
| 密码 | password、secret | 完全屏蔽,输出 *** |
| 身份证号 | 18 位身份证 | 保留前 4 后 2:1101***********12 |
| 手机号 | 11 位手机号 | 保留前 3 后 4:138****5678 |
| 银行卡号 | 16-19 位卡号 | 保留后 4 位:************1234 |
| Token/Key | token、api_key | 完全屏蔽 |
| 邮箱 | 邮箱地址 | 保留首字符和域名:z***@example.com |
| 完整地址 | 收货地址 | 保留省市区,屏蔽详细门牌号 |
[Standard] 提供统一的脱敏工具函数,在日志输出前调用。
示例:
from mask import mask_phone, mask_id_card, mask_password
logger.info(
"用户信息更新",
user_id=user.id,
phone=mask_phone(user.phone),
id_card=mask_id_card(user.id_card),
)[Caution] 打印异常堆栈时,异常消息中可能包含敏感信息(如 SQL 语句中的参数),需评估是否需要过滤。
[Standard] 高频路径(如循环内、热路径)上的 DEBUG 日志,必须使用惰性求值或级别判断,避免不必要的字符串拼接。
# 正确:使用 %s 占位符,日志库内部判断级别后才格式化
logger.debug("处理数据: item=%s", item)
# 错误:f-string 会在日志调用前就求值
logger.debug(f"处理数据: item={item}")[Caution] 不要在日志参数中执行昂贵的计算(如序列化大对象),会拖慢主流程。
[Default] 单条日志不超过 4KB;大对象日志需截断。
[Suggestion] 对批量数据处理,使用聚合日志而非逐条日志:
# 正确:聚合统计
processed, failed = 0, 0
for item in items:
try:
process(item)
processed += 1
except Exception:
failed += 1
logger.info("批量处理完成: total=%d, processed=%d, failed=%d",
len(items), processed, failed)[Suggestion] 高并发服务使用异步日志(AsyncAppender / QueueHandler),避免日志 I/O 阻塞业务线程。
[Caution] 异步日志可能丢失缓冲区中未落盘的日志,需在服务关闭时正确 flush。