结构化日志
把日志作带字段(时间戳、级别、服务、trace_id、消息)的 JSON 发,非非结构化文本。
为什么
非结构化日志("checkout failed for user 12345 at 14:05")人可读但机敌 — 你无法按服务过滤、按 trace_id 分组或按字段告警。事件中值班 grep 10000 行文本、找 3 相关、漏解释根因的第 4。结构化日志({ts, level, service, trace_id, user_id, msg, error})可查询:"显示过去 5m trace_id=abc 的所有 ERROR 日志"50ms 返 3 行,且你可告警"error.rate > 1/s by service"。trace_id 是乘数 — 它跨服务链日志,故结账失败一查询显示购物车服务日志、支付服务日志和 DB 查询。转结构化日志的团队报根因识别快 60% 因日志可查询非可 grep。
何时使用
新服务第一天。下次重构时给现有服务补结构化日志。
如何做
1. 选结构化日志库和格式
- JSON 是标准;多日志聚合器(Loki、Datadog、ELK)原生解析它
- 用库的结构化 API 非字符串插值:logger.info({service, trace_id, msg})
2. 每日志含这些字段
- timestamp(ISO 8601,UTC)、level(DEBUG/INFO/WARN/ERROR)、service、trace_id、span_id
- 加请求特定字段:user_id、route、status_code、latency_ms
- trace_id 不可商量 — 它是你跨服务链日志的方式
3. 跨服务边界传播 trace_id
- 入请求 → 生成 trace_id(或从头接受)→ 放上下文
- 出请求(HTTP、gRPC、DB)→ 头/元数据含 trace_id
- 这是分布式追踪;OpenTelemetry 自动做
4. 按正确级别和量日志
- INFO:请求开始/结束、业务事件(下单、支付失败)
- WARN:降级但功能(缓存未命中、重试成功)
- ERROR:请求失败、需关注;含错误和栈
- DEBUG:详细诊断,prod 默认关
5. 别日志敏感数据
- prod 日志无密码、令牌、PII 或全请求体
- 用日志消毒器删 已知敏感字段示例
// 坏:非结构化文本
console.log("checkout failed for user " + userId + " at " + new Date());
// 好:结构化 JSON
import { logger } from "./logger";
logger.error({
service: "checkout",
trace_id: req.traceId, // 从网关传播
span_id: generateSpanId(),
user_id: req.userId,
route: "/checkout",
status_code: 500,
latency_ms: 1240,
msg: "checkout failed",
error: err.message,
stack: err.stack,
});
// 输出(JSON,一行):
{"ts":"2026-03-15T14:05:01Z","level":"ERROR","service":"checkout",
"trace_id":"abc123","span_id":"def456","user_id":12345,
"route":"/checkout","status_code":500,"latency_ms":1240,
"msg":"checkout failed","error":"null discount","stack":"..."}
// 在 Loki/Datadog 查(找此 trace 所有日志):
{trace_id="abc123"} → 一查询返 checkout、cart、payment、db 日志
// 按服务错误率告警:
rate({level="ERROR",service="checkout"}[5m]) > 1 → page checkout 值班常见问题
- ⚠字符串插值日志 — 人可读但不可查询;值班 grep 10k 行漏根因。
- ⚠无 trace_id — cart、checkout 和 payment 服务日志无法链;值班分别查每服务按时间戳关联。
- ⚠日志敏感数据 — prod 日志中密码或 PII 在技术事件上叠合规和安全事件。
- ⚠一切 INFO 级 — 无信号;值班无法过滤到要紧的故 grep 希望。
最佳实践
- ✓把日志作带 timestamp、level、service、trace_id、span_id 和请求字段的 JSON 发;用库的结构化 API 非字符串插值。
- ✓跨所有服务边界(HTTP、gRPC、DB)传播 trace_id 使日志跨服务链;OpenTelemetry 自动做此。
- ✓按正确级别日志(INFO 事件、WARN 降级、ERROR 带栈失败)且从不日志敏感数据(密码、令牌、PII)。
- ✓在日志聚合器按字段(trace_id、service、level)查询;按结构化字段(按服务 error.rate)告警非 grep 文本。