日志必须结构化、可检索、有上下文,强制 JSON 格式,含时间戳(ISO8601)、服务名、环境、trace_id、level 和业务内容;字段小写蛇形命名,关键信息不得藏于 message;分级明确,接入轻量 SDK 与日志平台治理。

日志不是写给自己看的,而是写给排查问题的人(可能是未来的你)看的。统一日志格式、结构和字段语义,能让研发和运维在故障发生时快速对齐上下文,减少“你那边有没有打日志”“这个 trace_id 怎么查不到”这类低效沟通。
核心原则:结构化 + 可检索 + 有上下文
避免纯文本堆砌,强制使用 JSON 格式输出;每条日志必须包含时间戳、服务名、环境标识、请求唯一标识(如 trace_id)、日志等级和业务主体内容。不强制要求所有字段一次写全,但关键字段(如 error、user_id、order_id、http_status)应在对应场景下显式写出,而非藏在 message 字符串里。
- 时间戳用 ISO8601 格式:"2024-05-22T14:30:45.123Z"(带毫秒和时区)
- 服务名统一用小写短名,如 "user-service",不带版本或实例信息
- 环境字段固定为 "env": "prod" / "staging" / "dev",禁止用 "production" 或 "test"
- trace_id 必须透传,HTTP 入口从 header(如 X-Trace-ID)注入,RPC 调用通过中间件自动携带
字段命名与语义规范
字段名全部小写,用下划线分隔(snake_case),避免驼峰或中横线。常见字段含义需团队对齐,例如:
- level:仅限 "debug" / "info" / "warn" / "error" / "fatal",禁用 "warning"、"err" 等别名
- span_id:同一次调用链中的子操作 ID,与 trace_id 配合用于链路追踪
- user_id:数字型用户主键,非用户名或 token;匿名场景填 "-1" 或留空(不填 null)
- status_code:HTTP 状态码或内部业务码(如 "200", "BUSINESS_FAILED"),不混用字符串和数字
- duration_ms:耗时统一毫秒整数,不带单位字段,不四舍五入(保留原始纳秒转毫秒值)
日志分级与内容约束
不同 level 对应明确行为边界:
- debug:仅本地开发或灰度环境开启,含参数快照、分支路径标记,上线后默认关闭
- info:记录正常业务流转节点,如 "order_created"、"payment_confirmed",不含敏感数据
- warn:异常但未中断流程,如降级触发、重试第2次、缓存穿透返回空
- error:导致当前请求失败,必须含 stack_trace(截断前 5 层即可)和 cause(如 "redis_timeout")
- fatal:进程级崩溃前最后一条日志,由 panic handler 自动注入,研发不手动打
接入与治理建议
用轻量工具落地规范,不依赖强改造:
- Go/Java/Python SDK 封装统一 Logger,预置 env/service/trace_id 注入逻辑
- Nginx/Apache 日志启用 log_format json,字段映射到标准字段(如 $request_id → trace_id)
- ELK 或 Loki 中配置 pipeline,自动补全缺失字段(如无 env 则设为 "unknown"),并过滤掉纯 message 匹配的非结构化日志
- CI 流水线加入日志格式校验:抽取 100 条样本,用 jq 验证必选字段存在性及类型(如 level 是否为字符串)
规范的价值不在文档多厚,而在每行日志都能被机器准确解析、被人一眼读懂意图。从下一个 commit 开始,让日志成为协作的接口,而不是甩锅的证据。










