可观测性(Observability)是指通过系统的外部输出(指标、日志、追踪)推断其内部状态的能力。在分布式系统中,可观测性是诊断问题、优化性能、保障可靠性的核心手段。可观测性不是"监控的升级版",而是理解系统行为的方式——好的可观测性设计让你在问题发生前感知风险,在问题发生时快速定位,在问题发生后复盘改进。
本文系统性地介绍可观测性的三大支柱(Metrics/Logs/Traces)、技术栈选型、黄金信号、告警设计、成本优化以及 Hugo 在实际项目中的踩坑经验与内部约定。
指标的特质是可聚合——将大量原始数据压缩为统计量,便于趋势分析和告警触发。
| 类型 | 说明 | 示例 |
|---|---|---|
| 计数器(Counter) | 单调递增,只增不减 | 请求总数、错误总数、处理订单数 |
| 仪表盘(Gauge) | 当前瞬时值,可上下波动 | CPU 使用率、内存占用、队列深度、连接数 |
| 直方图(Histogram) | 分布统计,分桶计数 | 请求延迟分布、响应体大小分布 |
| 汇总(Summary) | 客户端计算的滑动窗口分位数 | P50/P95/P99 延迟(客户端预计算) |
Hugo 的踩坑记录:早期项目使用 Summary 类型计算 P99,发现不同实例的滑动窗口不一致,导致聚合后的 P99 偏差很大。后来统一改用 Histogram + 服务端聚合,虽然存储成本略高,但数据一致性更好。
标签(Label/Tag)是指标的多维分析基础,设计不当会导致"指标爆炸"(Cardinality Explosion)。
推荐的三维标签体系:
what → 指标名称(http_requests_total)
where → 位置维度(service=payment, region=ap-southeast-1)
which → 细分维度(method=POST, endpoint=/api/v1/pay, status=200)
高基数陷阱:
/users/12345/profile 和 /users/67890/profile 变成不同序列Hugo 的内部约定:
user_tier=free|premium|enterprise 而非 user_id=xxx/users/{id}/profile高频场景下需要权衡精度与成本:
| 策略 | 适用场景 | 实现方式 |
|---|---|---|
| 全量采集 | 核心业务指标(订单、支付) | 无采样,100% 上报 |
| 时间分片 | 高频计数器(HTTP 请求) | 每 10 秒聚合为 1 个数据点 |
| 概率采样 | 非关键指标(健康检查) | 只采集 1% 样本 |
| 自适应采样 | 流量波动大的场景 | 低流量时全量,高流量时降采样 |
日志的特质是不可聚合但高维度——保留最丰富的上下文信息,适合根因分析。
Hugo 团队强制要求 JSON 格式结构化日志,示例:
{
"timestamp": "2026-04-20T10:15:30.123Z",
"level": "ERROR",
"service": "payment-service",
"trace_id": "abc123def456",
"span_id": "span-789",
"thread": "http-worker-3",
"message": "订单支付处理失败",
"context": {
"order_id": "ORD-2026-001",
"user_id": "USR-8888",
"amount": 199.99,
"currency": "CNY",
"payment_method": "alipay"
},
"error": {
"type": "PaymentGatewayTimeout",
"message": "Gateway response timeout after 30s",
"stack_trace": "..."
}
}
| 级别 | 使用场景 | 生产环境保留策略 |
|---|---|---|
| ERROR | 需要人工介入的异常 | 永久保留 |
| WARN | 潜在问题但不影响主流程 | 保留 90 天 |
| INFO | 关键业务事件 | 保留 30 天 |
| DEBUG | 开发调试信息 | 默认不采集,问题排查时动态开启 |
| TRACE | 最详细的调用链信息 | 仅开发环境使用 |
Hugo 的踩坑记录:
同一线程/请求内的所有日志必须共享以下上下文:
请求级上下文(贯穿全链路):
- trace_id: 链路追踪 ID
- request_id: 请求唯一标识
- user_id: 用户标识(脱敏)
- session_id: 会话标识
服务级上下文(单次调用内):
- service_name: 服务名
- instance_id: 实例标识
- version: 服务版本
实现方式:使用 MDC(Mapped Diagnostic Context)或 ThreadLocal 存储,在日志框架中自动注入。
| 数据类型 | 脱敏方式 | 示例 |
|---|---|---|
| 身份证号 | 保留前3后4 | 110***********1234 |
| 银行卡号 | 保留前6后4 | 622202****8888 |
| 手机号 | 保留前3后4 | 138****8888 |
| 密码/Token | 完全替换 | [REDACTED] |
| 邮箱 | 保留域名 | ***@gmail.com |
Hugo 的内部约定:脱敏逻辑在日志输出层统一处理,不允许业务代码自行处理,避免遗漏。
追踪的特质是因果关联——还原请求的完整执行路径,定位延迟瓶颈和失败节点。
| 概念 | 说明 |
|---|---|
| Trace | 一个端到端请求的完整链路 |
| Span | 链路中的一个工作单元,包含起止时间、操作名、标签、日志 |
| Span Context | 跨服务传递的上下文(Trace ID、Span ID、采样标志) |
| Baggage | 随追踪上下文传递的键值对数据(慎用,有传播开销) |
| 策略 | 原理 | 适用场景 |
|---|---|---|
| 头部采样(Head-based) | 请求入口处决定是否采样 | 简单、低开销,适合均匀采样 |
| 尾部采样(Tail-based) | 收集完整链路后根据规则过滤 | 保留异常链路,丢弃正常链路 |
| 概率采样 | 按固定比例随机采样 | 通用场景,如 1% 采样率 |
| 速率限制采样 | 每秒最多采集 N 条 | 突发流量场景 |
Hugo 的项目经验:
必须追踪的关键路径:
Hugo 的踩坑记录:
trace_id 和 span_id 字段。 ┌─────────────┐
│ 告警触发 │
└──────┬──────┘
│
┌────────────┼────────────┐
↓ ↓ ↓
┌───────┐ ┌────────┐ ┌─────────┐
│ Metrics │ │ Logs │ │ Traces │
│ (什么) │ │ (哪里) │ │ (哪个) │
│ 发生了 │ │ 发生在 │ │ 具体是 │
│ 问题? │ │ 哪里? │ │ 哪个请求 │
└───┬───┘ └───┬────┘ └────┬────┘
│ │ │
└───────────┴────────────┘
↓
┌─────────────┐
│ 根因定位 │
└─────────────┘
典型排查流程(Hugo 实战案例):
payment-service 错误率从 0.1% 突增到 15%,触发 P1 告警PaymentGatewayTimeout 异常,集中在 alipay-gateway 实例alipay-gateway → alipay-api 的 HTTP 调用耗时从 200ms 增加到 35s| 方案 | 架构模式 | 查询语言 | 扩展性 | 适用场景 |
|---|---|---|---|---|
| Prometheus | 拉模式、单机/联邦 | PromQL | 中等(联邦集群) | Kubernetes 云原生标配 |
| InfluxDB | 推模式、TSDB | InfluxQL/Flux | 高(企业版集群) | 自定义指标、IoT 场景 |
| VictoriaMetrics | 兼容 Prometheus | PromQL | 很高(原生集群) | 大规模 Prometheus 替代 |
| M3DB | 分布式、分片 | M3QL | 超高(Uber 规模) | 超大规模指标存储 |
| Thanos | Prometheus + 对象存储 | PromQL | 高 | 长期存储 + 全局查询 |
Hugo 的选择:
| 方案 | 索引策略 | 存储成本 | 查询性能 | 适用场景 |
|---|---|---|---|---|
| ELK Stack | 全文索引 | 高 | 中等 | 通用日志分析、安全审计 |
| Loki | 标签索引(无全文) | 低 | 标签查询快 | Kubernetes 日志、成本敏感 |
| Splunk | 全文索引 | 很高 | 强 | 企业级、合规要求 |
| ClickHouse | 稀疏索引 | 很低 | 分析极强 | 结构化日志、OLAP 分析 |
| Grafana Tempo | 追踪ID索引 | 很低 | 追踪查询快 | 与追踪系统联动 |
Hugo 的选择:
| 方案 | 协议支持 | 存储后端 | 特色功能 | 适用场景 |
|---|---|---|---|---|
| Jaeger | OpenTelemetry、Zipkin | Cassandra/ES/Badger | CNCF 项目、生态完善 | 开源首选 |
| Zipkin | Zipkin 协议 | Cassandra/MySQL/ES | 轻量、简单 | 简单追踪需求 |
| SkyWalking | 自有协议 + OTel | ES/H2/MySQL/TiDB | APM 全功能、中文社区 | 国产 APM 替代 |
| Tempo | OpenTelemetry | S3/GCS/MinIO | Grafana 生态、对象存储 | 与 Grafana 集成 |
| Honeycomb | OpenTelemetry | SaaS | 高性能分析、事件驱动 | 商业方案 |
Hugo 的选择:
OpenTelemetry(OTel)正在成为可观测性领域的事实标准,提供统一的采集层:
┌─────────────────────────────────────────────────────────────┐
│ 应用层(自动/手动埋点) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Metrics │ │ Logs │ │ Traces │ │ Baggage │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ └─────────────┴─────────────┴─────────────┘ │
│ ↓ │
│ OpenTelemetry SDK │
└─────────────────────────┬───────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ OpenTelemetry Collector(采集代理) │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Receiver │ │ Processor │ │ Exporter │ │
│ │ (接收数据) │ │ (处理转换) │ │ (输出后端) │ │
│ └────────────┘ └────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────┘
OTel Collector 的核心能力:
Hugo 的部署模式:
Google SRE 提出的四个关键指标,适用于任何服务的监控:
| 信号 | 说明 | 典型指标 | 告警阈值建议 |
|---|---|---|---|
| 延迟(Latency) | 服务响应时间 | P50/P95/P99 响应时间 | P99 > 500ms 告警 |
| 流量(Traffic) | 服务负载量 | QPS、并发连接数、带宽 | 容量规划的 80% 预警 |
| 错误(Errors) | 失败请求比例 | 错误率、HTTP 5xx 比例 | 错误率 > 1% 告警 |
| 饱和度(Saturation) | 资源使用程度 | CPU、内存、磁盘、连接池 | CPU > 80%、连接池 > 90% |
Hugo 的实践:
| 指标 | 说明 | 计算公式 |
|---|---|---|
| Rate | 每秒请求数 | sum(rate(http_requests_total[1m])) |
| Errors | 每秒错误数 | sum(rate(http_requests_total{status=~"5.."}[1m])) |
| Duration | 请求处理时间分布 | histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[1m])) |
RED 方法 Dashboard 模板:
# Rate
sum(rate(http_requests_total{service="$service"}[1m]))
# Errors
sum(rate(http_requests_total{service="$service",status=~"5.."}[1m]))
/ sum(rate(http_requests_total{service="$service"}[1m]))
# Duration (P99)
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket{service="$service"}[1m])) by (le)
)
| 指标 | 说明 | 监控对象 |
|---|---|---|
| Utilization | 资源使用率 | CPU、内存、磁盘、网络带宽 |
| Saturation | 资源饱和/排队程度 | CPU 负载、IO 等待、连接队列长度 |
| Errors | 资源错误数 | 磁盘 IO 错误、网卡丢包、内存 OOM |
Hugo 的扩展:在 USE 基础上增加了 A(Availability,可用性),形成 USEA 方法:
HTTP Request Headers:
traceparent: 00-abc123def456789-xyz789abc123def-01
└─ version └─ trace-id └─ parent-id └─ sampled
tracestate: vendor1=abc,vendor2=def
→ 网关 → 服务A → 服务B → 数据库
↓ ↓ ↓
日志中 日志中 日志中
都包含 都包含 都包含
trace-id trace-id trace-id
W3C Trace Context 标准:使用 traceparent 和 tracestate Header,已被 OpenTelemetry 采纳。
{
"timestamp": "2026-04-20T10:15:30.123Z",
"level": "ERROR",
"trace_id": "abc123def456789",
"span_id": "span-789",
"trace_flags": "01",
"service": "payment-service",
"message": "订单处理失败",
"attributes": {
"order_id": "ORD-2026-001",
"error_type": "PaymentGatewayTimeout",
"duration_ms": 35000
}
}
查询联动:在 Grafana 中点击一个异常 Span,自动跳转到 Loki 查询该 trace_id 的所有日志。
| 级别 | 名称 | 响应要求 | 通知渠道 | 典型场景 |
|---|---|---|---|---|
| P0 | 紧急 | 立即响应,5分钟内 | 电话 + 短信 + IM | 核心服务不可用、资金损失、数据丢失 |
| P1 | 高 | 30分钟内响应 | 短信 + IM | 非核心服务不可用、性能严重下降 |
| P2 | 中 | 2小时内响应 | IM | 非关键功能异常、容量预警 |
| P3 | 低 | 工作日内处理 | 邮件/IM | 异常趋势、优化建议、容量规划 |
Hugo 的内部约定:
避免告警疲劳的 5 个关键原则:
| 原则 | 说明 | 实现方式 |
|---|---|---|
| 可行动(Actionable) | 每个告警都有明确的处置步骤 | 告警描述中嵌入 Runbook 链接 |
| 去重降噪 | 相同根因的告警聚合为一条 | 使用 Alertmanager 的 grouping |
| 分级路由 | 不同级别发送到不同渠道 | P0→电话,P1→短信,P2→IM |
| 静默期 | 维护窗口、已知问题自动静默 | 定时静默规则 + 手动静默 API |
| 自动恢复 | 问题恢复后自动发送恢复通知 | 使用 Prometheus 的 for 子句 |
Hugo 的踩坑记录:
# Prometheus Alerting Rule 示例
groups:
- name: payment_service
rules:
# P0: 服务不可用
- alert: PaymentServiceDown
expr: up{job="payment-service"} == 0
for: 1m
labels:
severity: p0
team: payment
annotations:
summary: "支付服务不可用"
description: "支付服务 {{ $labels.instance }} 已宕机超过 1 分钟"
runbook_url: "https://wiki.hugogu.cn/runbooks/payment-service-down"
# P1: 错误率突增
- alert: PaymentErrorRateHigh
expr:
(
sum(rate(http_requests_total{job="payment-service",status=~"5.."}[5m]))
/
sum(rate(http_requests_total{job="payment-service"}[5m]))
) > 0.05
for: 2m
labels:
severity: p1
team: payment
annotations:
summary: "支付服务错误率超过 5%"
description: "当前错误率: {{ $value | humanizePercentage }}"
# P2: 延迟升高
- alert: PaymentLatencyHigh
expr: |
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket{job="payment-service"}[5m])) by (le)
) > 0.5
for: 5m
labels:
severity: p2
team: payment
annotations:
summary: "支付服务 P99 延迟超过 500ms"
可观测性数据量巨大,成本控制是生产环境必须面对的挑战。
| 数据类型 | 热数据(实时查询) | 温数据(近线查询) | 冷数据(归档) |
|---|---|---|---|
| 指标 | 15 天(SSD) | 3 个月(HDD) | 1 年(对象存储) |
| 日志 | 7 天(SSD) | 30 天(HDD) | 90 天(对象存储) |
| 追踪 | 3 天(SSD) | 7 天(HDD) | 30 天(对象存储) |
| 策略 | 适用数据 | 效果 | 实现方式 |
|---|---|---|---|
| 采样 | Traces、Logs | 降低 90%+ 存储 | Traces 1% 采样,日志按级别分天数 |
| 压缩 | 全部 | 降低 70% 存储 | 列式压缩(Parquet)、字典编码 |
| 聚合 | Metrics | 降低 95% 长期存储 | 原始 15 天,1 分钟粒度 3 个月,5 分钟粒度 1 年 |
| 过滤 | Traces、Logs | 降低 50% 存储 | 健康检查不采集,静态资源不追踪 |
| 分层存储 | 全部 | 降低 80% 成本 | 热数据 SSD → 温数据 HDD → 冷数据 S3 |
Hugo 的实战数据:
| 方案 | 每日 1TB 日志成本 | 每日 10 亿指标点成本 | 备注 |
|---|---|---|---|
| 自建 ELK | ~$500 ~$200 | 需运维人力 | |
| 自建 Loki | ~$100 ~$200 | 轻量但功能有限 | |
| Datadog | ~$3,000 ~$1,500 | SaaS,功能全 | |
| Splunk | ~$5,000 ~$2,000 | 企业级,贵 | |
| AWS CloudWatch | ~$2,000 ~$500 | 与 AWS 集成好 | |
| Grafana Cloud | ~$1,000 ~$300 | 开源生态 |
┌─────────────────────────────────────────────────────────────────┐
│ 应用层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Service A │ │ Service B │ │ Service C │ │ Gateway │ │
│ │ (Java) │ │ (Go) │ │ (Python) │ │ (Nginx) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ └───────────────┴───────────────┴───────────────┘ │
│ ↓ │
│ OpenTelemetry SDK │
└──────────────────────────────┬───────────────────────────────────┘
↓ OTLP/gRPC
┌─────────────────────────────────────────────────────────────────┐
│ OpenTelemetry Collector │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Receiver │ │ Processor │ │ Exporter │ │
│ │ OTLP │→ │ Batch │→ │ Prometheus (Metrics) │ │
│ │ Prometheus │ │ Filter │ │ Kafka → ClickHouse │ │
│ │ Jaeger │ │ Sample │ │ (Logs) │ │
│ └─────────────┘ └─────────────┘ │ Jaeger (Traces) │ │
│ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 存储与可视化层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Prometheus │ │ ClickHouse │ │ Jaeger │ │
│ │ + Thanos │ │ (Logs) │ │ (Traces) │ │
│ │ (Metrics) │ │ │ │ │ │
│ └──────┬──────┘ └──────┬──────┘ └───────────┬─────────────┘ │
│ └────────────────┴─────────────────────┘ │
│ ↓ │
│ Grafana │
│ (统一 Dashboard:Metrics + Logs + Traces 联动) │
└─────────────────────────────────────────────────────────────────┘
Hugo 团队的可观测性开发规范:
/health、/metrics、/ready 端点| 踩坑场景 | 影响 | 解决方案 |
|---|---|---|
| DEBUG 日志未关闭 | 一天 2TB 日志,系统雪崩 | 配置中心统一管控日志级别 |
| SQL 参数未脱敏 | 敏感数据泄露 | 日志层统一脱敏,禁止业务代码处理 |
| 高基数标签 | Prometheus 内存 OOM | 基数上限监控,动态 ID 替换为占位符 |
| 消息队列无 Trace | 异步流程成追踪盲区 | 消息格式强制包含 trace_id |
| 告警过于敏感 | 200+ 条告警,值班麻木 | 告警评审制度,误报率 > 20% 下线 |
| Summary 类型聚合偏差 | P99 数据不一致 | 统一使用 Histogram + 服务端聚合 |
| 只追踪 HTTP 忽略 DB | N+1 问题定位 4 小时 | 数据库访问强制埋点,慢查询标记错误 |
| 告警无状态记录 | 无法统计 MTTR | 告警状态写入数据库,自动生成报表 |
OpenTelemetry 正在推动 Metrics、Logs、Traces 的统一采集和关联:
eBPF(Extended Berkeley Packet Filter)正在改变可观测性的采集方式:
代表项目:Pixie(New Relic)、Grafana Beyla、Cilium Hubble
Hugo 的观点:AI 辅助可观测性很有前景,但目前还不能替代人工判断。最佳实践是"AI 推荐 + 人工确认",而非完全自动化。
| 阶段 | 行动项 | 优先级 |
|---|---|---|
| 立即 | 所有服务暴露 /metrics 端点 |
P0 |
| 立即 | 结构化日志改造(JSON 格式) | P0 |
| 本周 | 接入 OpenTelemetry SDK | P1 |
| 本周 | 定义黄金信号 Dashboard | P1 |
| 本月 | 部署 Collector 和统一后端 | P1 |
| 本月 | 建立告警分级和 Runbook 制度 | P1 |
| 本季度 | 实现 Metrics/Logs/Traces 联动查询 | P2 |
| 本季度 | 成本优化(采样策略、分层存储) | P2 |
相关页面: