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 本章知识体系
下面这张图,是我梳理的监控数据采集整体架构。你把它记牢了,后面几章讲根因定位时,会反复用到这些数据源。
3.6 本章小结
监控数据采集,是 SLA 违约定位的「地基」。地基不稳,上面盖的楼再漂亮也没用。我总结几个要点:
- Prometheus:采指标,看趋势,设告警
- ELK:聚日志,查细节,找原因
- APM:追链路,看依赖,定瓶颈
- 自定义埋点:补业务,填空白,保完整