概述

你有没有遇到过这种情况:用户反馈"系统好卡",你打开 Grafana 看了一堆面板,CPU 正常、内存正常、网络也正常,但用户就是说慢。这时候你需要的不是更多的指标面板,而是一条完整的请求链路——从用户点下按钮到数据库返回结果,每一跳花了多少时间,卡在哪一步。

这就是 APM(Application Performance Monitoring,应用性能监控)干的事。

简单说:日志告诉你发生了什么,指标告诉你系统是否健康,APM 告诉你为什么慢、慢在哪里、影响了谁。三者各管一摊,缺一不可。

本文从实际选型角度出发,拆解主流开源和商业 APM 工具的架构差异、适用场景和坑点。不吹不黑,每家都有适合的场景,关键看你的团队规模、技术栈和预算。

APM 要解决什么问题

先说清楚为什么需要 APM,而不是直接跳到工具对比。

微服务架构下的"链路黑盒"

单体应用时代,一个请求从入口到数据库都在一个进程里完成,打个断点就能调试。微服务拆分后,一个用户请求可能经过 API 网关 → 认证服务 → 订单服务 → 支付服务 → 消息队列 → 库存服务 → 数据库,中间还穿插着 Redis 缓存和第三方 API 调用。

任何一个环节变慢,整体就慢。但你看日志只能看到单个服务的视角,没法把整条链路串起来。这就像你在医院看病,内科查完说没问题让你去外科,外科查完说没问题让你去骨科——每个科室都说自己没问题,但你就是难受。APM 就是那个能把你所有科室检查结果串起来看的"全科医生"。

APM 的三个核心能力

能力解决什么问题类比
分布式追踪(Distributed Tracing)一个请求经过哪些服务、每跳花了多久快递物流追踪,每个中转站都有时间戳
性能剖析(Profiling)某个函数执行慢,CPU 花在哪了体检报告,精确到每个器官的指标
错误追踪(Error Tracking)异常发生在哪个服务的哪行代码车辆故障码,直接定位到故障部件

分布式追踪是 APM 最核心的能力。它通过在请求入口生成一个唯一的 Trace ID,然后通过 HTTP Header 或 RPC 上下文传递到下游服务,每个服务在自己的处理过程中记录一个 Span(你可以理解为链路上的一个节点),最终拼出完整的调用树。

关键概念速览

术语含义说明
Trace一次完整的请求链路由多个 Span 组成的有向无环图(DAG)
Span链路上的一个操作节点包含操作名、起止时间、标签、日志
Context Propagation上下文传递Trace ID 通过 HTTP Header 在服务间传递
Sampling采样不可能记录所有请求,按策略采样部分
Instrumentation探针/埋点代码层面自动或手动注入追踪逻辑

采样策略很关键。线上流量大的时候,全量记录 Trace 会把存储和 CPU 吃干。常见做法是头部采样(Head-based Sampling)——在请求入口决定是否记录,要么整条链路全记,要么全不记。尾部采样(Tail-based Sampling)更精细——在链路结束时根据条件(比如耗时超过阈值、出现错误)决定是否保留,但实现复杂度高,需要中间层缓存完整链路。

开源 APM 工具全景对比

2026 年开源 APM 领域的格局已经比较清晰了。按照功能覆盖范围,可以分成三类:

  1. 一体化 APM:链路 + 指标 + 告警一站式,代表是 SkyWalking、Pinpoint
  2. 链路追踪专精:只管 Trace,需要配合 Prometheus + Grafana 做指标,代表是 Jaeger、Zipkin
  3. 可观测拼装栈:Prometheus + Grafana + Loki + Tempo(简称 LGTM 栈),灵活但集成成本高

五大开源方案速览

工具定位语言支持存储后端适合场景
Apache SkyWalking一体式 APMJava/Go/Python/Node.js/PHP 等ES/BanyanDB/H2Java 微服务为主、需要拓扑图
JaegerCNCF 链路追踪多语言(OTel SDK)ES/Cassandra/Badger只需 Trace、已有 Prometheus 栈
Zipkin轻量链路追踪多语言ES/MySQL/Cassandra小规模服务、快速上手
Grafana Tempo分布式追踪后端OTel/Jaeger/Zipkin 协议对象存储(S3/GCS)已用 Grafana、想要低成本长期存储
PinpointJava 字节码 APM仅 JavaHBase纯 Java 微服务、无侵入埋点

Apache SkyWalking

SkyWalking 是 Apache 基金会顶级项目,国内使用率很高。它的核心卖点是开箱即用——装上 OAP(Observability Analysis Platform)服务端 + Agent 探针,自动生成服务拓扑图、链路追踪、指标监控和告警,不需要额外配置 Prometheus。

技术架构上,SkyWalking 用 Agent 在应用侧做字节码增强(Java 用 JavaAgent,其他语言用 gRPC 手动埋点),数据通过 gRPC/HTTP 发送到 OAP 服务端,OAP 负责聚合分析和存储。存储支持 Elasticsearch、BanyanDB(SkyWalking 自研的时序数据库)和 H2(仅测试用)。

优势

  • 拓扑图自动发现,服务依赖关系一目了然
  • 一站式,不需要额外拼装组件
  • 国内社区活跃,文档中文友好
  • 支持 Service Mesh 场景(Istio/Envoy 数据面)

坑点

  • OAP 服务端对内存消耗不小,生产环境至少 8GB 起步
  • ES 存储在高基数场景下性能会掉,BanyanDB 还在成熟期
  • 非 Java 语言的 Agent 功能比 Java 弱不少
  • 规则配置方式偏重 XML/YAML,学习曲线有点陡

一个典型的 SkyWalking 部署架构:

# docker-compose-skywalking.yml
version: '3.8'

services:
  oap:
    image: apache/skywalking-oap-server:10.1.0
    ports:
      - "11800:11800"  # gRPC 接收 Agent 数据
      - "12800:12800"  # HTTP REST API
    environment:
      SW_STORAGE: elasticsearch
      SW_STORAGE_ES_CLUSTER_NODES: elasticsearch:9200
      SW_CORE_RECORD_DATA_TTL: 7        # 链路数据保留 7 天
      SW_CORE_METRICS_DATA_TTL: 30      # 指标数据保留 30 天
    depends_on:
      - elasticsearch
    restart: unless-stopped

  ui:
    image: apache/skywalking-ui:10.1.0
    ports:
      - "8080:8080"
    environment:
      SW_OAP_ADDRESS: http://oap:12800
    depends_on:
      - oap
    restart: unless-stopped

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.13.0
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - "ES_JAVA_OPTS=-Xms2g -Xmx2g"
    volumes:
      - es-data:/usr/share/elasticsearch/data
    restart: unless-stopped

volumes:
  es-data:

Java 应用接入只需要一行参数:

# 启动 Java 应用时挂载 SkyWalking Agent
java -javaagent:/path/to/skywalking-agent.jar \
     -Dskywalking.agent.service_name=order-service \
     -Dskywalking.collector.backend_service=oap:11800 \
     -jar order-service.jar

Go 应用需要手动埋点(SkyWalking Go Agent 还在发展中),使用 OTel SDK + SkyWalking exporter:

package main

import (
    "context"
    "log"
    
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/trace"
)

// 初始化 tracer,将数据发送到 SkyWalking OAP
func initTracer() func() {
    // 实际生产中配置 OTLP exporter 指向 SkyWalking OAP 的 OTLP 接收端口
    // SkyWalking 9.x+ 原生支持 OTLP 协议
    tp, err := initOTLPProvider("oap:11800", "order-service")
    if err != nil {
        log.Fatal(err)
    }
    otel.SetTracerProvider(tp)
    return func() { tp.Shutdown(context.Background()) }
}

// 在 HTTP handler 中创建 span
func handleOrder(ctx context.Context, orderID string) error {
    ctx, span := tracer.Start(ctx, "handleOrder")
    defer span.End()
    
    span.SetAttributes(attribute.String("order.id", orderID))
    
    // 调用下游支付服务,trace context 自动透传
    if err := callPaymentService(ctx, orderID); err != nil {
        span.RecordError(err)
        return err
    }
    return nil
}

Jaeger

Jaeger(德语"猎人")是 Uber 开源、后来捐给 CNCF 的分布式追踪项目。它只做一件事——Trace 存储、查询和可视化,不做指标和告警。

Jaeger 的定位很明确:如果你已经有 Prometheus + Grafana 做指标监控,只缺一个链路追踪后端,那 Jaeger 是最干净的选择。它不试图包揽一切,但 Trace 做得很扎实。

优势

  • CNCF 毕业项目,和 Kubernetes 生态融合好
  • 原生支持 OpenTelemetry 协议(OTLP)
  • UI 简洁,Trace 瀑布图可读性强
  • 支持自适应采样(Adaptive Sampling),根据流量自动调整采样率

坑点

  • 只管 Trace,指标和日志要另搭
  • 存储后端选型让人头疼——ES 太重,Cassandra 运维复杂,Badger 只适合单机
  • 社区活跃度不如 SkyWalking

Jaeger 支持 OTLP 直连,用 OpenTelemetry SDK 埋点后直接发给 Jaeger:

# Jaeger all-in-one 部署(仅测试用)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jaeger
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jaeger
  template:
    metadata:
      labels:
        app: jaeger
    spec:
      containers:
      - name: jaeger
        image: jaegertracing/all-in-one:1.60
        ports:
        - containerPort: 16686  # UI
        - containerPort: 4317    # OTLP gRPC
        - containerPort: 4318    # OTLP HTTP
        env:
        - name: COLLECTOR_OTLP_ENABLED
          value: "true"
        - name: SPAN_STORAGE_TYPE
          value: badger
        - name: BADGER_EPHEMERAL
          value: "false"
        - name: BADGER_DIRECTORY_VALUE
          value: /data/values
        - name: BADGER_DIRECTORY_KEY
          value: /data/keys
        volumeMounts:
        - name: data
          mountPath: /data
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: jaeger-pvc

Zipkin

Zipkin 是 Twitter 开源的链路追踪系统,比 Jaeger 更早。它的设计理念是"够用就好"——功能不多但稳定,部署简单。

老实说,新项目我不太推荐 Zipkin 了。功能上 Jaeger 完全覆盖了 Zipkin 的能力,而且 Jaeger 原生支持 OTLP,生态更活跃。Zipkin 的优势在于历史悠久、SDK 生态广,但新项目直接上 Jaeger 或 Tempo 更好。

Grafana Tempo

Tempo 是 Grafana Labs 出的高性能 Trace 后端,主打一个卖点:用对象存储替代数据库

传统 Trace 存储用 ES 或 Cassandra,成本高、运维重。Tempo 把 Trace 数据存在 S3/GCS/MinIO 这类对象存储上,成本降一个数量级,而且容量几乎无限。查询时通过 Trace ID 直接检索,不做全文检索(这是它和 Jaeger 的核心差异)。

优势

  • 存储成本极低,S3 一个月几美元就能存海量 Trace
  • 和 Grafana 深度集成,Trace 指标日志三联动
  • 架构简单,只有 ingester + querier + compactor 三个组件

坑点

  • 必须知道 Trace ID 才能查——不支持按服务名 + 时间范围搜索 Trace 列表(v2.0 后有 TraceQL 改善了不少)
  • 依赖对象存储,本地部署需要 MinIO

Tempo 适合已经用 Grafana 全家桶的团队。如果你的指标用 Prometheus、日志用 Loki,那 Trace 用 Tempo 是顺理成章的选择:

# Tempo + MinIO 部署
server:
  http_listen_port: 3200

distributor:
  receivers:
    otlp:
      protocols:
        grpc:
          endpoint: 0.0.0.0:4317
        http:
          endpoint: 0.0.0.0:4318

ingester:
  max_block_duration: 5m

compactor:
  compaction:
    block_retention: 48h

storage:
  trace:
    backend: s3
    s3:
      bucket: tempo-traces
      endpoint: minio:9000
      access_key: minioadmin
      secret_key: minioadmin
      insecure: true

Pinpoint

Pinpoint 是韩国 Naver 公司开源的 APM,特点是纯 Java、零侵入。它通过字节码增强自动埋点,Java 应用挂上 Agent 就能监控,不需要改一行代码。

如果你是纯 Java 技术栈,Pinpoint 的无侵入体验确实好。但一旦有 Go、Python、Node.js 服务,Pinpoint 就无能为力了。而且它的存储依赖 HBase,运维复杂度不低。现在新项目我会更推荐 SkyWalking——同样是字节码增强,但多语言支持和社区活跃度都更好。

商业 APM 工具对比

商业 APM 的核心优势是省心——不用自己运维存储后端,有专业团队做异常检测算法,集成度更高。但价格也不便宜。

维度DatadogDynatraceNew Relic
定位云原生监控平台AI 驱动全栈 APM开发者友好 APM
部署模式纯 SaaSSaaS 为主,私有化受限SaaS 轻量化
自动发现Agent + 850+ 集成OneAgent 自动插桩OTel-native
AI 引擎Watchdog 异常检测Davis 因果 AIApplied Intelligence
价格参考~$3000-5000/月(50 主机)~$69/主机/月~$49/用户/月
数据合规数据跨境数据默认跨境跨境存储
适合场景云原生、K8s 重度用户大型企业、需因果分析中小团队、快速上手

Datadog 2025 年 Q3 季度营收 8.857 亿美元,市值约 402 亿美元,服务覆盖全球 95% 的财富 500 强企业。连续五年被 Gartner 评为可观测性平台魔力象限领导者。参考来源:2026 年可观测厂商选型指南

Datadog

Datadog 是目前最火的商业可观测平台,没有之一。它的杀手锏是集成广度——850+ 个开箱即用的集成,覆盖 AWS/GCP/Azure 所有主流云服务、Kubernetes、数据库、消息队列、APM、日志、安全,几乎你能想到的都有。

Datadog 的 APM 通过 dd-trace 库自动埋点,支持 Java/Go/Python/Node.js/.NET/PHP/Ruby。数据发送到 Datadog SaaS 后端,UI 里可以直接从指标下钻到 Trace 再到日志,三联动体验流畅。

但 Datadog 的计费方式需要警惕:按主机 + 按模块收费,50 台主机的 APM + 日志 + 基础设施监控,月费很容易上到 3000-5000 美元。而且数据全部存在 Datadog 的 SaaS 上,国内企业需要考虑数据跨境合规问题。

Dynatrace

Dynatrace 的核心差异化是 Davis AI 引擎——它不是简单的阈值告警,而是基于因果分析自动定位根因。比如一个服务变慢,Davis 能告诉你"因为依赖的数据库查询变慢了,而查询变慢是因为某个索引被删除了"。

OneAgent 是 Dynatrace 的数据采集方式,一个 Agent 自动完成全栈监控——从基础设施到应用代码到用户体验。安装后零配置自动发现,这点对大型企业很有吸引力。

但 Dynatrace 的闭源程度很高,OneAgent 是专有的,数据格式不开放。一旦用上,迁移成本极高。价格方面约 $69/主机/月,对于大规模部署成本不低。

New Relic

New Relic 是 APM 领域的老牌玩家,开发者体验做得好。NRQL(New Relic Query Language)是一套类似 SQL 的查询语言,灵活度很高。而且 New Relic 是最早拥抱 OpenTelemetry 的商业厂商,OTel-native 的架构让数据可移植性更好。

但 New Relic 的基础设施监控和数据库深度监控相对薄弱,MySQL 慢查询分析、Redis 缓存命中率这些 DBA 关心的场景不如 Datadog 完善。大规模部署时按用户计费的模式容易失控。

OpenTelemetry:厂商中立的未来

聊 APM 选型绕不开 OpenTelemetry(简称 OTel)。这是 CNCF 的第二活跃项目(仅次于 Kubernetes),目标是统一可观测性三大支柱(Trace/Metrics/Logs)的数据采集标准。

为什么 OTel 重要

OTel 之前,每家 APM 厂商都有自己的 SDK:Jaeger 用 Jaeger client,Zipkin 用 Brave/Zipkin client,SkyWalking 用自己的 Agent。你选了一家就得用它的 SDK,换厂商就要改所有服务的埋点代码——这是典型的厂商锁定。

OTel 解决的是埋点标准化问题:应用侧只用 OTel SDK 埋点,数据通过 OTLP 协议发出去,后端可以是 Jaeger、SkyWalking、Tempo、Datadog 任何一个。换后端不用改应用代码。

OpenTelemetry 于 2019 年由 OpenTracing 和 OpenCensus 合并而来,继承了 OpenTracing 的厂商中立理念和 OpenCensus 的多信号能力。Traces Spec 2021 年达到 Stable,Metrics Spec 2021 年底 Stable,Logs Spec 2023 年中 Stable。参考来源:OpenTelemetry 实战:云原生可观测性三大支柱统一标准

OTel 架构三层分离

┌─────────────────────────────────────────────────────────┐
│                    应用层(Application)                   │
│   OTel SDK 自动/手动埋点 → 生成 Span/Metric/Log           │
└────────────────────────┬────────────────────────────────┘
                         │ OTLP 协议(gRPC :4317 / HTTP :4318)
┌─────────────────────────────────────────────────────────┐
│                  采集层(Collector)                       │
│   接收 → 处理(过滤/采样/批处理)→ 导出                    │
└─────────┬───────────────────────┬───────────────────────┘
          │                       │
          ▼                       ▼
┌──────────────────┐   ┌──────────────────────────────────┐
│  Jaeger / Tempo   │   │  Prometheus / SkyWalking / ES   │
│  (Trace 后端)      │   │  (Metrics / 日志后端)             │
└──────────────────┘   └──────────────────────────────────┘

OTel Collector 部署

OTel Collector 是 OTel 架构中的核心组件——它是一个数据中转站,负责接收、处理和导出遥测数据。生产环境强烈建议部署 Collector,而不是让应用直连后端:

# otel-collector-config.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  # 批处理,减少导出请求次数
  batch:
    timeout: 5s
    send_batch_size: 1024
  
  # 内存限制器,防止突发流量 OOM
  memory_limiter:
    check_interval: 1s
    limit_mib: 512
  
  # 尾部采样:只保留慢请求和错误请求
  tail_sampling:
    decision_wait: 10s
    policies:
      - name: errors
        type: status_code
        status_code:
          status_codes: [ERROR]
      - name: slow
        type: latency
        latency:
          threshold_ms: 500
      - name: random_keep
        type: probabilistic
        probabilistic:
          sampling_percentage: 10

exporters:
  # 发送到 Jaeger
  otlp/jaeger:
    endpoint: jaeger:4317
    tls:
      insecure: true
  
  # 发送到 Prometheus(指标)
  prometheus:
    endpoint: 0.0.0.0:8889
  
  # 发送到 Loki(日志)
  loki:
    endpoint: http://loki:3100/loki/api/v1/push

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, tail_sampling, batch]
      exporters: [otlp/jaeger]
    metrics:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [prometheus]
    logs:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [loki]

应用侧埋点示例(Go)

package main

import (
    "context"
    "log"
    "net/http"
    
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/propagation"
    "go.opentelemetry.io/otel/sdk/resource"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
    "go.opentelemetry.io/otel/trace"
    "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

func initTracer(ctx context.Context, serviceName string) func() {
    // 创建 OTLP gRPC exporter,指向 Collector
    exporter, err := otlptracegrpc.New(ctx,
        otlptracegrpc.WithEndpoint("otel-collector:4317"),
        otlptracegrpc.WithInsecure(),
    )
    if err != nil {
        log.Fatalf("failed to create exporter: %v", err)
    }
    
    // 配置资源信息(服务名、实例 ID 等)
    res, _ := resource.New(ctx,
        resource.WithAttributes(
            semconv.ServiceName(serviceName),
            semconv.ServiceVersion("v1.2.0"),
            semconv.DeploymentEnvironment("production"),
        ),
    )
    
    // 创建 TracerProvider
    tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exporter),
        sdktrace.WithResource(res),
        // 头部采样:10% 采样率
        sdktrace.WithSampler(sdktrace.TraceIDRatioBased(0.1)),
    )
    otel.SetTracerProvider(tp)
    otel.SetTextMapPropagator(propagation.TraceContext{})
    
    return func() {
        _ = tp.Shutdown(ctx)
    }
}

func main() {
    ctx := context.Background()
    shutdown := initTracer(ctx, "api-gateway")
    defer shutdown()
    
    tracer := otel.Tracer("api-gateway")
    
    // 使用 otelhttp 自动埋点 HTTP 服务
    handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ctx := r.Context()
        span := trace.SpanFromContext(ctx)
        span.SetAttributes(attribute.String("user.agent", r.UserAgent()))
        
        // 调用下游服务,TraceContext 自动透传
        callDownstream(ctx, "http://order-service:8080/api/orders")
        w.Write([]byte("OK"))
    })
    
    wrapped := otelhttp.NewHandler(handler, "api-gateway")
    http.ListenAndServe(":8080", wrapped)
}

func callDownstream(ctx context.Context, url string) {
    // otelhttp.NewClient 自动注入 Trace Header
    client := http.Client{
        Transport: otelhttp.NewTransport(http.DefaultTransport),
    }
    req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
    resp, err := client.Do(req)
    if err != nil {
        log.Printf("downstream call failed: %v", err)
        return
    }
    defer resp.Body.Close()
}

选型决策框架

说了这么多工具,到底怎么选?我梳理了一个六维选型框架,按照优先级排序:

维度一:技术栈匹配度

你的技术栈推荐方案原因
纯 Java 微服务SkyWalking / Pinpoint字节码增强,零侵入
多语言微服务(Go/Python/Java 混合)Jaeger + OTel SDKOTel 多语言支持好
已经用 Grafana 全家桶Tempo生态一致性,Trace 指标日志三联动
云原生 K8s 重度用户Datadog(有预算)/ SkyWalking(开源)K8s 集成深度
纯 Go 技术栈Jaeger + OTel SDKGo 原生支持 gRPC,和 Jaeger 天然契合

维度二:运维复杂度

开源 APM 最大的成本不是 License,是运维。你得自己管存储后端、做高可用、搞容量规划:

方案运维组件数存储运维难度日常维护工作量
SkyWalkingOAP + ES/BanyanDB中(ES 需调优)
Jaeger + ESCollector + ES
Tempo + S3Ingester + Querier + S3低(S3 免运维)
Datadog0(SaaS)0极低
自建 LGTM 栈Prometheus + Grafana + Loki + Tempo高(4 个组件)

维度三:存储成本

Trace 数据量很大。一个中等规模的微服务集群(50 个服务,日均 1 亿请求),10% 采样率,每天产生的 Trace 数据量在 50-200GB 之间。

存储方案月成本估算(50GB/天)数据保留查询性能
Elasticsearch$200-500(3 节点集群)7-14 天强(全文检索)
Cassandra$150-3007-30 天
S3/对象存储$15-3030-90 天中(按 Trace ID 查)
Datadog SaaS包含在 License 中15-30 天

维度四:合规与数据主权

国内企业的数据合规要求越来越严。如果你在金融、政务、医疗等行业,数据出境是红线:

方案合规风险解决方式
Datadog高(数据跨境)无国内节点,数据出境
Dynatrace高(数据跨境)私有化版本功能受限
New Relic高(数据跨境)无国内节点
SkyWalking自托管,数据在本地
Jaeger + ES自托管,数据在本地

维度五:团队规模与能力

团队规模推荐路径理由
5 人以下 SRE/运维Datadog 或 New Relic没人力运维开源方案
5-15 人 SRESkyWalking 或 Tempo有能力维护,成本可控
15 人以上自建 OTel + LGTM可定制性高,长期成本最低

维度六:TCO(总拥有成本)

别只看 License 费用。开源方案的 TCO 包括:

  • 存储成本(ES 集群 / S3 存储费)
  • 运维人力(至少 0.5 个 FTE 专职维护)
  • 硬件成本(OAP/Collector 节点)
  • 培训成本(团队学习曲线)

一个 50 台主机规模的集群,开源方案年 TCO 约 15-30 万人民币(含人力),商业方案约 30-60 万。规模再大,开源的成本优势才体现出来。

生产环境实战建议

建议1:采样策略别偷懒

全量采样听起来美好,实际线上跑两天存储就爆了。根据我的经验,合理的采样策略是:

# 生产环境推荐采样配置
sampling:
  # 正常请求:头部采样 5-10%
  head_based:
    ratio: 0.05
  
  # 慢请求(> 500ms):全量保留
  # 错误请求:全量保留
  # 通过 OTel Collector 尾部采样实现
  tail_based:
    policies:
      - type: status_code
        status_codes: [ERROR]
      - type: latency
        threshold_ms: 500
      - type: probabilistic
        sampling_percentage: 5

这样既控制了数据量,又不会漏掉关键问题。

建议2:Collector 必须高可用

OTel Collector 是数据链路的咽喉。它挂了,所有应用的 Trace 都发不出去。生产部署至少 3 个 Collector 实例 + 负载均衡:

# Kubernetes 部署 OTel Collector(Deployment 模式)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: otel-collector
spec:
  replicas: 3
  selector:
    matchLabels:
      app: otel-collector
  template:
    metadata:
      labels:
        app: otel-collector
    spec:
      containers:
      - name: collector
        image: otel/opentelemetry-collector-contrib:0.103.0
        ports:
        - containerPort: 4317  # OTLP gRPC
        - containerPort: 4318  # OTLP HTTP
        - containerPort: 8888  # Metrics
        resources:
          requests:
            cpu: 500m
            memory: 512Mi
          limits:
            cpu: 2000m
            memory: 2Gi
        livenessProbe:
          httpGet:
            path: /health
            port: 13133
        readinessProbe:
          httpGet:
            path: /health
            port: 13133
---
apiVersion: v1
kind: Service
metadata:
  name: otel-collector
spec:
  selector:
    app: otel-collector
  ports:
  - name: otlp-grpc
    port: 4317
    targetPort: 4317
  - name: otlp-http
    port: 4318
    targetPort: 4318

建议3:别一上来就追求完美

见过太多团队选型时纠结半个月,最后什么都没落地。务实做法:

  1. 第一周:用 OTel SDK 给最核心的 2-3 个服务埋点,后端先用 Jaeger all-in-one 单机跑起来
  2. 第二周:验证 Trace 数据质量,看链路是否完整、Span 是否有业务上下文
  3. 第一月:根据实际数据量决定后端选型——量小用 Jaeger + Badger,量大上 Tempo + S3 或 SkyWalking + ES
  4. 第三月:接入告警、配置 SLO,让 Trace 数据真正服务于故障排查

建议4:Span 标签要带业务上下文

纯技术 Trace(只有 HTTP 方法 + URL + 耗时)用处有限。真正有价值的是带业务上下文的 Trace:

// 好的做法:Span 里带上业务信息
func processOrder(ctx context.Context, order *Order) error {
    ctx, span := tracer.Start(ctx, "processOrder")
    defer span.End()
    
    // 关键业务属性
    span.SetAttributes(
        attribute.String("order.id", order.ID),
        attribute.String("order.user_id", order.UserID),
        attribute.Float64("order.amount", order.Amount),
        attribute.String("order.status", order.Status),
    )
    
    // 记录关键事件
    span.AddEvent("payment_initiated", trace.WithAttributes(
        attribute.String("payment.gateway", order.PaymentGateway),
    ))
    
    if err := validateOrder(ctx, order); err != nil {
        span.RecordError(err)
        span.SetStatus(codes.Error, err.Error())
        return err
    }
    
    return nil
}

这样在 Jaeger/SkyWalking UI 里搜索 Trace 时,可以直接用 order.id=xxx 过滤,快速定位到具体订单的完整链路。

建议5:监控你的监控系统

APM 系统本身也是服务,它也需要被监控。至少关注:

监控指标告警阈值含义
Collector 拒绝率> 1%应用发了数据但 Collector 处理不过来
Collector 导出延迟> 5s数据积压,可能后端存储出问题
存储磁盘使用率> 80%需要扩容或调整保留时间
Trace 完整率< 90%部分服务没正确传递 Trace Context
采样率偏差> 预期 ±20%采样策略可能配置错误
# PromQL:监控 OTel Collector 的数据丢弃率
rate(otelcol_processor_refused_spans_total[5m]) 
/ 
(rate(otelcol_receiver_accepted_spans_total[5m]) + rate(otelcol_processor_refused_spans_total[5m]))

不同规模团队的推荐方案

小团队(1-5 台服务器)

别折腾了。直接用 Datadog 或 New Relic 的免费额度,或者 Jaeger all-in-one 单机。你的时间应该花在业务上,不是运维监控系统。

中等规模(20-100 台服务器)

推荐方案:OTel SDK + Jaeger(或 Tempo)+ Prometheus + Grafana。

  • 埋点用 OTel SDK,保证厂商中立
  • Trace 后端用 Jaeger + ES 或 Tempo + S3
  • 指标继续用 Prometheus
  • 日志用 Loki 或 ELK
  • 统一在 Grafana 里展示

这个组合的 TCO 比商业方案低 60-70%,但需要 0.5-1 个 FTE 维护。

大规模(100+ 台服务器)

推荐方案:SkyWalking 或自建 OTel + LGTM 全栈。

  • SkyWalking 一站式,运维组件少
  • 或自建 OTel Collector + Tempo + Prometheus + Loki + Grafana,可定制性最高
  • 部署多区域联邦,解决跨机房查询问题
  • 考虑用 Tail Sampling 做精细化采样控制

金融/政务等合规要求高的场景

数据不能出境,商业 SaaS 基本排除了。推荐:

  • SkyWalking 私有化部署(国内社区活跃,中文文档全)
  • 或自建 OTel + Jaeger + ES,数据完全在本地
  • 按等保要求配置日志保留策略(通常 180 天以上)

总结

APM 选型没有银弹。我见过太多团队花了大价钱买了商业 APM,最后只用来看个拓扑图;也见过团队用开源方案搭了很完整的可观测平台,但维护到崩溃。

几个核心判断:

  1. 先上 OTel 再选后端。不管最终选什么工具,应用侧埋点统一用 OpenTelemetry SDK。这样换后端时不用改业务代码,这是最重要的架构决策。
  2. 小团队别自建。5 人以下运维团队,直接用商业 SaaS。自建开源 APM 的运维成本远超 License 费用。
  3. SkyWalking 是国内 Java 团队的默认选择。开箱即用,拓扑图好看,社区支持好。但注意 OAP 吃内存、ES 调优有坑。
  4. Tempo 适合存储成本敏感的场景。S3 存储 + Grafana 展示,成本只有 ES 方案的 1/10。但查询方式受限,适合"按 Trace ID 查"的场景。
  5. 商业方案看 Datadog。如果你不在意数据合规且预算充足,Datadog 的集成广度和产品成熟度确实领先。但做好成本管控,别被按量计费反噬。
  6. 采样策略决定了系统可用性。全量采样会拖垮存储,合理采样是 APM 系统能长期稳定运行的关键。

最后一点经验:APM 不是装上就完事的。它需要持续投入——调整采样率、优化 Span 标签、配合 SLO 做告警。一个用了一年的 APM 系统比刚上线的系统有价值得多,因为积累了足够的历史基线数据。别频繁换工具,选好了就深耕。

参考资料与致谢

本文在撰写过程中参考了以下资料,感谢原作者的贡献:

  1. 2026 开源 APM 选型指南: OpenTelemetry 原生方案怎么选 — DataBuff,开源 APM 工具分类与选型维度对比
  2. 2026 年 Top 5 开源 APM 工具对比指南 — 腾讯云开发者社区,五大开源 APM 工具特性与优劣势分析
  3. 2026 年可观测厂商选型指南 — CSDN,商业可观测厂商(Datadog/Dynatrace/New Relic)市场数据与能力对比
  4. APM 工具介绍:Agent 探针、Trace 追踪、Span 片段、Sampling 采样 — CSDN,APM 核心概念与工作原理详解
  5. OpenTelemetry 实战:云原生可观测性三大支柱统一标准 — CSDN,OpenTelemetry 历史演进与架构设计
  6. APM 工具选型终极对比: Applications Manager vs Datadog vs New Relic — ManageEngine,商业 APM 工具多维度对比分析