可观测性工程:结构化日志、Prometheus指标与OpenTelemetry链路追踪

系统介绍可观测性三大支柱(日志、指标、链路追踪)的工程实践,涵盖结构化日志、Prometheus/Grafana监控体系、OpenTelemetry统一采集、SLO/SLI定义及告警策略。

监控告诉你系统"坏了";可观测性让你理解系统"为什么坏了"。

现代分布式系统的复杂性,已经超出了传统"仪表盘 + 告警"能覆盖的范围。
可观测性(Observability)不是工具,而是一种能力——让你通过系统的外部输出(日志、指标、链路),理解其内部状态。


目录


1. 引言:可观测性 vs 监控

维度监控(Monitoring)可观测性(Observability)
目标系统"是否正常"系统"为何如此"
数据预定义指标高基数、可探索
工具仪表盘 + 阈值告警Logs + Metrics + Traces 联动
适用已知问题未知问题、复杂故障
思维被动主动、探索性

监控是可观测性的子集。真正的可观测性,让你能回答"我从没见过这种问题"。


2. 三大支柱:Logs、Metrics、Traces 及其关联

               ┌──────────────┐
               │   Traces     │  请求路径,跨服务因果关系
               └──────┬───────┘
                      │ span_id / trace_id
         ┌────────────┼────────────┐
         ▼            ▼            ▼
   ┌──────────┐ ┌──────────┐ ┌──────────┐
   │  Logs    │ │ Metrics  │ │  Logs    │
   │ Service A│ │ Service A│ │ Service B│
   └──────────┘ └──────────┘ └──────────┘
  • Logs:离散事件,详细描述"发生了什么"
  • Metrics:聚合数值,回答"整体情况如何"
  • Traces:跨服务请求路径,定位"哪里出了问题"

三者通过 trace_idspan_idservice_name 关联,形成完整的可观测性网络。


3. 结构化日志

3.1 JSON 格式标准

非结构化日志(如 printf)已不适合分布式系统。所有日志必须结构化:

{
  "timestamp": "2025-11-30T10:15:23.456Z",
  "level": "error",
  "service_name": "order-service",
  "trace_id": "abc123def456",
  "span_id": "span-789",
  "message": "failed to create order",
  "error": "database connection timeout",
  "user_id": "user-42",
  "duration_ms": 1200,
  "host": "order-service-7b8d9-xk2p"
}

3.2 字段规范

字段类型必填说明
timestampISO 8601统一使用 UTC
levelenumdebug / info / warn / error
messagestring简明描述
service_namestring服务标识
trace_idstring链路追踪 ID
span_idstring推荐当前 span ID
errorstring条件错误详情
duration_msnumber推荐操作耗时

禁止:日志中写入 PII(如密码、Token、完整邮箱)。


4. 日志采集栈

推荐栈:Loki + Promtail + Grafana(轻量,与 Prometheus 生态统一)。

Promtail 配置示例

server:
  http_listen_port: 9080

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: kubernetes-pods
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_label_app]
        target_label: app
      - source_labels: [__meta_kubernetes_namespace]
        target_label: namespace
      - source_labels: [__meta_kubernetes_pod_name]
        target_label: pod

对于大规模场景,可考虑 ELK(Elasticsearch + Logstash + Kibana)或 OpenSearch


5. Prometheus 指标体系

Prometheus 定义四种核心指标类型:

类型描述典型场景
Counter单调递增计数器请求总数、错误总数
Gauge可增可减的数值当前连接数、队列深度
Histogram观测值的分桶分布请求延迟分布
Summary客户端计算的分位数延迟 P50/P95/P99

命名规范

<namespace>_<subsystem>_<name>_<unit>

http_requests_total
http_request_duration_seconds
process_cpu_seconds_total
go_goroutines

6. Prometheus 配置示例

6.1 抓取配置

# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s
  external_labels:
    cluster: production
    env: prod

scrape_configs:
  - job_name: 'kubernetes-services'
    kubernetes_sd_configs:
      - role: service
    relabel_configs:
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_port]
        action: replace
        target_label: __address__
        regex: (.+)
        replacement: $1

  - job_name: 'node-exporter'
    static_configs:
      - targets: ['node-exporter:9100']

6.2 告警规则

# alerting_rules.yml
groups:
  - name: service_alerts
    rules:
      - alert: HighErrorRate
        expr: |
          sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
          /
          sum(rate(http_requests_total[5m])) by (service) > 0.05
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "{{ $labels.service }} error rate > 5%"
          description: "Current error rate: {{ $value | humanizePercentage }}"

      - alert: HighLatencyP99
        expr: |
          histogram_quantile(0.99,
            sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)
          ) > 2.0
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "{{ $labels.service }} P99 latency > 2s"

7. Grafana 仪表盘设计

推荐遵循 REDUSE 模型:

RED 模型(面向服务)

  • Rate:每秒请求数
  • Errors:每秒错误数
  • Duration:请求延迟分布

USE 模型(面向基础设施)

  • Utilization:资源使用率(CPU、内存、磁盘)
  • Saturation:资源饱和度(队列、等待)
  • Errors:错误事件数

Google 四大金指标(Golden Signals)

  • Latency(区分成功/失败)
  • Traffic(并发流量)
  • Errors(错误率)
  • Saturation(容量利用率)

仪表盘设计原则:先看 Golden Signals,再看细节;先看趋势,再看瞬时值。


8. OpenTelemetry 统一采集

OpenTelemetry(OTel)是 CNCF 项目,统一了日志、指标、链路追踪的采集标准。

8.1 SDK 集成与 Context Propagation

OTel 的核心价值:

  • 统一 API:一套 SDK 采集三种信号
  • Context Propagation:trace_id 在服务间自动传递(W3C Trace Context / B3)
  • 厂商无关:Exporter 可随时切换(Jaeger、Tempo、Datadog…)

8.2 Exporter 配置(Go 示例)

package main

import (
    "context"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/sdk/resource"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
)

func initTracer(ctx context.Context) (*sdktrace.TracerProvider, error) {
    exporter, err := otlptracegrpc.New(ctx,
        otlptracegrpc.WithEndpoint("otel-collector:4317"),
        otlptracegrpc.WithInsecure(),
    )
    if err != nil {
        return nil, err
    }

    res, err := resource.New(ctx,
        resource.WithAttributes(
            semconv.ServiceName("order-service"),
            semconv.ServiceVersion("1.2.0"),
            semconv.DeploymentEnvironment("production"),
        ),
    )

    tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exporter),
        sdktrace.WithResource(res),
        sdktrace.WithSampler(sdktrace.ParentBased(
            sdktrace.TraceIDRatioBased(0.1), // 10% 采样
        )),
    )

    otel.SetTracerProvider(tp)
    return tp, nil
}

最佳实践:生产环境用 OTel Collector 作为中继,统一采集、处理、分发。


9. SLO/SLI/SLA 定义

概念全称含义
SLIService Level Indicator可量化的服务指标(如可用性 99.9%)
SLOService Level Objective内部目标(如"30 天内 SLI ≥ 99.9%")
SLAService Level Agreement对外承诺(违约有惩罚)

Error Budget 计算

Error Budget = 1 - SLO

SLO = 99.9%
→ 30 天允许的不可用时间 = 30 × 24 × 60 × (1 - 0.999) = 43.2 分钟

典型 SLI 定义

  • 可用性:成功的请求数 / 总请求数
  • 延迟:P99 请求延迟 < 500ms 的比例
  • 吞吐量:每秒处理的请求数 > 阈值

Error Budget 的意义:不是追求零故障,而是在可靠性与发布速度之间取得平衡。


10. 告警策略

告警疲劳是可观测性的最大敌人。

告警分类

类型描述示例
基于症状(推荐)用户/业务受影响“错误率 > 5%”
基于原因(少用)底层资源异常“CPU > 90%”
信息性无需立即处理“新版本部署完成”

减少告警疲劳的原则

  1. 只告警需要人介入的事——否则就是噪音
  2. 基于 SLO 告警,而不是基于瞬时阈值
  3. 告警必须附带 Runbook 链接
  4. 定期清理无效告警,每季度 Review 一次
  5. 分级告警:Critical(立即)/ Warning(工作时间)/ Info(仅记录)

11. 可观测性成本优化

可观测性数据的成本往往被低估。三大成本优化杠杆:

采样策略

  • 链路追踪:生产环境 1%–10% 采样
  • 错误请求:100% 保留(Tail-based Sampling)
  • 健康检查:0% 采样

日志级别管理

  • 生产环境默认 INFO 级别
  • DEBUG 仅在问题排查时动态开启(通过 /debug 端点或配置中心)
  • 使用采样日志减少高频事件输出

指标基数控制

  • 避免高基数标签(如 user_idrequest_id
  • 指标基数 > 10,000 时,必须评审
  • 使用 Recording Rules 预聚合

成本公式

可观测性成本 ≈ 数据量 × 保留天数 × 单位存储成本

保留策略:Metrics 15 个月、Logs 30 天(合规需求除外)、Traces 7 天。


12. 总结与可观测性成熟度模型

阶段特征能力
L1 · 基础监控CPU/内存告警、简单日志知道"机器活着"
L2 · 结构化可观测JSON 日志、Prometheus 指标、基础 Trace知道"服务是否正常"
L3 · 关联性分析trace_id 关联 Logs/Metrics/Traces能快速定位故障
L4 · SLO 驱动定义 SLI/SLO,Error Budget 告警用业务视角衡量可靠性
L5 · 自动化响应基于可观测数据自动扩缩容/回滚系统自我修复

可观测性 Checklist

  • 所有日志结构化(JSON),包含 trace_id
  • 所有服务暴露 Prometheus 指标(/metrics
  • 链路追踪覆盖核心路径,采样率合理
  • Grafana 仪表盘遵循 RED / USE / Golden Signals
  • SLO 已定义,Error Budget 告警已配置
  • 告警分级,附带 Runbook
  • 日志脱敏,PII 不入日志
  • 数据保留策略明确,成本可控

一句话总结:可观测性的价值不在于"看仪表盘",而在于让你能理解系统的行为、预测系统的风险、定位系统的问题


13. 延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页