可观测三件套:指标、日志与链路追踪

可观测三件套:TraceID 串起指标、日志与链路

排查能力的天花板由可观测性决定:没有指标只能靠猜,没有日志只能靠复现,没有链路只能靠翻代码。本章讲清 Metrics、Logs、Trace 三者的分工、各自的判读方法,以及三者如何互相印证——最后给出没有 APM 时的兜底方案。

三者的分工

维度Metrics 指标Logs 日志Trace 链路
回答什么有没有问题、范围多大错在哪一步、什么异常慢在哪一跳、谁的责任
数据形态时间序列,聚合值离散事件,全量文本调用树,带父子关系
成本低(可长期保存)高(需采样与轮转)中(必须采样)
强项趋势与告警细节与上下文跨服务耗时归因
弱项无细节、无个体难以聚合统计采样后可能漏掉长尾
典型入口大盘与告警面板journalctl、日志平台Trace 查询与耗时 TopN

只用一件工具排查,等于只用一只眼睛看世界。三个一起上,才能形成“现象 → 证据 → 结论”的闭环。

Metrics:USE 与 RED

两种方法各管一半:USE 看资源,RED 看服务。

方法维度指标判读要点
USE资源利用率 UtilizationCPU、内存、带宽使用率逼近 100% 才算异常,先找最先接近的那个
USE饱和度 Saturationload、队列长度、连接池等待数有排队就说明资源不够,即使利用率不高
USE错误 Errors网卡丢包、磁盘重试、OOM 次数这类错误不会体现在利用率上
RED速率 RateQPS/TPS突增可能是重试放大,突降可能是上游断了
RED错误 Errors5xx 比例、超时率按接口与状态码拆开看
RED时长 DurationP50/P95/P99涨在哪个分位决定影响多少人

延迟必须看分位,不同分位对应不同故事:

分位含义常见根因
P50一半请求快于它整体变慢:依赖变慢、数据量增长
P955% 的请求慢于它局部热点、索引失效、单机故障
P991% 的请求慢于它GC 停顿、锁竞争、慢查询、重试
Max最慢的那一个超时与长尾,直接决定用户投诉

告警阈值怎么定

  • 阈值要基于基线:先观察两周同一个时间段的正常值,再取 3 倍标准差或静态倍数。
  • 优先报警 P99 与错误率,不建议对平均延迟报警,响应太迟钝。
  • 每个告警必须能被一条命令验证,否则运维会习惯性忽略它。
  • 分级:P0 打电话(核心接口不可用),P1 群里通知(错误率超阈值),P2 只进工单(水位趋势)。
坏告警:CPU > 80% 持续 5 分钟   → 不一定影响用户,频繁误报
好告警:下单接口 5xx 比例 > 1% 持续 1 分钟,且请求量 > 100/s
好告警:下单接口 P99 > 2s 持续 3 分钟

Logs:结构化才可用

日志的价值取决于字段是否统一。排查时要按“时间 + traceId + 接口 + 错误码”聚合,纯文本自由格式做不到。

必须有的字段作用
时间戳(毫秒,带时区)与变更时间线对齐
TraceID / SpanID跨服务串联一次请求
用户 ID / 租户 ID评估影响面、复现个案
接口名 / 方法名按接口聚合错误率
耗时(自身耗时 + 下游耗时)区分自身慢还是下游慢
错误码 / 异常类名分类统计,避免只看总数
# 用 TraceID 串起一次请求在所有服务里的日志
grep -h "<trace-id>" /var/log/myapp/*.log | sort -k1,2 | head -50

# 按分钟统计错误数,定位拐点
grep "ERROR" app.log | awk '{print substr($1,1,16)}' | uniq -c | tail -30

# 结构化日志(JSON)按字段过滤
grep '"level":"ERROR"' app.log | head -20

日志落盘必须配套轮转,否则日志本身就是下一个故障源:磁盘写满或 inode 耗尽都会导致全站不可用。

Trace:把耗时拆开

一次慢请求的耗时永远能拆成四段:自身计算、等待下游、等待网络、排队等待。

耗时构成表现优化方向
自身计算本服务 Span 内耗时长,无子 Span优化代码、算法、序列化
等待下游子 Span 本身慢推动下游优化、加超时与降级
网络子 Span 开始时间明显晚于父 Span重试、DNS、跨机房调用
排队等待线程池、连接池等待 Span扩池、隔离、限流

读 Trace 的三个要点:先看最底层的慢 Span(那里才是真凶),再看是否所有请求都慢(全局问题还是个别请求),最后看 Span 数量是否异常膨胀(N+1 调用)。

三者互相印证

症状先用谁再用谁确认
大盘延迟抬头,不知哪台机器Metrics 分实例曲线Logs 看实例日志,Trace 看该实例慢 Span
错误率上升Metrics 按接口拆分Logs 找首个异常栈
偶发超时、均值正常Trace 找长尾请求Logs 看该 TraceID 完整链路
单机 CPU 高、整体正常Metrics 分实例登机器用 top -Hp 定位线程
用户说“很慢”但监控正常Logs 按用户 ID 检索Trace 看该用户请求,常见是数据量差异

没有 APM 时的兜底

没有链路追踪也能做到 80% 的定位能力,前提是把 TraceID 落到日志里。

请求进入 Nginx:生成/透传 X-Request-ID → 应用日志打印该 ID
  ↓
Nginx access.log 记录 $request_time(整体)与 $upstream_response_time(后端)
  ↓
两者之差 ≈ 网络与排队耗时;upstream 耗时大 → 问题在后端
  ↓
应用日志按 X-Request-ID 捞出该请求的全部日志 → 看到具体慢在哪一步

Nginx 侧至少加上这几个变量:

log_format main '$remote_addr - $request_id [$time_local] "$request" '
                '$status $body_bytes_sent $request_time $upstream_response_time '
                '"$http_referer" "$http_user_agent" $upstream_addr';

判读规则:$request_time 大而 $upstream_response_time 小,问题在 Nginx 自身或客户端;两者都大,问题在后端;$upstream_addr 指向哪台就查哪台。

预防与巡检

  • 核心接口必须有指标、日志、TraceID 三项,缺一项就算可观测性欠债。
  • 定期验证告警真的能触发,静默的告警比没有告警更危险。
  • 日志字段变更要走评审,字段一旦改名,历史检索链路全部失效。
  • 采样率要留够长尾:错误请求应该 100% 采样,正常请求可以低比例采样。

小结:Metrics 告诉你有没有问题与范围,Logs 告诉你错在哪一步,Trace 告诉你慢在哪一跳;延迟看 P95/P99 而非均值,告警按接口与分位设置,日志必须结构化并带 TraceID;没有 APM 时用 Nginx 的 $request_time$upstream_response_time 差值加 TraceID 兜底,同样能定位到具体服务与具体步骤。

笔记加载中…