【架构实战】可观测性三支柱实战:Metrics、Logging、Tracing 如何统一落地 📅 发布时间:2026/8/19 15:59:23 👁 浏览次数: 【架构实战】可观测性三支柱实战Metrics、Logging、Tracing 如何统一落地上午我们聊了全链路追踪用 OpenTelemetry 把每一次调用串成一条 Trace。但单靠 Trace 是不够的——Trace 回答这一次请求为什么慢Metrics 回答系统整体水位怎么样Logging 回答具体报了什么错。这三者合起来才叫可观测性Observability。很多团队踩过的坑是Prometheus 一套、ELK 一套、SkyWalking 一套三套系统各玩各的指标对不上、日志串不起、链路查不到根因。排查一个问题要在三个控制台之间来回跳光对时间就能对半小时。今天聊清楚可观测性三支柱到底是什么、怎么选型、以及如何用 OpenTelemetry 把它们统一到一条技术栈里。一、三支柱谁解决什么问题1.1 Metrics指标系统健康度Metrics 是聚合后的数值回答系统现在怎么样每秒请求数QPS、错误率、延迟分位数P50/P95/P99CPU、内存、磁盘、网络队列堆积量、连接池使用率、GC 暂停时间特点数据量小、可聚合、可告警。适合做 dashboard 和告警规则但看不到具体哪一次请求。1.2 Logging日志事件详情Logging 是离散的事件记录回答到底发生了什么业务日志下单成功、支付回调、库存扣减异常日志Exception 堆栈、SQL 执行失败访问日志谁在什么时间调了什么接口特点信息最丰富、最细粒度但数据量巨大且非结构化或半结构化检索需要索引。1.3 Tracing追踪调用链路Tracing 是跨服务的调用关系记录回答一次请求经过了哪些服务、每步花了多久Trace ID 贯穿所有服务每个 Span 记录一个操作的耗时、状态、属性还原完整的调用树定位最慢环节特点结构清晰、跨服务串联但采样成本高无法记录所有请求的所有细节。1.4 三者的关系一个经典例子用户反馈下单很慢。三种工具各自怎么用Metrics 先定位查 Prometheus发现下单接口 P99 从 200ms 涨到 2s错误率 3%。确认问题存在范围在订单服务。Tracing 找环节查 Trace发现order-service调用inventory-service的 Span 耗时 1.8s其他环节都正常。定位到库存服务。Logging 查根因查库存服务的日志发现 Redis 连接池获取超时堆栈指向JedisConnectionException: timeout。根因是连接池配置过小 Redis 抖动。三者是递进关系缺一不可。只靠日志你得猜是哪个服务慢只靠指标你不知道具体哪次请求只靠 Trace你不知道底层报了什么错。二、选型主流方案对比2.1 Metrics 领域方案特点适合场景Prometheus Grafana拉模式、PromQL 强大、生态最全事实标准K8s 环境首选VictoriaMetricsPrometheus 兼容、单机性能强、存储省大规模指标、长期存储Thanos / MimirPrometheus 高可用 长期存储多集群、多租户场景InfluxDB写模型不同、持续查询时序分析类需求较少用于 K8s2.2 Logging 领域方案特点适合场景ELKElasticsearch Logstash Kibana全文检索强、生态成熟经典方案中小规模首选EFKFluentd/Fluent Bit ES采集轻量、配置灵活K8s 环境常用Loki Grafana只索引标签不索引内容、成本低与 Prometheus 共用 Grafana 的团队ClickHouse 自建写入极快、压缩比高日志量特别大的场景2.3 Tracing 领域方案特点适合场景JaegerOTel 原生支持、轻量中小团队快速落地Zipkin老牌、简单历史项目迁移成本低SkyWalking无侵入 Agent、中文文档全Java 系、不想改代码的团队Grafana Tempo与 Grafana 生态深度集成已用 Grafana 全家桶的团队2.4 关键判断到底选几套很多团队的误区是Metrics 选 Prometheus、Logging 选 ELK、Tracing 选 SkyWalking都是各自领域最强的——然后发现三套系统的数据互相割裂告警说 QPS 暴涨但 Trace 里查不到对应时段的数据采样率不同日志里有关联的 requestId但 Tracing 用另一套 traceId对不上三个 UI 三套查询语法值班同学要记三本手册正确思路是优先选能统一的栈。要么全上 Grafana 系Prometheus Loki Tempo要么全上 OTel 生态统一 exporter collector后端按需接。三、统一的关键OpenTelemetryOpenTelemetryOTel已经成了可观测性的事实标准——它用一套 SDK 统一了 Metrics、Logging、Tracing 三大信号的采集通过 OTLP 协议发给后端。3.1 OTel 的架构应用 (Java/Go/Python/Node...) │ OTel SDK 采集三大信号 ▼ OTel Collector (agent 模式 / 独立部署) │ OTLP / Prometheus / Jaeger / 其他协议 ▼ 后端存储 可视化 (PrometheusGrafana / Tempo / Loki / ES...)核心组件OTel SDK打进应用里的库自动或手动埋点生成 Metrics、Logs、Traces 三种数据OTel Collector独立的数据管道服务接收、处理、转发遥测数据支持路由、采样、脱敏、批处理OTLP 协议三种信号统一走这个协议传输一套网络、一套鉴权3.2 三大信号如何关联OTel 的杀手锏是通过 Context 关联三种信号每个请求生成trace_id和span_id日志里自动注入当前 Trace 的trace_id指标采集时带上服务名、环境等标签这样就能实现从指标下钻到日志从日志跳到 Trace# OTel SDK 配置示例环境变量OTEL_SERVICE_NAME:order-serviceOTEL_EXPORTER_OTLP_ENDPOINT:http://otel-collector:4317OTEL_METRICS_EXPORTER:otlpOTEL_LOGS_EXPORTER:otlpOTEL_TRACES_EXPORTER:otlpOTEL_TRACES_SAMPLER:parentbased_traceidratioOTEL_TRACES_SAMPLER_ARG:0.1只要配置了这些应用启动后自动采集 HTTP/RPC 调用指标QPS、延迟、错误率自动生成调用链 Span日志自动带上 trace_id3.3 日志关联 Trace 的关键操作日志要能和 Trace 关联必须把trace_id注入到日志字段里。以 Java Logback 为例pattern%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [traceId%X{trace_id:-}, spanId%X{span_id:-}] - %msg%n/patternOTel Java Agent 会自动把 trace_id/span_id 放入 MDCMapped Diagnostic ContextLogback 里用%X{trace_id}就能取到。排查问题时从告警或 Dashboard 看到异常日志检索trace_idxxx找到那一次请求的所有日志点开同 ID 的 Trace看完整调用链从三套系统来回切变成一个 ID 贯穿到底。四、落地架构实战一套完整的可观测性平台下面给出一套经过验证的落地方案中小团队5~50个服务可以直接照抄。4.1 整体架构┌─ K8s 集群 ──────────────────────────────────────┐ │ 应用 Pod (OTel SDK / Agent 埋点) │ │ │ OTLP (gRPC) │ │ OTel Collector (DaemonSet, 每个节点一个) │ │ ├──→ Prometheus (指标) ──→ Grafana (看板) │ │ ├──→ Tempo (Trace) ────→ Grafana (链路视图) │ │ └──→ Loki (日志) ──────→ Grafana (日志检索) │ │ │ │ │ └──→ Alertmanager → 钉钉/企微 │ └─────────────────────────────────────────────────┘选型理由Prometheus Grafana指标事实标准Grafana 一个入口看所有Tempo Loki同为 Grafana 系天然联动trace_id一键跳转OTel Collector统一接入未来换后端不用改应用Alertmanager统一告警路由到钉钉/企业微信/邮件4.2 OTel Collector 配置示例# collector-config.yamlreceivers:otlp:protocols:grpc:endpoint:0.0.0.0:4317http:endpoint:0.0.0.0:4318processors:batch:timeout:5ssend_batch_size:1024memory_limiter:check_interval:1slimit_mib:512exporters:prometheus:endpoint:0.0.0.0:9091namespace:otelotlp/tempo:endpoint:tempo:4317tls:insecure:trueotlp/loki:endpoint:loki:4317tls:insecure:trueservice:pipelines:metrics:receivers:[otlp]processors:[memory_limiter,batch]exporters:[prometheus]traces:receivers:[otlp]processors:[memory_limiter,batch]exporters:[otlp/tempo]logs:receivers:[otlp]processors:[memory_limiter,batch]exporters:[otlp/loki]4.3 告警规则实战指标采进来了不配告警等于白采。经典告警规则groups:-name:service-alertsrules:# 错误率超过 5% 持续 5 分钟-alert:HighErrorRateexpr:|sum(rate(http_server_duration_milliseconds_count{ status_code~5.. }[5m])) by (service_name) / sum(rate(http_server_duration_milliseconds_count[5m])) by (service_name) 0.05for:5mlabels:severity:criticalannotations:summary:{{ $labels.service_name }} 错误率超过 5%description:当前错误率 {{ $value | humanizePercentage }}# P99 延迟超过 1s 持续 10 分钟-alert:HighLatencyexpr:|histogram_quantile(0.99, sum(rate(http_server_duration_milliseconds_bucket[5m])) by (service_name, le)) 1000for:10mlabels:severity:warningannotations:summary:{{ $labels.service_name }} P99 延迟超过 1s告警分层建议P0立即处理错误率 5%、服务完全不可用、磁盘将满P130分钟内P99 超阈值、队列堆积、连接池打满P2当天处理容量水位CPU/内存 80%、证书即将过期4.4 日志采集最佳实践日志规范是日志平台能用的前提强烈建议强制这几条JSON 结构化输出别用自由文本统一 JSON字段固定必带 trace_id / span_id与 Trace 关联的前提分级打日志ERROR 只打可恢复/不可恢复的异常别把异常当正常流程别打敏感信息手机号、身份证、密码一律脱敏或干脆不打日志级别可动态调整线上排查时能把某个服务的日志临时调到 DEBUG五、踩坑清单真实经验坑1Metrics 和 Tracing 的采样率不一致现象告警说 QPS 3000但 Trace 里同一时段只有 300 条。原因Trace 开了 10% 采样但没意识到 Metrics 是全额统计的。解决明确Metrics 全额、Trace 采样是正常设计。需要精确排查时临时把采样率调到 100%查完再调回。坑2日志和 Trace 对不上现象日志里有 trace_id但在 Tempo 里查不到。原因异步线程没传递 Context线程池里的任务丢了 trace_id用了消息队列Consumer 侧没继续传递 traceparent 头采样导致这条日志的 Trace 被采样掉了解决线程池用 OTel 的Context.taskWrapping包装任务MQ 消息头里透传traceparent日志检索时对有 trace_id 但查不到 Trace的情况有预期坑3Collector 成为瓶颈现象接入 OTel 后应用 RT 没变但 Collector 的 CPU 飙到 90%。原因Collector 单点部署所有信号都压在一个 Pod 上batch 配置不当每条数据都立刻转发。解决Collector 用 DaemonSet 部署每个节点一个各收各的开 batch processor5s 攒一批再发memory_limiter 防止 OOM坑4日志量爆炸存储扛不住现象Loki 磁盘 3 个月满了查询越来越慢。原因DEBUG 日志全量采集日志没分级保留策略没配。解决生产只采 INFO 以上DEBUG 按需开按环境分流dev/test 日志保留 7 天生产保留 30~90 天Loki 配 retentionES 配 ILM 冷热分层坑5告警风暴现象凌晨 3 点钉钉被刷了 200 条告警值班同学直接静音。原因告警规则没配for持续时间抖动一下也告警没做聚合。解决所有告警加for: 5m持续 5 分钟才触发用sum by (service_name)聚合别每条实例都发配 Alertmanager 的group_wait/group_interval抑制重复告警六、渐进式落地路线别想着一步到位建议按这个节奏推进第一阶段1~2周指标先行部署 Prometheus Grafana服务接入 OTel SDK采集 HTTP 指标配置 5 条核心告警错误率、延迟、CPU、内存、磁盘产出值班同学有系统健康总览了第二阶段2~4周Trace 补上部署 Tempo或 Jaeger所有服务开启 Trace 导出先开 10% 采样打通从 Grafana 面板点进 Trace的路径产出慢请求能定位到具体服务了第三阶段1~2月日志统一 三信号联动部署 Loki或迁移 ELK日志接入 OTel注入 trace_id实现指标 → 日志 → Trace一键下钻产出完整可观测性闭环第四阶段按需深化SLO 管理错误预算链路拓扑自动生成服务依赖图日志异常检测AI 辅助可选七、总结一句话Metrics 告诉你哪里病了Logging 告诉你病根是什么Tracing 告诉你病是怎么传染的。三者结合才是完整的可观测性。落地要点回顾选型看生态优先选能统一的栈Grafana 系或 OTel 系别三套系统割裂标准看 OTel一套 SDK、一套协议OTLP采三种信号未来换后端不改应用关联靠 trace_id日志注入 trace_id才能实现三信号联动下钻告警要克制for 时长 聚合 分级别让告警风暴毁掉告警体系落地要渐进先指标、再链路、后日志每一步都能立刻产生价值可观测性建设是典型的越早做越省等系统出过两次查了 3 小时没定位到根因的事故团队的决心就有了。与其等事故不如现在就把三支柱搭起来——你不需要最好的工具你需要的是一套能用、能联动、查得到底的体系。