“日志全是文本,grep 出来还靠人猜”——这是大多数团队日志体系的现状。结构化日志把每条日志变成可查询的字段集合(JSON),让日志从"给人读的流水"变成"给机器分析的记录"。配合语义约定统一字段命名、Trace ID 注入打通链路,日志才能真正支撑排障、分析与告警。
关键概念:结构化日志 = 每条日志输出为一个结构化对象(典型 JSON),关键信息成为字段而非散在文本里。语义约定 = 对"这些字段叫什么、代表什么"的统一规范,让多团队、多语言日志能对齐。
1. 为什么必须结构化
1.1 文本日志的局限
文本日志的问题:
2026-09-27 12:00:01 ERROR order timeout user 1001 cost 3.2s
→ grep 能搜,但"解析"很脆弱(格式改一点就碎)
→ 无法按字段过滤/聚合/排序
→ 日志库索引难、查询慢
→ 跨服务关联(Trace/订单)靠人肉猜
结构化日志:
{"time":"...","level":"error","event":"order.timeout",
"user_id":1001,"order_id":88,"cost_sec":3.2}
→ 字段级过滤、聚合、排序、告警全部可行
1.2 结构化的收益
- 可查询:按字段(user_id/error_code)精确检索
- 可聚合:错误率按服务/路由统计
- 可关联:Trace ID/Order ID 字段直接串联
- 可告警:字段规则 → 结构化日志触发告警
- 可分析:导入 BI/异常检测
ℹ️ 核心:结构化的价值不是"格式好看",而是"字段可编程"——日志从静态文本变为可被系统消费的数据。
2. 字段规范:日志应该长什么样
2.1 字段分层的约定
一条好的结构化日志字段分三层:
基础字段(每条都有):
time(时间,RFC3339 带时区)
level(debug/info/warn/error)
message(人可读的说明)
service(服务名)
trace_id / span_id(链路关联)
业务字段(按事件补充):
事件相关:order_id、user_id、route、code
度量相关:duration_ms、retry_count、size
环境/上下文字段:
env(prod/staging)、instance、region、commit(版本)
2.2 字段命名纪律
命名规则建议:
- 小写 snake_case(order_id、user_id、duration_ms)
- 层级统一:service.name、trace.id、http.status_code
- 同一含义全局一个名字(别 user_id 和 userId 混用)
- 单位进名字或值(duration_ms 而不是 duration)
对齐语义约定:
- OpenTelemetry Semantic Conventions 提供标准字段名
- 多语言 SDK 自动带 service.name、trace.id 等
- 自定义字段遵循同样风格,避免"方言丛生"
3. 多语言如何输出结构化日志
3.1 常见语言配置
// 通用做法:应用直接用 JSON 格式输出到 stdout/stderr
// 然后由采集器(promtail/fluent-bit/otel collector)收集
// Node.js(pino):开箱即用 JSON 日志
const logger = pino({ base: { service: "api" } });
logger.info({ order_id: 88 }, "order created");
// 输出:{"time":"...","level":30,"service":"api",
// "order_id":88,"msg":"order created"}
// Go(slog):结构化日志原生支持
slog.Info("order created",
"order_id", 88,
"user_id", 1001,
"duration_ms", 320)
# Python(structlog / logging + json formatter)
log.info("order created", order_id=88, user_id=1001)
要点:
日志写 stdout/stderr(统一由采集器收集)
别把结构化日志和业务输出混写
关键上下文(trace_id)由 SDK 自动注入
3.2 Trace ID 注入:日志与链路打通
结构化日志最有价值的关联:
每条日志带上当前请求的 trace_id / span_id
→ 在日志里搜 trace_id 即得整条链路的全部日志
→ 在追踪系统点一个 span 可跳对应日志
实现:
OpenTelemetry SDK 自动把 trace_id 注入日志上下文
或日志库手动从 span 上下文取 trace_id
效果:
"一次请求"的日志聚合查询、跨服务日志串联
→ 排障从"翻 N 个文件"变为"按 trace 一次拉全"
4. 日志上下文与关联性
4.1 上下文(Context)怎么传
关联维度不止 trace:
- 请求维度:trace_id、request_id
- 业务维度:order_id、user_id、session_id
- 环境维度:service、env、region、instance
设计原则:
高频关联字段 → 放结构化字段
关联方式 → 中间件/拦截器自动注入,别手动传参
示例:HTTP 中间件自动为每条请求日志注入:
trace_id、user_id(从认证信息)、route、status_code
4.2 相关性排查的姿势
结构化后的排障流程:
1. 按 trace_id 搜一次请求的全链路日志
2. 按 order_id 搜该业务对象的所有操作记录
3. 按 error_code 聚合 → 看分布/趋势
4. 按 service + level=error 扫全局错误
对比文本日志:
从"grep 关键词 + 人肉拼时间线"变成"字段查询 + 自动聚合"
5. 日志脱敏与合规
5.1 哪些字段必须脱敏
敏感字段(禁止明文入日志):
- 密码 / token / 密钥 / 会话凭证
- 身份证、手机号、银行卡等 PII
- 内部 URL 带认证信息
脱敏方式:
- 应用侧:打日志前就脱敏(推荐,源头治理)
- 采集侧:正则/插件脱敏(兜底)
- 查询侧:权限控制(谁可看原始日志)
5.2 脱敏实现示例
应用侧原则:永远不打印敏感字段,只打印"已脱敏/已散列"
例:user.email = "a***@example.com",token = "****"
采集侧(promtail/fluent-bit)也可做:
- 替换模式:password: "xxx" → password: "***"
- 丢弃字段:drop 掉含敏感名的字段
合规:
- 日志保留期限制(按法规/业务)
- 日志访问权限分级
- 敏感日志独立加密/短期存储
6. 查询与分析的落地
6.1 日志查询的典型操作
按字段过滤:
{service="api"} | json | level=="error" and order_id=="88"
按字段聚合:
| json | status_code by (route) → 错误率统计
按时间与 trace:
{trace_id="abc123"} | json # 一条链路全部日志
跨系统:
LogQL(Loki)/ ESQL(ES)各有字段语法
结构化日志让这些语法真正可用
6.2 从"存"到"用"
结构化日志的完整价值链:
结构化输出 → 采集器收集 → 日志库存储/索引
→ 字段查询/聚合 → 面板/告警/分析 → 排障与决策
配套:
- 日志保留分级(热/温/冷)
- 高价值日志(error/trace)优先保留
- 与分析平台(BI/异常检测)打通
7. 常见避坑
| 坑 | 现象 | 对策 |
|---|---|---|
| 日志仍是文本 | 查询靠 grep | 应用输出 JSON |
| 字段方言乱 | 多团队难对齐 | 统一语义约定 |
| 没有 trace_id | 日志串联不了 | OTel 自动注入 |
| 打印敏感字段 | 合规风险 | 源头脱敏 |
| 日志与业务混写 | 解析困难 | 只输出结构化日志 |
| 只存不用 | 日志库沦为磁盘 | 接查询/聚合/告警 |
8. 最佳实践清单
□ 日志一律结构化输出(JSON),写 stdout/stderr
□ 字段按 基础/业务/环境 三层,snake_case 统一命名
□ 对齐 OTel 语义约定(service.name/trace.id/http.*)
□ Trace ID 自动注入,日志与链路打通
□ 上下文由中间件自动注入(trace/user/route)
□ 敏感字段源头脱敏,采集侧兜底
□ 日志保留分级,高价值日志优先
□ 结构化日志接查询、聚合、告警与 BI
□ 建立日志规范文档,多语言统一落地
一句话原则
结构化日志 + 语义约定 = 日志从"给人读"变成"给机器分析",
字段可查询、可关联、可告警、可合规。
小结
结构化日志与语义约定的核心是"把日志变成字段可编程的数据":应用统一输出 JSON 结构化日志,按语义约定统一字段命名(对齐 OTel),Trace ID 自动注入打通链路与日志,并做好敏感字段脱敏与保留分级。落地记住五件事:日志统一 JSON 结构化输出、字段用约定命名对齐 OTel、Trace ID 自动注入实现链路串联、敏感字段源头脱敏、结构化日志接上查询/聚合/告警。当排障从"grep 关键词 + 人肉拼时间线"变成"字段查询 + 一键拉全链路",日志就从最被低估的资产变成了最可靠的真相来源。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。