惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

小众软件
小众软件
N
News and Events Feed by Topic
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
The Cloudflare Blog
H
Heimdal Security Blog
Schneier on Security
Schneier on Security
Engineering at Meta
Engineering at Meta
Google Online Security Blog
Google Online Security Blog
宝玉的分享
宝玉的分享
AI
AI
The GitHub Blog
The GitHub Blog
MongoDB | Blog
MongoDB | Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
The Last Watchdog
The Last Watchdog
T
Troy Hunt's Blog
S
Security @ Cisco Blogs
H
Hacker News: Front Page
F
Fortinet All Blogs
博客园_首页
S
Secure Thoughts
N
News and Events Feed by Topic
P
Proofpoint News Feed
Microsoft Azure Blog
Microsoft Azure Blog
I
InfoQ
Spread Privacy
Spread Privacy
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Hugging Face - Blog
Hugging Face - Blog
Hacker News: Ask HN
Hacker News: Ask HN
C
CXSECURITY Database RSS Feed - CXSecurity.com
酷 壳 – CoolShell
酷 壳 – CoolShell
Stack Overflow Blog
Stack Overflow Blog
L
LINUX DO - 最新话题
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Schneier on Security
Know Your Adversary
Know Your Adversary
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Scott Helme
Scott Helme
P
Privacy & Cybersecurity Law Blog
S
Securelist
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
O
OpenAI News
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
PCI Perspectives
PCI Perspectives
L
LangChain Blog
雷峰网
雷峰网
Security Archives - TechRepublic
Security Archives - TechRepublic
V2EX - 技术
V2EX - 技术

Aimee's Blog

微服务与服务拆分:何时拆、怎么拆 异步与事件驱动架构:把协作从「打电话」改成「发消息」 高可用设计:怎么让系统尽量不宕机 可扩展性设计:怎么让系统加机器就能扛更多 缓存架构:多级缓存怎么搭 高并发三板斧:限流、熔断、降级 架构设计到底在设计什么 —— 从单体到微服务的演进 服务成本账:一个服务一个月烧多少钱 API 设计:好接口长什么样 消息队列:为什么要 MQ,以及丢失、重复、顺序怎么破 查询优化:慢查询怎么治 —— 索引 + 预聚合 缓存:为什么快、三大坑、和数据库怎么保持一致 存储与基建选型 —— 传统业务 vs AI 大模型时代 权限系统怎么设计?—— 从 RBAC 到 Google Zanzibar 鉴权怎么落地?—— 常见业务场景的方案选型与避坑 JWT、OAuth、Token 刷新 —— 一个全栈工程师的鉴权入门笔记 我把跑了五年的旧博客升级到了 3.0 大模型 AIGC 满天飞 推荐一些学习和资料网站 2024 冒个泡 冒个泡 阿里云新机器安装 自定义配置 vue 的 webpack.config.js 从输入url到页面展示 React API 使用demo 欢迎加入 aimee前端技术交流群 github actions 自动化部署 很久以前的美食美荟 无穷之路纪录片还不错哦 vue 模板编译和组件化 vue2 响应式原理 vue2 虚拟DOM原理 vue2 源码解析-初始化 vue2.0 router及简易实现 防抖和节流 Proxy 和 Object.defineProperty git rebase
可观测性:线上出问题怎么查
Aimee · 2026-06-15 · via Aimee's Blog

服务半夜报警、接口忽然变慢、用户说"点了没反应"——登上机器一脸懵:日志刷得飞快却不知道看哪条,问题到底出在哪个服务、哪一段、哪一行,全靠猜。

靠手动打几行日志、登上机器一行行翻去抓瞎,和有一套**可观测性(Observability)**体系按图索骥,是两种完全不同的体感。可观测性这套我也是补服务端才真正搞懂的——以前一直把它当成"监控的高级说法"。这篇就把它最该懂的事讲清楚:三大支柱各管什么、怎么配合,以及一次"接口变慢"该怎么顺藤摸瓜。配套一个零依赖 demo,跑一下就能在终端里看到指标、日志、链路三件套,和一次完整的排查路径。


一、可观测性是什么,为什么不是"监控"

老话叫"监控(Monitoring)":提前定好要盯的指标(CPU、内存、QPS),到了阈值就报警。它能回答你预先想到的问题——"CPU 超 80% 了吗"。

但线上的事故,十有八九是你没预想到的:某个接口在特定参数下变慢、两个服务之间的调用偶尔超时、一小撮用户命中了某个边界 bug。监控的看板上一切"正常",问题却真实存在。

可观测性换了个思路:不预设你会问什么,而是让系统主动吐出足够多的信号,事后你能从外部观测到的数据里,反推内部到底发生了什么——哪怕这个问题你从没预想过。

这套信号,业界收敛成了三大支柱:Metrics(指标)、Logging(日志)、Tracing(链路)。它们不是三选一,而是各回答一类问题、互相补位:

支柱一句话回答的问题数据形态
Metrics 指标系统现在健康吗整体状况:请求量多大、错误率多高、耗时多久按时间记录的(可聚合)
Logging 日志具体发生了什么某个时刻、某个请求里的细节:报了什么错一条条离散事件
Tracing 链路这个慢请求卡在哪一环一个请求跨多个服务时,每段花了多久一条请求的调用树

有个画面挺好记:指标是仪表盘(一眼看整体健康)、日志是行车记录仪(回放某一刻的细节)、链路是 GPS 轨迹(看请求一路经过哪些服务、在哪堵住)。下面逐个拆。

二、Metrics:系统现在健康吗

指标是按时间记录的数:每秒请求数、错误率、p95 耗时、内存占用……特点是可聚合——能求和、求平均、求分位数,画成随时间起伏的曲线。

该收哪些指标?RED 与 USE

指标可以收成千上万个,但盯不过来。业界有两套现成的方法论帮你抓重点:

  • RED(面向请求/服务):Rate(请求量)、Errors(错误数/率)、Duration(耗时)。任何一个对外提供接口的服务,盯住这三个数,基本就知道它健不健康。
  • USE(面向资源):Utilization(使用率)、Saturation(饱和度/排队)、Errors(错误)。看 CPU、内存、磁盘、连接池这类资源时用它。

对大多数业务服务,先把 RED 收齐就够用了。demo 里每个接口都按 RED 统计:请求量、错误数、平均耗时和 p95(95 分位耗时——比平均值更能反映"大多数人的最差体验",因为平均值会被少数极端值和大量快请求一起拉平)。

怎么把指标收上来:Prometheus 的"拉取"模型

收指标主流用 Prometheus。它的设计有个关键选择——拉取(pull):不是你的服务把数据"推"给它,而是每个服务自己暴露一个 /metrics 端点(就是一段纯文本),Prometheus 定时来拉

http_requests_total{path="/order"} 70
http_request_errors_total{path="/order"} 11
http_request_duration_ms_p95{path="/order"} 41

每行是一个带标签(label)的样本:{path="/order"} 就是标签,让同一个指标能按接口、机器、状态码等维度切分。demo 的 Metrics.toPrometheus() 导出的就是这种文本,Prometheus 来拉的也正是这个。

为什么是拉取而不是推送?拉取模型下,Prometheus 掌握全部目标列表——拉不到就知道这个实例挂了(顺带成了一种存活检查);扩缩容时只要服务注册进来就能被发现,不用每个实例反向配置往哪推。推送模型在某些场景(短命的批处理任务)更合适,Prometheus 也留了 Pushgateway 兜底,但常驻服务默认走拉取。

这些数存哪:时序数据库

指标这种"每个时间点一个数值"的数据,有专门的存法——时序数据库(TSDB,Time Series Database),Prometheus 自带一个。它针对"按时间追加写、按时间范围+标签查"做了重度优化,和你存订单用的关系型数据库是两套东西。

这正呼应一句老话:什么数据用什么存。 指标是时序数据 → 时序库;订单是结构化关系数据 → 关系库;日志是半结构化文本 → 搜索/日志库。用错了存储,要么慢要么贵。

收上来的指标,最后用 Grafana 画成看板(dashboard):一屏排开各服务的 RED 曲线,健不健康一眼就看出来。Prometheus 负责拉取和存储,Grafana 负责展示,是最经典的开源组合。

三、Logging:具体发生了什么

指标告诉你"/order 错误率涨到 15%",但为什么错,指标答不了——它只是个数。这时候要翻日志

别再打纯文本了:结构化日志

很多人的日志长这样:

[2026-06-07 12:00:01] order failed for user 123, reason: db timeout

人能读,但机器难检索。想统计"db timeout 出现了多少次""用户 123 的所有报错",得写正则去硬抠这行文本,既慢又脆(改一下格式就全废)。

结构化日志(Structured Logging)把日志写成带字段的 JSON:

{"ts":"2026-06-07T12:00:01Z","level":"error","traceId":"trace-0005","msg":"request failed","path":"/order","code":500,"reason":"db timeout"}

好处是机器可检索:level=error AND reason="db timeout"path="/order" 这样按字段精确过滤,而不是对一行文本碰运气。demo 里的 Logger 输出的就是这种 JSON 行。

日志级别:别乱打

每条日志带一个级别(level),用来表达"这条有多重要、平时要不要看":

级别什么时候用线上默认
debug排查时才需要的细节(变量值、命中没命中缓存)(太吵)
info正常的关键节点(请求进来了、任务跑完了)
warn不正常但还能扛(重试了一次、降级了)
error出错了、需要关注(异常、失败)

线上一般设个 minLevel=info,把 debug 过滤掉——否则 debug 噪音会把真正要紧的 error 淹没。demo 里就演示了这点:cache miss 是 debug 级,默认不输出。

反过来也别走极端:把什么都打成 error,告警就成了"狼来了"(见第五节)。级别打得准,日志才有用。

集中收集

单机日志躺在各自机器的磁盘上,十几台机器出了事一台台 grep 太痛苦。所以要集中收集:每台机器的日志统一汇到一处,再统一检索。

常见两套开源方案:ELK(Elasticsearch 存+搜、Logstash/Beats 采集、Kibana 看)偏重、检索强;Loki(配 Grafana 用)更轻,思路是"日志也按标签索引、像查指标一样查日志"。选哪个看日志量和检索需求,但集中这件事本身,是规模上来后绕不开的。

日志存哪、怎么查

ELK / Loki 本质在回答两个问题:日志存哪、怎么查出来。

存哪——日志是"半结构化、量大、写多读少"的数据,存法和订单这种关系数据完全不同:

  • Elasticsearch:倒排索引,全文检索 + 按任意字段查都强,但存储和成本高(日志量大很烧钱);
  • Loki:只给日志的标签建索引、正文压缩后扔对象存储,便宜不少,适合"按标签 + 时间范围捞";
  • 云日志服务(阿里云 SLS、AWS CloudWatch Logs):托管,开箱即用;
  • 冷热分层(控成本的关键):近期热日志放 ES / Loki 可快查,老日志归档到便宜的对象存储,各设保留期(热的存 7~30 天,冷的归档更久)。

怎么查——靠结构化字段精确过滤(level=error AND path=/order),而不是对一行文本碰运气(ES 配 Kibana、Loki 配 Grafana)。最关键的一招是用 traceId 串:拿一个出问题请求的 traceId,精确捞出它经过的所有日志——这正是第六节排查实战的最后一步。"结构化 + 带 traceId"不是为了好看,是为了查得到

项目里怎么落地

把日志真正用起来,几条实践:

  • 统一规范:全服务结构化 JSON,必带字段——时间、级别、服务名、traceId、关键业务 id(订单 / 用户)、msg。绝不打敏感信息(密码、token、身份证),这是安全红线。
  • 采集与应用解耦:应用只管把日志打到 stdout / 文件,由采集 agent(Filebeat / Fluentd)收走;日志洪峰大时,中间加一层 Kafka 缓冲削峰(就是消息队列那篇的削峰),再落到存储:
应用打日志 → 采集 agent(Filebeat/Fluentd) → [Kafka 缓冲] → ES/Loki 存储 → Kibana/Grafana 查
  • 日志和监控分工:告警优先用指标(Metrics)——错误率、延迟这些;日志负责事后查细节。别用"狂打 error 日志 + 关键字告警"代替指标监控,那样又贵又吵。
  • 控量控成本:日志量爆炸是真实账单。线上关掉 debug、热点路径别狂打、必要时采样;配合上面的冷热分层和保留期。

四、Tracing:这个慢请求卡在哪一环

前面两根支柱在单个服务里就够用。但现代系统往往是一个请求跨好几个服务:网关 → 订单服务 → 库存服务 → 数据库 → 缓存。这时候问题来了——

用户说"这个请求好慢",你看指标:订单服务 p95 高。但订单服务自己也在调下游,到底是它自己慢,还是它等的某个下游慢? 单看一个服务的指标和日志,串不起整条调用链

traceId 与 span:把一次请求串起来

**链路追踪(Tracing)**解决的就是这个。核心两个概念:

  • trace:一整个请求的完整调用过程,分配一个全局唯一的 traceId
  • span:这条 trace 里的一段工作(一次函数调用、一次下游请求、一次查库)。每个 span 记自己的开始/结束时间,于是有了耗时

一个请求经过的所有服务,都带着同一个 traceId;每段操作开一个 span,span 之间用 parent 关系串成一棵。把这棵树画出来,哪一段最长一目了然。demo 里 Tracer 就是这么干的,输出一条 /order 请求的 span 树:

HTTP /order             40ms  ██████████
  └─ cache.get           5ms  █
  └─ db.query           35ms  █████████

条形最长的 db.query 就是瓶颈——慢在数据库这一环,不用再瞎猜。

OpenTelemetry:采集的统一标准

要让 traceId 自动在服务间传递、span 自动采集,过去每家工具各搞一套,换工具就得重新埋点。现在业界统一到了 OpenTelemetry(OTel):一套厂商中立的标准 + SDK,定义了 trace/metric/log 怎么采集、怎么传递。你的代码按 OTel 埋点,后端想用 Jaeger、Grafana Tempo 还是别的来存储展示,随时能换,不被绑定。

关键认知:链路追踪的价值,在跨服务才真正凸显。单服务的慢,日志+指标基本能定位;一旦请求跨了三五个服务,没有 traceId 把它们缝在一起,排查就是大海捞针。

五、告警:别让"狼来了"

收了一堆指标,总不能盯着看板过日子。**告警(Alerting)**就是给指标设规则:超过阈值就自动通知人(电话、IM、邮件)。比如"/order 错误率 > 5% 持续 5 分钟"就报警。

听起来简单,真正的难点是别让告警变成噪音。两个最常见的坑:

  • 告警风暴:数据库一抖,依赖它的几十个服务同时报警,几百条消息糊脸,真正的根因反而被淹没。
  • 狼来了:阈值设太敏感、或者对一些"抖一下自己会恢复"的情况也报警,半夜被吵醒一看是误报——几次之后,大家就开始无视告警,等真出事也没人理了。

实践里靠这几招治噪音:

  1. 分级:区分 P0(立刻起床处理)/ P1(上班时间看)/ 提示(看板上有就行),只有最高级别才电话轰炸。
  2. 基于"症状"而非"原因"告警:对用户能感知的结果告警(错误率、延迟、可用性),而不是对每个内部指标都设阈值——内部指标抖动多,用户体验才是底线。
  3. 告警要可执行:一条好告警应该告诉值班人"出了什么事、影响多大、大概去哪看",而不是甩一个干巴巴的数字。
  4. 抑制与聚合:同一个根因引发的一连串告警,合并成一条;已知在处理的,自动静默。

一句话:告警的目标不是"报得多",而是"报得准"——每一次响铃都值得人放下手里的事去看。

六、排查实战:一次"接口变慢"怎么查

把三根支柱串起来,看一次真实的排查。场景:用户反馈下单变慢了。

第一步,看指标(Metrics)——定位"哪个接口"。 打开 Grafana,扫各接口的 RED。demo 的汇总表里:

接口        Rate  Errors  ErrRate  avg   p95
/order       70     11    15.7%    40ms  41ms   ← p95 最高,还有错误率
/user        73      0     0.0%    12ms  13ms
/health      60      0     0.0%     2ms   3ms

/order 的 p95 明显高于其它接口,还伴随 15.7% 的错误率——嫌疑接口锁定 /order。但指标只告诉你"它慢",没说慢在哪。

第二步,看链路(Tracing)——定位"慢在哪一段"。 抓一条 /order 的慢请求,看它的 span 树:

HTTP /order             40ms
  └─ cache.get           5ms
  └─ db.query           35ms   ← 这一段吃掉了大头

db.query 占了绝大部分耗时——瓶颈在数据库这一环。如果这里是跨服务调用,你还能顺着 traceId 跳到下游服务,继续往下看。

第三步,看日志(Logging)——定位"那一段到底报了什么"。 拿着这条请求的 traceId,去日志系统里精确过滤,翻 db 那一环附近的日志:

{"level":"error","traceId":"trace-0005","msg":"request failed","path":"/order","code":500,"reason":"db timeout"}

真相浮出水面:数据库超时了。接下来就是看那条慢 SQL 是缺索引、还是锁等待、还是连接池被打满——但定位到这一步,问题已经从"系统某处慢"收敛成了"/order 的某条 DB 查询超时",范围小了几个数量级

这就是三根支柱的配合,也是可观测性最该带走的一条心法:

指标定位"哪个接口慢" → 链路定位"慢在哪一段" → 日志定位"那一段到底报了什么"。

traceId 是把三者缝在一起的线:指标发现异常 → 找到一条问题请求的 traceId → 用它在链路里看调用树、在日志里捞细节。三根支柱各看一面,traceId 让它们指向同一个请求

七、一张表:症状 → 先看哪根支柱 → 看什么

线上遇到不同症状,优先翻哪根支柱,一张表带走:

症状先看哪根支柱具体看什么
报警说错误率涨了MetricsRED 里的 Errors,定位是哪个接口
某个接口变慢Metrics → Tracing先看 Duration/p95 锁定接口,再看 span 树找慢的那一段
一个请求跨服务、不知卡在哪Tracing按 traceId 看调用树,哪段 span 最长
用户报某次操作失败Logging按 traceId / 用户 ID 过滤,看 error 日志的 reason
想知道整体健不健康MetricsGrafana 看板,各服务 RED 曲线
半夜被告警吵醒、疑似误报Metrics + 告警规则对照阈值是否过敏感、是不是"狼来了"
容量/资源是否到顶Metrics(USE)CPU/内存/连接池的使用率与饱和度

一句话收尾:三根支柱不是装着好看的,而是出事时让你从"一脸懵"变成"按图索骥"的地图。 平时埋好点、收好数,真出事那一刻就知道值了。


名词解释

  • 可观测性(Observability):从系统对外吐出的数据(指标/日志/链路)反推内部状态的能力,重点是能查清没预想到的问题;区别于只盯预设指标的"监控"。
  • Metrics(指标):按时间记录、可聚合的数值(请求量、错误率、耗时等)。
  • Logging(日志):一条条离散的事件记录,记录"某时刻发生了什么"。
  • Tracing(链路追踪):把一个请求跨多个服务的完整调用过程串起来,看每段耗时。
  • RED 方法:面向请求的指标方法论——Rate(请求量)、Errors(错误)、Duration(耗时)。
  • USE 方法:面向资源的指标方法论——Utilization(使用率)、Saturation(饱和度)、Errors(错误)。
  • p95 / 分位数:把耗时排序后取第 95% 位的值,反映"大多数请求的较差体验",比平均值更真实。
  • Prometheus:开源的指标采集与存储系统,采用**拉取(pull)**模型,自带时序数据库。
  • 拉取 / 推送(pull / push):Prometheus 主动来服务的 /metrics 端点拉数据(pull),而非服务推给它(push)。
  • Grafana:开源的可视化看板工具,常配 Prometheus / Loki 画指标与日志看板。
  • 时序数据库(TSDB):为"按时间追加写、按时间范围+标签查"优化的数据库,专门存指标。
  • 结构化日志(Structured Logging):写成带字段的 JSON 而非纯文本的日志,机器可按字段精确检索。
  • 日志级别(Log Level):debug / info / warn / error,表达日志的重要程度,线上常过滤掉 debug。
  • ELK / Loki:两套开源的日志集中收集与检索方案;ELK 偏重检索强,Loki 更轻、按标签索引。
  • 日志采集 agent(Filebeat / Fluentd):部署在机器上、把应用日志收集转发到中心存储的工具,让应用和日志存储解耦。
  • 冷热分层:近期常查的"热"日志放可快查的存储(ES / Loki),老的"冷"日志归档到便宜的对象存储,各设保留期,平衡查询速度与成本。
  • traceId / span:traceId 是一个请求的全局唯一标识,串起它经过的所有服务;span 是其中一段工作,记录起止时间和耗时。
  • OpenTelemetry(OTel):厂商中立的可观测数据采集标准与 SDK,埋点一次、后端可换(Jaeger、Tempo 等)。
  • Jaeger:开源的链路追踪后端,负责存储与展示 trace。
  • 告警(Alerting):对指标设阈值规则、超限自动通知人;难点是分级、降噪、避免"狼来了"。

配套 demo:backend-notes/03-reliability/obs-demo —— 跑一下:打印结构化日志(JSON + 级别过滤)、每个接口的 RED 汇总并导出 Prometheus 文本格式、一条慢请求的 span 树,最后串成一次"接口变慢"的完整排查路径。零依赖,不用装 Prometheus / Grafana。

本文属《研发都要懂的事》· "跑得稳"专题。完整代码与系列在 GitHub · backend-notes