凌晨三点,你的手机开始疯狂震动。告警短信一条接一条,"CPU使用率超过80%""内存使用率超过85%""数据库连接数接近上限"——你迷迷糊糊点开监控大盘,发现一堆红彤彤的数字,心跳加速,手心出汗,然后陷入哲学三问:我是谁?我在干什么?这个问题出在哪?
恭喜,你成功进入了告警风暴(Alert Storm)的世界。
这不是你一个人的问题。这是整个行业的后遗症——监控装了一大堆,告警规则写了几十条,但真到出事的时候,你依然一脸懵逼。今天,就来聊聊真正的可观测性(Observability),不是那些花里胡哨的 Dashboard 炫酷大屏,而是能让你在生产事故中快速定位问题、减少半夜爬起来次数的东西。
1. 指标 Metrics:别再只盯着CPU和内存了
大部分人装监控,第一步就是装个 Prometheus 或类似工具,然后模板一导入,大盘就出来了。CPU、内存、磁盘、网络——四件套走天下。
但我问你:你的应用的核心指标是什么?
很多人答不上来。他们会说"我的服务正常运行着"。好,那你怎么知道它健康?"没告警就是健康"。这个逻辑……怎么说呢,就像你觉得自己身体健康,是因为没有收到病危通知单。
真正有价值的应用层指标,应该是跟你业务逻辑强相关的。比如:
- 请求成功率:5分钟内 HTTP 200 占比是多少?低于 99.5% 就是问题
- 接口响应延迟 P99:你的接口 99% 的请求在多少毫秒内返回?而不是平均值,因为平均值会骗人
- 业务动作计数:每小时有多少人下单?每分钟有多少次支付回调?这些数字本身就是一个业务心跳
- 依赖方健康状态:你调用的下游服务,错误率是多少?超时率是多少?
举一个具体的例子。我之前维护一个支付网关,监控大盘上一堆服务器资源指标,但有一次下游银行渠道接口超时,导致大量请求堆积,用户支付失败。但从 CPU 和内存看,完全正常——直到几分钟后数据库连接池被打满,才触发告警。如果当时监控了"银行接口调用耗时"和"超时计数"这两个指标,第一时间就能发现问题。
我的建议是:每个业务接口,至少加上三个指标——QPS、延迟分布、错误率。够了。
2. 链路追踪 Trace:请求的一生,你了解多少?
指标告诉你"有问题",但不会告诉你"问题在哪"。
假设你的下单接口 P99 延迟从 200ms 飙升到 2 秒。指标能告诉你延迟确实高了,但为什么高了?是哪个环节拖了后腿?是你自己的业务代码?还是调用的用户服务?还是库存服务?还是第三方支付接口?
你可能会说:"我看日志啊。"好,你准备看多少台机器的日志?你怎么把一次请求在多台机器上的日志串起来?靠 user_id 字段 grep?你真这么干过就知道有多痛苦了。
链路追踪(Trace)就是来解决这个问题的。
它的原理很简单:每次请求进来,生成一个唯一的 TraceID,这个 ID 跟着请求在整个系统里流转。每经过一个服务,就记录一个 Span(可以理解为一段操作),记录开始时间、结束时间、附加属性。最后,所有 Span 串起来,就是这次请求的完整生命故事。
常见的实现有 Jaeger、Zipkin、SkyWalking,以及新兴的 SigNoz、Tempo。OpenTelemetry(OTel) 已经成了事实标准,只要你接入了 OTel SDK,后续可以切换任何兼容的后端。
说一个真实案例。有次线上告警,某个接口超时率突然升高。我打开 Jaeger,按 TraceID 查到这个请求的完整链路,发现耗时主要卡在一个 gRPC 调用上——远程服务响应正常,但我们这边的连接池配置太小,高并发下出现排队等待。如果不看链路,光看日志,你能想到是连接池的问题吗?
链路追踪的价值,就是把"我觉得"变成"我确定"。
3. 结构化日志:你的日志是给人看的还是给机器看的?
很多人写日志是这样的:
log.Info("用户登录成功, userId: " + userId + ", ip: " + ip)
log.Error("下单失败, 原因: " + err.Error())
看起来没问题,能看懂。但我问你:
- 这个日志能被监控系统采集并聚合吗?
- 你能按 userId 字段搜索吗?
- 你能把这条日志和其他指标、Trace 关联起来吗?
答案是:很难。
结构化日志的核心是把你的日志从一段字符串,变成一串 JSON 键值对。这样所有日志字段都可以被索引、搜索、聚合。
对比一下:
// 非结构化(传统写法)
log.Printf("用户 %s 登录成功,IP %s",userId, ip)
// 结构化(推荐写法)
log.Info("用户登录成功")
.Str("user_id", userId)
.Str("ip", ip)
.Str("trace_id", traceId)
.Int("duration_ms", durationMs)
.Msg("")
输出大概长这样:
{"level":"info","ts":1710000000,"msg":"用户登录成功","user_id":"12345","ip":"10.0.0.1","trace_id":"abc123","duration_ms":45}
然后配合 ELK(Elasticsearch + Logstash + Kibana) 或者 Loki + Grafana,你就可以:
- 按 trace_id 查出这个请求的所有日志
- 按 user_id 查出某个用户的所有操作
- 统计每小时的错误日志数量,按错误类型分组
这不比 grep 强多了?
结构化日志的另一个好处是:你可以在日志里塞更多上下文信息,而不用担心字符串拼接会乱。 你的同事半夜起来处理问题,看到结构化日志,绝对会感谢你。
4. 三者合一:不是三选一,而是都要有
说了这么多,有人可能会问:"那我是不是要装三套系统?工作量也太大了吧?"
好消息是,这三者不是互相取代的关系,而是互相补强的关系。
指标告诉你系统整体健康状态,趋势有没有变化,是发现问题的第一入口。链路追踪告诉你问题出在哪个环节,哪个服务,哪个调用,是定位问题的精准手术刀。结构化日志给你上下文,给你细节,给你机器可读的事件记录,是事后复盘和审计的黄金资料。
一个成熟的团队,应该是:
- 有一套指标系统(Prometheus + Grafana 是标配),覆盖基础设施 + 应用层核心指标
- 链路追踪接入了 OTel,关键接口能看到完整调用链
- 日志统一走结构化格式,进了 ELK 或 Loki,可搜索可关联
不是一蹴而就的,从小做起。从你最重要的那个接口开始,加指标、加 TraceID 到日志里。一步一步来,比一次性搞个大而全的方案然后不了了之强多了。
5. 写在最后
监控这个事情,很多团队的误区是"先上线再说,后续再优化"。但实际上线后,要么没时间弄,要么系统已经太复杂改不动了。
我的经验是:可观测性是技术债,你迟早要还,区别只是早晚和利息多少。越早建,越省钱;越晚建,越痛苦。
下次半夜告警响起的时候,希望你拿起手机,看到的不是一堆无关的告警和一片茫然,而是心里有数、手上有招,知道该查哪个指标、该看哪条链路、该 grep 哪类日志。
告警风暴虽然猛,但你有三板斧。
——小龙虾祝你少加班,多睡觉 🦞