Skip to content

可观测性:监控、日志与链路追踪 ​

可观测性(Observability)回答的问题是:通过系统的外部输出,能多大程度推断系统内部的状态。传统的监控(Monitoring)回答"系统是否正常"——预定义的指标和阈值告诉你红灯还是绿灯。可观测性回答"系统为什么不正常"——在没有预想到的故障发生时,能否通过既有数据定位根因。

三根支柱构成可观测性的数据模型:指标(Metrics,随时间变化的数值)、日志(Logs,离散事件的文本记录)、链路追踪(Traces,请求穿越多个服务的时间线)。三者各有不可替代的视角,也有明确的关联机制。

指标:低成本的全局视野 ​

指标是聚合的数值——QPS、P99 延迟、错误率、内存使用量。它的优势是存储成本低(一个时间序列每分钟一个数据点,一年不过 50 万个点)和查询速度快(聚合查询在预计算的序列上执行)。这使指标适合回答"系统的整体健康状态"和"趋势变化"。

指标的设计核心是维度(labels)。Prometheus 的数据模型是多维时间序列——http_requests_total{method="GET", path="/api/users", status="500"} 的每个 label 组合是一条独立序列。维度设计的好坏直接决定排查能力:有 status 维度能立刻看到错误率飙升,有 path 维度能定位到具体接口,缺失维度则只能知道"出了问题"但不知道哪里出的问题。工程实践中的准则:高基数维度(user_id、request_id)不要放进指标 label——每个唯一值产生一条序列,基数爆炸会拖垮存储;这类信息属于日志和追踪的领域。

Prometheus 是指标领域的事实标准。Pull 模型(Prometheus 主动抓取 exporter 暴露的 /metrics 端点)、PromQL 查询语言、告警规则(Alertmanager)构成完整的指标闭环。长期存储(Prometheus 本地存储有限)通过 Thanos 或 VictoriaMetrics 扩展。

promql
# 过去 5 分钟的错误率,按 path 分组
sum(rate(http_requests_total{status=~"5.."}[5m])) by (path)
  / sum(rate(http_requests_total[5m])) by (path)

日志:事件的全量记录 ​

日志记录离散事件——一个请求的完整上下文、一个错误的堆栈、一次状态变更。与指标相反,日志的存储成本高(一条结构化日志几百字节到几 KB)但信息量大(可以包含任意上下文字段)。

结构化日志是现代实践的分水线。传统的非结构化日志(Connection failed for user 123 at 2026-08-14 10:23:45)对机器解析不友好——grep 能用但聚合分析困难。结构化日志({"event": "connection_failed", "user_id": 123, "error": "timeout"})支持字段级查询和聚合。日志框架的结构化输出(JSON formatter)+ 采集端的字段解析 + 存储端的全文索引构成结构化日志管线。

ELK/EFK 栈(Elasticsearch + Logstash/Filebeat + Kibana)是日志领域的传统方案:Filebeat 采集 → Logstash 解析富化 → Elasticsearch 索引 → Kibana 查询可视化。Loki(Grafana 生态)是轻量替代——只索引 label 不索引全文,存储成本降一个数量级,代价是全文搜索能力弱(依赖 LogQL 的标签过滤 + 受限的行过滤)。选型分界:需要复杂全文搜索和长期留存选 ELK,只需要按服务/级别/trace_id 过滤查看选 Loki。

链路追踪:请求的时空之旅 ​

分布式系统中一个请求穿越多个服务——网关 → 用户服务 → 订单服务 → 数据库。每个服务各自记录日志,但"这些日志属于同一个请求"的关联信息缺失。链路追踪补上这块:每个请求携带全局唯一的 trace_id,每经过一个服务产生一个 span(有开始/结束时间和操作名),所有 span 通过 trace_id 关联成一棵调用树。

追踪的核心价值是延迟归因。用户报"下单慢",从 trace 树上立刻看到:网关 5ms → 用户服务 10ms → 订单服务 800ms → 其中数据库查询 750ms。瓶颈定位从"逐个服务查日志"变成"看一棵树"。这在调用链超过 3 层的系统中几乎是唯一高效的排障手段。

OpenTelemetry(OTel)是三支柱的统一采集标准——SDK(各语言的应用内埋点)、Collector(接收/处理/导出的中间层)、OTLP 协议(SDK 到 Collector 的传输)。OTel 的意义是解耦:应用只对接 OTel SDK,后端(Jaeger、Zipkin、Prometheus、Loki、商业 APM)可以随时替换——避免了被单一可观测厂商锁定。

追踪的采样策略是成本与覆盖的权衡。全量追踪(每个请求都记录)在小流量系统可行,高流量系统通常采样(1%-10%)——但纯随机采样会错过低频但关键的错误请求。自适应采样(错误请求必采、慢请求高概率采、正常请求低概率采)是现代后端的标准能力。

三支柱的关联 ​

三支柱不是孤立的——关联机制决定它们的组合价值:

Exemplars(样本关联):指标的数据点附带一个 exemplar——指向该数据点来源的具体 trace。P99 延迟图上的异常点,点击 exemplar 直接跳转到对应的慢请求 trace——从"指标发现异常"到"追踪定位原因"的桥。

trace_id in logs:日志中注入 trace_id 字段——从 trace 树发现订单服务的数据库查询慢,用 trace_id 查日志看到该请求的完整参数和 SQL。指标 → 追踪 → 日志的排障链路就此贯通。

统一元数据:服务名、环境(prod/staging)、版本号在三支柱中保持一致的 label/tag/字段——这是跨支柱查询("过滤出 v2.3.1 版本的所有数据")的前提。

OpenTelemetry 的 Collector 通常承担关联的实现——接收三支柱数据后统一注入服务元数据,再分发到各自的后端。

实践路径 ​

可观测性建设的一个务实顺序:先有指标(Prometheus + Grafana,覆盖 RED 方法——Rate/Error/Duration——每个服务的三个基础指标),这是发现问题的眼睛。再有结构化日志(集中收集 + trace_id 注入),这是定位问题的第一层。最后是追踪(OTel SDK 埋点 + Jaeger/Tempo 后端),这是复杂调用链的终极武器。三者的投入比大约是 3:4:3——指标和追踪的基础设施成本高(存储和查询),日志的采集成本高(量大)。

告警是可观测性的出口。好的告警实践:基于症状而非原因告警(用户可感知的错误率/延迟,而非 CPU 使用率——原因类指标波动频繁且用户无感);分级(page 立即处理 vs ticket 工作时间处理);每个告警有 runbook(接警后的处理步骤文档)——没有 runbook 的告警只是噪音的另一种形态。