🔍 可观测性

Metrics / Logs / Traces 三支柱——理解系统在发生什么的唯一途径

3
可观测性支柱:Metrics / Logs / Traces
CNCF
OpenTelemetry 已成为第 2 活跃项目
4
Google SRE 黄金信号
99.9%
典型 SLO 目标 (每月 ≤ 43 分钟不可用)
📋 目录 1. 什么是可观测性——监控 ≠ 可观测性 2. 三支柱:Metrics / Logs / Traces 3. OpenTelemetry:统一标准 4. 分布式追踪:一个请求经过 5 个服务 5. SLO / SLI / SLA 定义与设定 6. Google 四大黄金信号 7. Grafana + Prometheus + Jaeger 实操 8. AI Agent 系统的可观测性 9. 可观测性 Checklist

1. 什么是可观测性——监控 ≠ 可观测性

📖 核心区别

监控 (Monitoring):你知道系统可能出什么问题,提前埋点观测。回答 "我知道会出什么问题"

可观测性 (Observability):你不知道系统会出什么问题,但通过系统的外部输出可以推断内部状态。回答 "出了什么问题?为什么?"

类比:监控是体检时查已知指标(血压、血糖),可观测性是当你感觉不适时能从症状反推病因。

⚠️ 常见误区:装了 Prometheus + Grafana ≠ 有可观测性。如果你只能看 Dashboard 上的预设指标,那还是监控。真正的可观测性是:出了未知问题,你能通过已有数据快速定位根因,而不需要"加个日志重新部署"。

Charity Majors(Honeycomb CEO)的定义最为精辟:

"Observability is a measure of how well you can understand the internal state of a system based on its external outputs. Monitoring is what you do when you already know what questions to ask."

这个概念源自控制论。Rudolf E. Kálmán 在 1960 年定义了"可观测性":一个系统是可观测的,当且仅当从其外部输出可以唯一确定其内部状态。在软件领域,这意味着:

🔓 Known Knowns

你知道会出什么问题,也知道怎么查。→ 监控就够了。例:API 响应时间 > 500ms 告警

🤔 Known Unknowns

你知道可能出问题,但不知道具体情况。→ 需要指标+告警。例:不知道哪天数据库会慢

😱 Unknown Unknowns

你完全没想到会出问题。→ 这是可观测性的核心战场。例:某个新部署导致 1% 的请求静默失败

2. 三支柱:Metrics / Logs / Traces

📊
Metrics
聚合数值数据,回答"多少""多快"。
CounterGaugeHistogram
📝
Logs
离散事件记录,回答"发生了什么"。
结构化非结构化
🔗
Traces
请求链路追踪,回答"请求去了哪"。
SpanContext Propagation

2.1 Metrics 指标

指标是运行时捕获的数值度量,按时间聚合。OpenTelemetry 定义了以下指标仪器类型:

类型语义只增?典型场景
Counter单调递增的累计值✅ 只增请求总数、错误总数、已发送字节数
UpDownCounter可增可减的累计值❌ 可减当前活跃连接数、队列长度
Gauge当前瞬时值CPU 使用率、内存用量、温度
Histogram值分布统计请求延迟分布、响应体大小分布
Async Counter每次导出时采集的累计值进程启动以来的 CPU 秒数
Async Gauge每次导出时采集的瞬时值风扇转速(仅能读取不能递增)

关于 Metrics 稳定性:截至 OpenTelemetry 最新规范,Go/Java/Python/C++/.NET/PHP 的 Metrics API 和 SDK 均已标记为 Stable(稳定),可以放心用于生产环境。

📐 Prometheus 指标四黄金类型

Prometheus 定义了四种核心指标类型,与 OTel 大体对应:

# Counter — 只增计数器
http_requests_total{method="GET", path="/api/users", status="200"} 15423

# Gauge — 可增减的当前值
process_open_fds 127
node_memory_available_bytes 4.2e+09

# Histogram — 直方图(延迟分布)
http_request_duration_seconds_bucket{le="0.1"} 8023
http_request_duration_seconds_bucket{le="0.5"} 14201
http_request_duration_seconds_bucket{le="1.0"} 15890
http_request_duration_seconds_sum 5432.1
http_request_duration_seconds_count 16000

# Summary — 分位数摘要
http_request_duration_seconds{quantile="0.5"} 0.12
http_request_duration_seconds{quantile="0.9"} 0.34
http_request_duration_seconds{quantile="0.99"} 1.02

2.2 Logs 日志

日志是带时间戳的文本记录,分为结构化半结构化非结构化三种:

{
  "timestamp": "2026-05-18T08:04:12.345Z",
  "level": "ERROR",
  "service": "payment-gateway",
  "trace_id": "5b8aa5a2d2c872e8321cf37308d69df2",
  "span_id": "051581bf3cb55c13",
  "message": "Payment processing failed",
  "context": {
    "userId": "u_7x8k9m",
    "orderId": "ord_abc123",
    "errorCode": "CARD_DECLINED",
    "amount": 99.00
  }
}

✅ 可直接查询、过滤、聚合。OpenTelemetry 推荐方式。注意 trace_idspan_id 实现了日志与追踪的关联。

192.168.1.1 - johndoe [18/May/2026:08:04:12 +0800] "POST /api/v1/pay HTTP/1.1" 500 1234 "https://app.example.com" "Mozilla/5.0" {"traceId":"5b8aa5a2","duration":2340,"error":"CARD_DECLINED"}

⚡ 常见于 Nginx/Apache 访问日志。需正则解析。OTel Collector 的 filelogreceiver 可以处理。

[ERROR] 2026-05-18 08:04:12 - Payment processing failed for user johndoe. Exception: PaymentGatewayException: Card declined. Order: ord_abc123, Amount: $99.00

❌ 开发时方便阅读,但生产环境难以解析。需要自定义正则才能提取结构化信息。

💡 最佳实践:生产环境必须使用结构化日志。OpenTelemetry SDK 会自动将日志与当前 trace/span 关联——这是三支柱打通的关键。

2.3 Traces 分布式追踪

追踪记录一个请求从入口到完成的完整路径。核心概念:

概念说明
Trace一个请求的完整旅程,由多个 Span 组成
Span一个工作单元(一次 HTTP 调用、一次 DB 查询、一次 LLM 推理)
SpanContext包含 Trace ID、Span ID、Trace Flags——在服务间传播的上下文
Context Propagation通过 HTTP Headers(W3C Trace Context)或消息元数据在服务间传递 SpanContext
Baggage在整个追踪链路中传播的键值对(如 userId=xxx),用于跨服务传递业务上下文

W3C Trace Context 标准(traceparent header)是分布式追踪的基础:

# HTTP 请求中传播追踪上下文
traceparent: 00-5b8aa5a2d2c872e8321cf37308d69df2-051581bf3cb55c13-01
# 格式: version-trace_id-span_id-trace_flags
# 01 = sampled(采样)

tracestate: vendor1=value1,vendor2=value2
# 可选:供应商特定信息

3. OpenTelemetry:统一标准

在 OpenTelemetry 出现之前,可观测性生态碎片化严重:Jaeger 用自己的 SDK、Prometheus 用自己的格式、各云厂商有自己的 agent。OpenTelemetry 的使命是统一遥测数据的采集和传输标准

🏛️ OpenTelemetry 架构
┌──────────────────────────────────────────────────────────┐
│                    你的应用程序                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐                │
│  │ OTel SDK │  │ OTel SDK │  │ OTel SDK │                │
│  │ (Traces) │  │ (Metrics)│  │ (Logs)   │                │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘                │
│       └──────────────┼─────────────┘                       │
│                      ▼                                     │
│           ┌──────────────────────┐                         │
│           │  OTel Collector      │                         │
│           │  ┌──────┐ ┌──────┐  │                         │
│           │  │Recv'r│ │Proc'r│  │                         │
│           │  └──┬───┘ └──┬───┘  │                         │
│           │     └───┬────┘       │                         │
│           │         ▼            │                         │
│           │  ┌──────────────┐   │                         │
│           │  │   Exporter   │   │                         │
│           │  └──────┬───────┘   │                         │
│           └─────────┼───────────┘                         │
└─────────────────────┼────────────────────────────────────┘
                      ▼
       ┌──────────────┼──────────────┐
       ▼              ▼              ▼
  ┌─────────┐  ┌───────────┐  ┌──────────┐
  │ Jaeger  │  │ Prometheus │  │ Loki/    │
  │ (Traces)│  │ (Metrics)  │  │ ELK      │
  └─────────┘  └───────────┘  │ (Logs)   │
                              └──────────┘

3.1 OTel Collector 核心组件

📥 Receiver

接收数据。支持 OTLP(原生)、Prometheus remote_write、Jaeger、Zipkin、Fluentd 等多种格式。推荐使用 OTLP gRPC(高性能)。

⚙️ Processor

处理数据。常用:batch(批处理降低网络开销)、memory_limiter(防止 OOM)、attributes(添加/修改属性)、filter(过滤不需要的数据)、tail_sampling(尾部采样)。

📤 Exporter

发送数据。支持 Jaeger、Prometheus、Loki、Elasticsearch、各云厂商(Datadog/New Relic/Dynatrace)等。

3.2 采样策略

在生产环境中,不可能采集每一个 Trace。采样是必要的权衡:

策略原理优点缺点
Head Sampling在 Trace 开始时决定是否采样(如 10%)实现简单,Trace 完整可能漏掉罕见错误
Tail Sampling在 Trace 结束后根据内容决定是否保留保留所有错误和慢请求需要缓存完整 Trace,内存开销大
Priority Sampling标记重要请求强制采样关键路径不遗漏需要业务层配合
📌 采样最佳实践:正常流量 Head Sampling 1-5%,所有错误和 p99 慢请求强制采样(Tail Sampling)。这样既控制成本,又不丢失关键信息。

3.3 语义约定 (Semantic Conventions)

OpenTelemetry 定义了标准属性名,确保跨服务、跨语言的一致性:

# 推荐的标准属性名
http.request.method          # "GET", "POST"
http.response.status_code    # 200, 404, 500
url.path                     # "/api/v1/users"
url.query                    # "?page=1&limit=10"
server.address               # "api.example.com"
server.port                  # 443
network.protocol.version     # "1.1"
user_agent.original          # "Mozilla/5.0 ..."

# AI/LLM 语义约定(OpenTelemetry 正在制定中)
gen_ai.system                # "openai", "anthropic"
gen_ai.request.model         # "gpt-4o", "claude-3.5-sonnet"
gen_ai.request.max_tokens    # 4096
gen_ai.response.finish_reason # "stop", "length"
gen_ai.usage.input_tokens    # 1500
gen_ai.usage.output_tokens   # 823

4. 分布式追踪:一个请求经过 5 个服务

假设一个电商下单请求经过 5 个微服务,我们看看追踪如何工作:

🔗 分布式追踪可视化(交互式)

0ms200ms400ms600ms800ms

4.1 Context Propagation 详解

分布式追踪的核心难题是上下文传播。每个服务需要从入站请求中提取 Trace ID,并在出站请求中注入:

# Node.js 示例:Express 中间件自动传播上下文
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const { Resource } = require('@opentelemetry/resources');
const { ATTR_SERVICE_NAME } = require('@opentelemetry/semantic-conventions');

const sdk = new NodeSDK({
  resource: new Resource({
    [ATTR_SERVICE_NAME]: 'payment-gateway',
  }),
  traceExporter: new OTLPTraceExporter({
    url: 'http://otel-collector:4317', // OTLP gRPC
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();

// 自动注入/提取 W3C traceparent header
// 无需手动修改业务代码!
# Python 示例:手动 Span 创建 + HTTP 传播
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
import requests

# 初始化
provider = TracerProvider()
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://otel-collector:4317"))
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

tracer = trace.get_tracer("payment-service")
propagator = TraceContextTextMapPropagator()

# 创建 Span 并在 HTTP 调用中传播上下文
with tracer.start_as_current_span("process_payment") as span:
    span.set_attribute("payment.order_id", "ord_abc123")
    span.set_attribute("payment.amount", 99.00)

    # 调用下游服务时注入 trace context
    headers = {}
    propagator.inject(headers)  # 自动添加 traceparent header

    response = requests.post(
        "http://risk-service/api/v1/evaluate",
        headers=headers,
        json={"orderId": "ord_abc123", "amount": 99.00}
    )

5. SLO / SLI / SLA 定义与设定

Google SRE 书籍提出的三个核心概念:

📊 SLI — 服务水平指标

衡量服务行为的量化指标。好的 SLI 从用户视角度量。

例:请求延迟 p99 < 500ms 的比例;成功请求占比;搜索结果相关性评分

🎯 SLO — 服务水平目标

SLI 的目标值,把可靠性与业务价值挂钩。

例:99.9% 的请求在 500ms 内完成;99.5% 的支付请求成功

📜 SLA — 服务水平协议

未达 SLO 时的商业后果(赔偿条款)。

例:可用性低于 99.9% 时赔偿月费的 10%

🎯 SLO Error Budget 计算器

5.1 常见 SLI 选择

类别SLI典型 SLO度量方式
可用性成功请求比例≥ 99.9%sum(rate(http_requests_total{status!~"5.."})[5m]) / sum(rate(http_requests_total)[5m])
延迟p99 请求延迟≤ 500mshistogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
正确性数据一致性校验通过率≥ 99.99%定期校验任务
吞吐请求处理速率≥ 1000 req/ssum(rate(http_requests_total[5m]))
覆盖功能可用率≥ 99.5%Synthetic monitoring / Canaries
⚠️ SLO 反模式

6. Google 四大黄金信号

Google SRE 书提出的四个关键监控维度,是最小化但有效的监控集合:

⏱️
Latency
请求完成时间。区分成功和失败的延迟——一个返回 500 的请求可能 10ms 就返回了,但那不是"快"
🚦
Traffic
系统承受的请求量。HTTP QPS、数据库 TPS、消息队列消费速率
Errors
请求失败率。显式错误(5xx)+ 隐式错误(返回 200 但内容错误)
💾
Saturation
资源满载程度。CPU、内存、磁盘、连接池、队列深度。"满"之前就该告警
📐 RED vs USE 方法论

两种互补的信号选择方法论:

方法论适用对象三个维度
RED (Request-Error-Duration)请求驱动型服务(API、Web)Rate(请求速率)、Errors(错误率)、Duration(延迟分布)
USE (Utilization-Saturation-Errors)基础设施(CPU、磁盘、网络)Utilization(使用率)、Saturation(饱和度/排队量)、Errors(错误数)

经验法则:服务用 RED,基础设施用 USE,两者结合就是四黄金信号的超集。

7. Grafana + Prometheus + Jaeger 实操

这是目前最主流的开源可观测性栈。以下是一个完整的 Docker Compose 部署:

# docker-compose.observability.yml
version: "3.8"

services:
  # ===== 数据采集 =====
  otel-collector:
    image: otel/opentelemetry-collector-contrib:0.96.0
    command: ["--config=/etc/otelcol/config.yaml"]
    volumes:
      - ./otel-collector-config.yaml:/etc/otelcol/config.yaml:ro
    ports:
      - "4317:4317"   # OTLP gRPC
      - "4318:4318"   # OTLP HTTP
      - "8889:8889"   # Prometheus exporter
    depends_on:
      - jaeger
      - loki

  # ===== Metrics =====
  prometheus:
    image: prom/prometheus:v2.50.0
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus-data:/prometheus
    ports:
      - "9090:9090"

  # ===== Traces =====
  jaeger:
    image: jaegertracing/all-in-one:1.54
    environment:
      - COLLECTOR_OTLP_ENABLED=true
    ports:
      - "16686:16686"  # Jaeger UI
      - "14268:14268"  # Jaeger HTTP collector
      - "4317"         # OTLP gRPC (via otel-collector)

  # ===== Logs =====
  loki:
    image: grafana/loki:2.9.4
    ports:
      - "3100:3100"
    command: -config.file=/etc/loki/local-config.yaml

  # ===== 可视化 =====
  grafana:
    image: grafana/grafana:10.3.3
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_AUTH_ANONYMOUS_ENABLED=true
    volumes:
      - grafana-data:/var/lib/grafana
      - ./grafana-datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml:ro
    ports:
      - "3000:3000"
    depends_on:
      - prometheus
      - jaeger
      - loki

volumes:
  prometheus-data:
  grafana-data:

7.1 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:
    send_batch_size: 1024
    timeout: 5s
  memory_limiter:
    check_interval: 1s
    limit_percentage: 80
    spike_limit_percentage: 25
  # 添加服务名属性
  resource:
    attributes:
      - key: deployment.environment
        value: production
        action: upsert

exporters:
  # 发送 Traces 到 Jaeger
  otlp/jaeger:
    endpoint: jaeger:4317
    tls:
      insecure: true
  # 发送 Metrics 到 Prometheus (供 Prometheus 拉取)
  prometheus:
    endpoint: "0.0.0.0:8889"
  # 发送 Logs 到 Loki
  loki:
    endpoint: "http://loki:3100/loki/api/v1/push"

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

7.2 Prometheus 配置

# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  # 从 OTel Collector 拉取指标
  - job_name: 'otel-collector'
    static_configs:
      - targets: ['otel-collector:8889']

  # 你的应用指标(如果直出 Prometheus 格式)
  - job_name: 'my-app'
    static_configs:
      - targets: ['host.docker.internal:8080']
    metrics_path: '/metrics'

7.3 Grafana 数据源配置

# grafana-datasources.yaml
apiVersion: 1
datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true

  - name: Jaeger
    type: jaeger
    access: proxy
    url: http://jaeger:16686
    # 关键:在 Grafana 中从 Metrics 跳转到 Traces
    jsonData:
      tracesToMetrics:
        - name: 'Search traces for latency'
          tags: ['service']
          datasourceUid: 'prometheus'

  - name: Loki
    type: loki
    access: proxy
    url: http://loki:3100
    # 关键:从 Logs 跳转到 Traces
    jsonData:
      derivedFields:
        - datasourceUid: 'jaeger'
          matcherRegex: '"trace_id":"(\w+)"'
          name: TraceID
          url: '$${__value.raw}'

7.4 应用端接入(Node.js 完整示例)

// tracing.js — 在应用入口最先引入
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-grpc');
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');
const { Resource } = require('@opentelemetry/resources');
const { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } = require('@opentelemetry/semantic-conventions');
const { logs } = require('@opentelemetry/api-logs');
const { OTLPLogExporter } = require('@opentelemetry/exporter-logs-otlp-grpc');
const { BatchLogRecordProcessor, LoggerProvider } = require('@opentelemetry/sdk-logs');

// 统一 OTLP endpoint
const OTEL_ENDPOINT = process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4317';

const sdk = new NodeSDK({
  resource: new Resource({
    [ATTR_SERVICE_NAME]: process.env.SERVICE_NAME || 'my-service',
    [ATTR_SERVICE_VERSION]: process.env.SERVICE_VERSION || '1.0.0',
  }),
  traceExporter: new OTLPTraceExporter({ url: OTEL_ENDPOINT }),
  metricReader: new PeriodicExportingMetricReader({
    exporter: new OTLPMetricExporter({ url: OTEL_ENDPOINT }),
    exportIntervalMillis: 10000, // 每 10 秒导出一次指标
  }),
  instrumentations: [
    getNodeAutoInstrumentations({
      // 关闭不需要的自动埋点以减少噪音
      '@opentelemetry/instrumentation-fs': { enabled: false },
    }),
  ],
});

sdk.start();

// 设置日志导出
const loggerProvider = new LoggerProvider();
loggerProvider.addLogRecordProcessor(
  new BatchLogRecordProcessor(
    new OTLPLogExporter({ url: OTEL_ENDPOINT })
  )
);
logs.setGlobalLoggerProvider(loggerProvider);

// 优雅关闭
process.on('SIGTERM', async () => {
  await sdk.shutdown();
  await loggerProvider.shutdown();
});

// ===== 业务代码中使用 =====
const { trace } = require('@opentelemetry/api');
const tracer = trace.getTracer('my-service');

async function handlePayment(orderId, amount) {
  return tracer.startActiveSpan('handle_payment', {
    attributes: {
      'payment.order_id': orderId,
      'payment.amount': amount,
    },
  }, async (span) => {
    try {
      // 调用风控服务
      const riskResult = await tracer.startActiveSpan('check_risk', async (childSpan) => {
        const result = await fetch('http://risk-service/api/v1/evaluate', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ orderId, amount }),
        });
        childSpan.setAttribute('risk.score', (await result.json()).score);
        return result.json();
      });

      // 调用支付网关
      const paymentResult = await tracer.startActiveSpan('charge_card', async (childSpan) => {
        const result = await fetch('http://payment-gateway/api/v1/charge', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ orderId, amount, riskScore: riskResult.score }),
        });
        childSpan.setAttribute('payment.gateway_response', result.status);
        return result.json();
      });

      span.setAttribute('payment.status', 'success');
      return paymentResult;
    } catch (err) {
      span.setAttribute('payment.status', 'failed');
      span.setAttribute('error.type', err.name);
      span.recordException(err);
      throw err;
    } finally {
      span.end();
    }
  });
}

8. AI Agent 系统的可观测性

AI Agent 系统本质上就是分布式系统——多个 Agent 协调 = 共识问题,记忆同步 = 一致性问题,工具调用 = 分布式事务。可观测性对 Agent 系统尤为重要,因为 LLM 的非确定性让调试极其困难。

🚨 Agent 系统的可观测性痛点

8.1 Agent Trace 设计

一个 Agent 执行流程的追踪应该长这样:

🤖 Agent 执行 Trace 示例

0s5s10s15s20s

8.2 Agent 可观测性指标

指标类别具体指标类型用途
性能agent.task.duration_secondsHistogram任务端到端耗时
agent.llm.latency_secondsHistogram每次 LLM 调用延迟
agent.tool.call.duration_secondsHistogram工具调用耗时
成本agent.llm.tokens.inputCounter输入 token 总量
agent.llm.tokens.outputCounter输出 token 总量
agent.llm.cost_usdCounter累计费用(美元)
质量agent.task.success_totalCounter成功任务数
agent.task.failure_totalCounter失败任务数
agent.hallucination.detected_totalCounter检测到的幻觉次数
工具agent.tool.calls_totalCounter工具调用次数
agent.tool.errors_totalCounter工具调用失败次数

8.3 LangChain / LangSmith 追踪

LangSmith 是 LangChain 的可观测性平台,提供 Agent 执行的完整追踪:

# Python:接入 LangSmith
import os

# 设置环境变量即可自动追踪
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "ls-xxxxx"
os.environ["LANGCHAIN_PROJECT"] = "my-agent"

# 所有 LangChain 调用自动被追踪
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent

llm = ChatOpenAI(model="gpt-4o")
# 每次 LLM 调用、每次工具执行都会记录到 LangSmith
agent = create_tool_calling_agent(llm, tools, prompt)

8.4 自建 Agent 追踪(OTel 原生)

# Python:用 OpenTelemetry 追踪 Agent 执行
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter

provider = TracerProvider()
provider.add_span_processor(
    BatchSpanProcessor(
        OTLPSpanExporter(endpoint="http://otel-collector:4317")
    )
)
trace.set_tracer_provider(provider)

tracer = trace.get_tracer("agent-framework")

class TracedAgent:
    def __init__(self, name, llm, tools):
        self.name = name
        self.llm = llm
        self.tools = {t.name: t for t in tools}

    async def run(self, task: str) -> str:
        with tracer.start_as_current_span(
            "agent.run",
            attributes={
                "agent.name": self.name,
                "agent.task": task[:200],  # 截断避免超大 span
            }
        ) as root_span:
            messages = [{"role": "user", "content": task}]
            max_iterations = 10

            for i in range(max_iterations):
                with tracer.start_as_current_span(
                    "agent.llm_call",
                    attributes={"agent.iteration": i}
                ) as llm_span:
                    response = await self.llm.ainvoke(messages)
                    input_tokens = response.usage_metadata.get("input_tokens", 0)
                    output_tokens = response.usage_metadata.get("output_tokens", 0)

                    llm_span.set_attributes({
                        "gen_ai.system": "openai",
                        "gen_ai.request.model": self.llm.model_name,
                        "gen_ai.usage.input_tokens": input_tokens,
                        "gen_ai.usage.output_tokens": output_tokens,
                    })

                if response.tool_calls:
                    for tc in response.tool_calls:
                        with tracer.start_as_current_span(
                            f"agent.tool.{tc['name']}",
                            attributes={
                                "tool.name": tc["name"],
                                "tool.args": str(tc["args"])[:500],
                            }
                        ) as tool_span:
                            try:
                                result = await self.tools[tc["name"]].ainvoke(tc["args"])
                                tool_span.set_attribute("tool.result", str(result)[:500])
                                tool_span.set_attribute("tool.status", "success")
                            except Exception as e:
                                tool_span.set_attribute("tool.status", "error")
                                tool_span.set_attribute("error.type", type(e).__name__)
                                tool_span.record_exception(e)
                                result = f"Error: {e}"
                            messages.append({"role": "tool", "content": str(result)})
                else:
                    root_span.set_attribute("agent.result_length", len(response.content))
                    return response.content

            root_span.set_attribute("agent.status", "max_iterations_reached")
            return "Max iterations reached"

9. 可观测性 Checklist

✅ 最小可观测性清单(上线前必做)

📊 可观测性成熟度评估

勾选你已实现的项,评估可观测性成熟度

0%

工具选型矩阵

类别开源方案商业方案独立开发者推荐
MetricsPrometheus + GrafanaDatadog / New RelicPrometheus + Grafana(免费,生态好)
TracesJaeger / Zipkin / Grafana TempoDatadog APM / Honeycomb / LightstepGrafana Tempo(与 Loki 无缝集成)
LogsLoki / Elasticsearch+KibanaDatadog Logs / SplunkLoki + Grafana(轻量,与生态统一)
SDK/采集OpenTelemetry各厂商 SDKOTel SDK(厂商中立)
Agent 追踪LangSmith / Phoenix / 自建 OTelLangSmith Pro / BraintrustPhoenix (Arize)(开源,本地运行)
告警Alertmanager / Grafana AlertsPagerDuty / OpsgenieGrafana Alerts(与仪表板集成)
💡 独立开发者最小栈
  1. OpenTelemetry SDK — 应用内埋点(自动+手动)
  2. OTel Collector — 统一采集和路由
  3. Grafana Cloud 免费层 — Metrics (10K series) + Logs (50GB) + Traces (都免费够用)
  4. Grafana Alerts — 关键指标告警到 Telegram/Slack
这样 $0 就有完整的三支柱可观测性。等业务增长再自建 Prometheus/Loki/Tempo。
🔗 相关阅读

🌐 分布式系统总览 · ⚡ 背压 & 流控 · 🔄 分布式事务 · 📜 Event Sourcing · 🧠 第一性原理 · 💡 LLM 成本监控

📅 数据采集日期:2026-05-18 · OpenTelemetry 规范版本截至写作时为最新稳定版
本页面内容基于 OpenTelemetry 官方文档、Google SRE 书籍和 Grafana Labs 文档整理