ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

可观测性总论

2026/8/22 22:16:26 拓冰建站 浏览量
可观测性总论 目录1. 可观测性在解决什么问题2. 三大支柱怎么分工3. SLI / SLO / SLA 和告警4. OpenTelemetry 在总论里的位置5. 三类数据靠什么关联本周目标先建立整张地图再把 Logs 吃透。Metrics、Traces 后面几周展开这里只要求知道它们各管什么、怎么和日志连上。1. 可观测性在解决什么问题监控老问法是「我预先设的灯亮了没有」CPU 80%、进程挂了。可观测性问的是系统内部没预想到的问题能不能从它对外发出的遥测里推断出来必看文档​编辑Observability primer英文短最重要​编辑What is OpenTelemetry?先读「什么是 / 不是」​编辑Signals 总览中文对照​编辑阳明 · OpenObserve 介绍里的三柱部分先当概念先别部署微服务之后一次请求会跨网关、订单、库存、支付。没有统一遥测就只能一台台翻机器日志。可观测性要让你能回答三类问题问题主要靠刚才发生了什么谁失败了原文怎么说Logs这一次请求怎么走慢在哪一跳Traces整体是否变差该不该告警、该扩容吗Metrics这三类合称三大支柱Three Pillars。OpenTelemetry 把它们叫Signals信号。微服务架构简单说就是把一个大系统拆成一堆小服务每个服务只管一块业务各自独立开发、部署、扩容。单体 vs 微服务单体Monolith整个应用打成一个包。登录、订单、支付、库存都在同一个进程里。改一处往往要整包重新发布。微服务Microservices按业务边界拆开。例如用户服务订单服务支付服务库存服务每个服务自己有代码、数据库常见做法通过网络互相调用。微服务的核心特点按业务拆分不是按技术层Controller / Service / DAO切而是按能独立交付的业务能力切。独立部署改支付逻辑只发支付服务不必动订单、用户。独立扩容下单高峰只扩订单服务支付不忙就不用一起加机器。技术可以不同订单用 Java推荐用 Python只要约定好接口即可实际团队通常会统一栈减少成本。服务之间用网络通信HTTP/gRPC、消息队列Kafka、RabbitMQ等而不是同一个进程里直接函数调用。监控 VS 可观测 业务例子监控回答「坏没坏、有多坏」可观测回答「这一单为什么坏、卡在哪」。业务场景财务要把一笔入库写成付款明细采购单PO2026SKUDEB-MF2入库单PRQA20260818。接口是test的/stock。用户侧现象入库同步失败财务明细没写上。监控会看到什么预先设好的灯看什么典型告警/save_stock错误率5 分钟错误率 1%接口耗时P99 1s进程/机器CPU、内存、实例是否存活这条真实请求总耗时约 867ms机器多半是绿的。监控最多告诉你财务入库接口又失败了一次。它回答不了是哪一单是采购挂了、主数据挂了还是财务自己写库失败要改代码、清重复数据还是重试没有预先做「按采购单号 / SKU / 唯一键冲突」的仪表盘监控就看不见根因。可观测会看到什么同一请求带上trace_idb15b931543f479ecda3e。从链路能还原这一单怎么走/stock财务867ms失败→ 拉抵扣方式、供应商、付款方式base都成功→ 拉采购单 PO202606purchase成功→ 本地 insert payment_item → 撞唯一索引失败Span 上的 exception 直接写出原因Duplicate entry 7263-216961-DEB-MF2-216209-R1for key payment_item.purchase_order_id_stock_id_relation_id_sku日志里还能对上采购侧获取采购单的 info。结论是下游都正常是同一单被写了第二次。这就是可观测不靠事先画好的图从这次请求自己吐出的 Trace / Log 反推出原因。对照记监控可观测问法/stock正不正常这一单为什么失败数据预先聚合的数字错误率、P99这次请求的链路 日志 异常原文这条故障「又失败了」「重复 insert 付款明细唯一键冲突」下一步扩容、重启、盯红灯查是否重复投递或把saveStock()改成先查再写一句话监控发现「入库同步在报错」可观测定位到「PO202606 这条明细已经存在」。总结光有日志不够日志是各服务自己记的句子默认对不齐也还原不出「这一单经过了谁」。光有指标找不到哪一次指标是先算成数字再存的单次请求在聚合时被抹掉了。trace_id就是那根绳子把同一次请求的 span 和日志串成一条可查的链。1为什么光有日志不够日志回答的是「某个进程在某一刻说了什么」。它不保证能对上同一单。finance打一句 SQL 报错purchase打一句「获取采购单结束」。没有同一 ID你只能靠时间戳大概猜并发一高就对错单。能看出调用顺序和耗时。 日志很少带「我是谁的子调用、我花了多少毫秒」。看不到/stock先调了 base、再调 purchase、最后在本地 insert 挂掉。覆盖是完整的。 那条真实故障里duplicate key 写在 Trace 的 exception 上cht_log_aws里财务这条 error 甚至没打出来只有采购的 info。只翻日志会以为「采购查单很正常财务没报错」。日志适合当现场笔录缺少关联时它是一堆散页不是一本完整卷宗。2为什么光有指标找不到「哪一次请求」慢指标是先聚合再存储 的时间序列例如/stock 错误率 1%/stock P99 900ms1 万次调用会被收成几个数字。慢的那一次、失败的那一次在平均数/百分位里已经混进去了。所以指标能告诉你现在接口变差了没有该不该告警、要不要扩容它 天生没有PO202606这一单、也没有「是/get_payment_method_all的 91ms 还是最后 insert 失败」。要找「哪一次」必须留下那一次请求的身份而不是只留下汇总。3trace_id存在是为了干什么给同一次用户/业务请求发一个通行证沿调用链一直带下去。一次/stock会跨财务、主数据、采购每个框是一个 span整棵树是一条 trace。所有 span、以及打了关联字段的日志都带同一个trace_id日志里常叫trace。有了它才能问问题靠什么这一单走了哪些服务同一trace_id下的 span 树时间花在哪一跳各 span 的duration哪一步失败了哪个 span 的ERROR/ exception那一步当时打了什么日志cht_log_aws里trace 同一个 id对照那条真实请求b15b931543f479ecda3e指标最多显示/stock失败了 1 次、耗时约 867ms日志采购侧能对上PO202606财务的 duplicate key 可能根本不在日志里trace_id把 35 个 span 收成一棵树并指出根因是payment_item唯一键冲突下游其实都成功所以trace_id不是又一种日志字段它是 跨服务把「这一次」钉住的主键。没有它监控只能说系统不太好日志只能说某台机器喊过一嗓子谁也还原不了这一单。2. 三大支柱怎么分工现象用户说「下单很慢 / 失败」│├─ Metrics错误率、P99 是否在涨 → 发现「有问题、有多大」├─ Traces这条请求经过哪些 span → 定位「哪一跳」└─ Logs那一跳打印的错误原文 → 看清「为什么」对照记忆Logs离散事件。一条日志 某一个时刻、某一个进程说的一句话加上字段。Traces一次请求的因果链。一条 Trace 多个 Span 组成的树。Metrics预先聚合好的数字时间序列。一条指标 名字 标签 时间 数值。它们不是互相替代只看日志细节全但海量、难看趋势、难设稳定告警。只看指标告警便宜但看不到「这一单」和错误原文。只看链路能看路径但业务上下文、堆栈、审计往往在日志里。排障标准顺序告警多来自 Metrics→ 筛慢/错 Trace → 用同一 ID 拉 Logs → 下结论。3. SLI / SLO / SLA 和告警词含义例子SLIService Level Indicator测量的东西成功请求比例、P99 延迟SLOService Level Objective承诺要达到的内部目标30 天成功率为 99.9%SLAService Level Agreement对客户的合同不达标常有赔偿可用性 99.5%关系先定 SLI测什么→ 再定 SLO多好→ SLA 是对外合同通常松于 SLO。告警应尽量对准 SLI 正在变坏错误率、延迟而不是「磁盘 70%」这种未必影响用户的机器指标。本周日志侧能做的告警通常是错误日志量突增、某service的levelerror持续出现。更稳的错误率告警放在 Metrics。RED 方法给在线服务用建立印象即可Rate每秒请求数Errors失败比例Duration耗时分布P95 / P99USE利用率、饱和度、错误更偏主机/中间件。4. OpenTelemetry 在总论里的位置OTEL 不是后端是怎么采集、怎么描述、怎么运出去的标准。应用插桩API / SDK / 自动 instrumentation→ OTLPgRPC 4317 或 HTTP 4318→ Collector可选过滤、加字段、分流→ OpenObserve / Jaeger / Prometheus …只要记住四点三种信号Logs、Metrics、Traces 可以同一套出口。OTLP官方传输协议。Collector独立进程应用不必直连每个后端。语义约定字段名尽量统一如service.name、trace_id。Jaeger / Zipkin 是 Trace 后端日志实验以现用 OpenObserve 为主。应用插桩就是在程序运行路径上多插一层采集代码业务还是「入库、写付款明细」额外记下这次请求走了谁、花了多久、有没有报错、带了哪个trace_id。它不改业务结果只让系统变得可观测。插在哪、产出什么一次/stock里插桩通常打在这些点上打点位置记下来的东西真实请求里对应HTTP 入口接口名、状态、总耗时根 span/stock调下游 HTTP/gRPC调了哪个服务、耗时/get_purchase_orders_ids、/get_supplier_ext数据库SQL 类型、耗时一串db_query异常错误类型和堆栈Duplicate entry ... payment_item这些记录变成 span同一次请求共用一个trace_id。没有插桩O2 里就没有这条瀑布图。财务服务栈里已经能看到插桩痕迹JaegerMiddleware、vendor/cht/trace。请求进来时中间件开一个 span、往下游传trace_id异常写进 span 的 exception——所以后来才能在 O2 里还原那一单。两种常见做法自动插桩用 Agent / 框架 / OpenTelemetry给 HTTP、DB、消息队列自动打点。业务代码几乎不用改覆盖更全。所看到的db_query、下游 HTTP span多半是这类。手动插桩在关键业务上自己写start_span(saveStock)。适合「按采购单号入库」这种自动层看不懂的业务语义。生产上通常是自动打基础设施 手动补业务名和订单号。和相邻几个词的区别说法含义插桩往运行路径里插入采集点埋点产品侧更常用记「用户点了什么」探针 / Agent做自动插桩的那一层Java Agent 等OTLP把打好的数据运到 OpenObserve 的协议插桩负责采集OTLP 负责运送O2 负责存和查。为什么微服务特别需要它单体里一次请求在同一进程打日志、打断点就够。微服务一次入库会跨财务、主数据、采购。没有插桩就只剩各服务自己的日志对不上「哪一次」也画不出谁调谁。插桩 给应用装上「这一单」的记录仪trace_id是记录仪上的编号。埋点就是在产品关键动作上记一笔「谁、在哪、做了什么」。典型是用户行为不是服务内部的调用链。例如打开入库页、点「同步库存」、提交失败。记下的是事件用来做转化、漏斗、运营分析。一条埋点通常是事件名 时间 用户/设备 页面 业务参数例如click_save_stock用户 U123采购单PO202606。埋点和插桩差在哪埋点插桩目的用户/业务做了什么这次请求在系统里怎么走、慢在哪谁关心产品、运营、增长研发、运维、排障产物点击、曝光、转化事件Trace / Metrics / Logs埋点看人怎么用产品插桩看系统怎么处理这一次请求。OTEL 是整套标准OTLP 是其中的运输协议。是什么OTELOpenTelemetry怎么采集、怎么描述 Traces / Metrics / LogsAPI、SDK、插桩、语义约定OTLPOpenTelemetry Protocol把已经采好的数据打包发出去的协议怎么放在一条链上应用OTEL 插桩 / SDK│ OTLPgRPC :4317 或 HTTP :4318▼Collector可选│▼OpenObserve / Jaeger / 其他后端OTEL 负责采集OTLP 负责运送O2 负责存储和查询。容易混的点说「接了 OTEL」通常指用 OpenTelemetry 的 SDK/Agent 打点。说「走 OTLP」通常指数据用 OTLP 传到 Collector 或 O2而不是 Jaeger/Zipkin 的老协议。可以只用 OTEL 采集、却用别的协议导出现在最常见的是 OTEL 采集 OTLP 导出。OTEL 是工具箱OTLP 是工具箱里那根网线。5. 三类数据靠什么关联没有约定字段三大支柱就是三座孤岛。最低限度要统一字段作用trace_id/trace/traceid把「这一次请求」的日志和 Span 对上span_id对上「这一跳」可选日志里有更好service.name/service是哪个服务打的环境 / 版本env、deployment.environment、service.version避免把测试和生产混在一起你们 OpenObserve 测试环境default组织里日志流cht_log_aws、cht_log_aws_audit链路流test_otel日志关联字段名叫trace也兼容trace_id/traceidcht_log_aws已建索引的字段包括service、appcode、trace、traceid所以本周操作的关键句是SELECT * FROM cht_log_aws WHERE trace trace_id OR trace_id trace_id OR traceid trace_id