3. 监控数据采集:Prometheus指标采集、ELK日志聚合、APM链路追踪、自定义埋点

监控数据采集,说白了就是给系统装「传感器」。没有数据,根因定位就是瞎猜。我见过太多团队,SLA 违约了才手忙脚乱去查日志,结果发现关键指标根本没采——那种感觉,就像开车没仪表盘,全靠感觉。

这一章,我把监控数据采集拆成四个维度:指标、日志、链路、自定义埋点。每个维度都有它的「脾气」,你得知道什么时候用谁,怎么用。

3.1 Prometheus 指标采集:系统健康的「心电图」

Prometheus 是我个人最常用的指标采集工具。它走的是「拉模式」——定期去目标服务抓数据。你想想看,这比推模式好在哪?挂了也能发现,因为数据不来了嘛。

3.1.1 核心概念

  • 指标(Metric):比如 CPU 使用率、请求 QPS、错误率
  • 标签(Label):给指标打标签,比如 method="GET"、status="500"
  • 采集间隔(Scrape Interval):默认 15s,我建议关键指标调到 5s

关键点:指标采集不是越多越好。我见过有人把几百个指标全塞进去,结果告警阈值都设不过来。核心原则:只采能反映 SLA 状态的指标。

3.1.2 实战配置示例

# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'api-service'
    metrics_path: '/metrics'
    static_configs:
      - targets: ['localhost:8080', 'localhost:8081']
        labels:
          service: 'user-api'
          env: 'production'

嗯,这里要注意:metrics_path 默认是 /metrics,但有些框架会用 /actuator/prometheus。我曾经因为路径写错,整整两天没采到数据,还以为是服务挂了。

3.1.3 常用指标类型

类型 说明 典型场景
Counter 只增不减的计数器 请求总数、错误总数
Gauge 可增可减的瞬时值 内存使用量、连接数
Histogram 分布统计 请求延迟 P50/P99
Summary 类似 Histogram,但客户端计算分位数 延迟敏感场景

我的经验:Histogram 和 Summary 别混用。我踩过坑——两个都上了,结果存储开销翻倍,查询还慢。选一个就行,我倾向 Histogram,因为服务端聚合更灵活。

3.2 ELK 日志聚合:故障现场的「黑匣子」

指标告诉你「系统出问题了」,日志告诉你「为什么出问题」。ELK(Elasticsearch、Logstash、Kibana)是日志聚合的老牌组合。我个人习惯用 Filebeat 替代 Logstash 做采集端,轻量很多。

3.2.1 日志采集架构

应用日志 → Filebeat → Kafka(缓冲) → Logstash(解析) → Elasticsearch(存储) → Kibana(可视化)

为什么要加 Kafka?我遇到过日志量暴增把 ES 打挂的情况。加个缓冲层,Logstash 处理不过来时,数据还能在 Kafka 里排队,不会丢。

3.2.2 日志格式规范

你想想看,如果每行日志格式都不一样,聚合查询就是噩梦。我建议统一用 JSON 格式:

{
  "timestamp": "2025-01-15T10:30:00.123Z",
  "level": "ERROR",
  "service": "order-service",
  "trace_id": "abc123",
  "message": "数据库连接超时",
  "stack_trace": "java.sql.SQLTimeoutException: ...",
  "duration_ms": 5000
}

避坑指南:我曾经见过团队日志里不打印 trace_id,结果排查问题时,根本没法把一次请求的多个日志串起来。后来我强制要求:所有日志必须带 trace_id,这是链路追踪的基础。

3.2.3 关键聚合查询

Kibana 里我常用的几个查询:

  • level: ERROR AND service: order-service —— 查某个服务的错误日志
  • trace_id: abc123 —— 查一次请求的全链路日志
  • duration_ms > 3000 —— 查慢请求日志

3.3 APM 链路追踪:请求的「CT 扫描」

指标和日志都是「点」上的数据,链路追踪把点连成「线」。我用得最多的是 Jaeger 和 SkyWalking。它们能告诉你:一次请求经过了哪些服务,每个服务花了多少时间,哪一步最慢。

3.3.1 核心概念

  • Trace:一次完整的请求链路
  • Span:链路中的一个步骤,比如调用数据库、调用下游服务
  • Span Context:跨服务传递的上下文,包含 trace_id、span_id

3.3.2 采样策略

全量采集?别闹。高并发系统每秒几万个请求,全量采存储扛不住。我建议:

场景 采样率 说明
正常流量 1% - 10% 够用就行,统计意义足够
高错误率时段 100% 自动提升采样率,方便定位
核心交易链路 50% - 100% 比如支付、下单,不能丢

我的做法:用自适应采样。错误率超过阈值时自动切到全量采样,正常时降到 1%。这样既省钱,又不漏关键数据。

3.3.3 代码埋点示例(Java + OpenTelemetry)

// 自动埋点:引入 agent 即可
java -javaagent:opentelemetry-javaagent.jar \
     -Dotel.service.name=order-service \
     -Dotel.traces.exporter=jaeger \
     -jar order-service.jar

// 手动埋点:自定义 Span
Span span = tracer.spanBuilder("queryDatabase")
    .setAttribute("db.type", "MySQL")
    .setAttribute("sql", "SELECT * FROM orders WHERE id = ?")
    .startSpan();
try {
    // 执行数据库查询
} finally {
    span.end();
}

3.4 自定义埋点:补全「最后一公里」

框架自带的监控往往不够。比如你想知道「用户从点击到下单平均花了多久」,或者「某个业务规则触发了多少次」。这些框架采不到,得自己埋点。

3.4.1 埋点原则

  • 业务语义明确:埋点名要让人一看就懂,比如 order.create.success
  • 不要埋太多:每个埋点都有性能开销,我一般控制在 20 个以内
  • 带上上下文:比如用户 ID、商品 ID,方便后续分析

3.4.2 代码示例(Prometheus 自定义指标)

// 定义计数器
Counter orderSuccessCounter = Counter.build()
    .name("order_create_success_total")
    .help("Total number of successful order creations")
    .labelNames("payment_method")
    .register();

// 业务代码中调用
orderSuccessCounter.labels("wechat_pay").inc();

3.4.3 埋点与 SLA 的关系

我举个例子。有一次 SLA 违约,原因是「下单成功率低于 99.9%」。但框架指标显示服务正常,数据库也正常。后来查自定义埋点才发现:某个商品库存为 0 时,前端没做校验,用户点了下单直接报错。这个错误在框架层面是「正常业务异常」,但用户感知就是「下单失败」。

核心结论:SLA 监控必须覆盖业务语义。框架指标 + 自定义埋点,缺一不可。

3.5 本章知识体系

下面这张图,是我梳理的监控数据采集整体架构。你把它记牢了,后面几章讲根因定位时,会反复用到这些数据源。

监控数据采集架构 业务应用(API / 微服务) Prometheus 指标 Filebeat 日志 APM Agent 链路 自定义埋点 Prometheus Server Elasticsearch Jaeger / SkyWalking 自定义存储 Grafana / Kibana / 告警平台 SLA 违约根因定位 说明:四类数据源缺一不可,指标看趋势、日志看细节、链路看依赖、埋点看业务

3.6 本章小结

监控数据采集,是 SLA 违约定位的「地基」。地基不稳,上面盖的楼再漂亮也没用。我总结几个要点:

  • Prometheus:采指标,看趋势,设告警
  • ELK:聚日志,查细节,找原因
  • APM:追链路,看依赖,定瓶颈
  • 自定义埋点:补业务,填空白,保完整