可观测性三支柱:Traces、Metrics、Logs
在微服务和云原生架构中,系统由数十甚至上百个服务组成,传统的日志排查方式已经力不从心。可观测性(Observability)成为理解系统行为的必备能力。
可观测性 ≠ 监控。监控是已知问题的检测,可观测性是未知问题的探索。
可观测性的三大支柱:
| 支柱 | 作用 | 典型场景 | 示例 |
|---|---|---|---|
| Traces | 请求的完整生命周期 | 跨服务调用链追踪 | 用户下单→库存扣减→支付→通知 |
| Metrics | 系统的量化指标 | 性能监控、容量规划 | QPS=1200, P99延迟=230ms |
| Logs | 离散的事件记录 | 错误排查、审计 | NullPointerException at OrderService:142 |
OpenTelemetry是CNCF的孵化项目,目标是提供一套厂商中立的API、SDK和工具,统一采集Traces、Metrics、Logs三大信号。
OpenTelemetry架构:SDK、Collector、Exporter
OpenTelemetry采用三层架构,实现采集与导出的解耦:
┌─────────────────────────────────────────────────┐
│ 应用层 │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ SDK │ │ SDK │ │ SDK │ ← 自动/手动埋点 │
│ │(Java)│ │(Go) │ │(Node)│ │
│ └──┬───┘ └──┬───┘ └──┬───┘ │
│ │ │ │ │
│ └─────────┼─────────┘ │
│ │ OTLP(gRPC/HTTP) │
│ ┌────────────▼────────────┐ │
│ │ OTel Collector │ ← 数据处理中枢 │
│ │ ┌──────┐┌──────┐┌────┐│ │
│ │ │Recv ││Proc ││Exp ││ ← 接收→处理→导出 │
│ │ └──────┘└──────┘└────┘│ │
│ └──────┬───────┬─────────┘ │
│ │ │ │
│ ┌────▼──┐ ┌──▼─────┐ │
│ │Jaeger │ │Prometheus│ ← 后端存储 │
│ └───────┘ └─────────┘ │
└─────────────────────────────────────────────────┘
三层模型详解
| 层级 | 组件 | 职责 | 部署位置 |
|---|---|---|---|
| SDK | OpenTelemetry SDK | 自动/手动埋点,生成遥测数据 | 应用进程内 |
| Collector | OTel Collector | 接收、处理、路由遥测数据 | 独立进程/容器 |
| Exporter | OTLP Exporter | 将数据导出到后端 | Collector内部 |
**OTLP(OpenTelemetry Protocol)**是SDK与Collector之间的通信协议,基于gRPC/HTTP,是整个架构的粘合剂。
Java Agent自动埋点:零代码侵入
OpenTelemetry Java Agent最大的魅力在于零代码侵入——只需添加一个JVM参数,即可自动追踪Spring Boot、HTTP客户端、JDBC、Redis等常用组件。
1. 下载Java Agent
curl -L -O https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
2. 启动Spring Boot应用
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.service.name=order-service \
-Dotel.traces.exporter=otlp \
-Dotel.metrics.exporter=otlp \
-Dotel.logs.exporter=otlp \
-Dotel.exporter.otlp.endpoint=http://collector:4317 \
-Dotel.exporter.otlp.protocol=grpc \
-jar my-app.jar
3. 常用配置项
| 配置项 | 默认值 | 说明 |
|---|---|---|
otel.service.name |
unknown | 服务名称,标识调用链中的节点 |
otel.traces.exporter |
otlp | Trace导出器:otlp/zipkin/none |
otel.metrics.exporter |
otlp | Metric导出器:otlp/prometheus/none |
otel.logs.exporter |
otlp | Log导出器:otlp/none |
otel.exporter.otlp.endpoint |
http://localhost:4317 | Collector地址 |
otel.instrumentation.common.enabled |
true | 全局开关 |
otel.instrumentation.spring-web.enabled |
true | Spring Web自动埋点 |
otel.instrumentation.jdbc.enabled |
true | JDBC自动埋点 |
otel.traces.sampler |
parentBased_alwaysOn | 采样策略 |
4. 自动埋点覆盖范围
| 组件 | 自动追踪 | 自动Metrics | 说明 |
|---|---|---|---|
| Spring Web MVC/WebFlux | ✅ | ✅ | HTTP请求、Controller方法 |
| Spring RestTemplate/WebClient | ✅ | ✅ | 出站HTTP调用 |
| JDBC / R2DBC | ✅ | ✅ | 数据库查询 |
| Redis (Lettuce/Jedis) | ✅ | ✅ | Redis操作 |
| Kafka | ✅ | ✅ | 消息生产/消费 |
| gRPC | ✅ | ✅ | gRPC调用 |
| Log4j2 / Logback | ✅ | - | 日志关联TraceID |
效果:一个JVM参数,即可在Jaeger中看到完整的跨服务调用链,无需修改任何业务代码。
分布式追踪实战:跨服务调用链可视化
调用链示例
用户下单的请求经过4个服务,OpenTelemetry自动将整个调用链串联:
[OrderService] POST /api/orders
├── [InventoryService] POST /api/inventory/deduct
│ └── [Redis] GET inventory:product-123
├── [PaymentService] POST /api/payments/charge
│ ├── [DB] INSERT INTO payments
│ └── [Kafka] PRODUCE payment-success
└── [NotificationService] CONSUME payment-success
└── [SMTP] SEND order-confirmation
在Jaeger/Tempo中,这个调用链被可视化为一棵树,每个节点是一个Span,包含:
- Span名称:如
POST /api/orders - 持续时间:如
230ms - Attributes:如
http.method=POST,http.status_code=200 - Events:如异常堆栈
- Links:关联的Span(如Kafka消息的Producer→Consumer)
Trace上下文传播
OpenTelemetry通过W3C Trace Context标准在服务间传播TraceID:
GET /api/inventory/deduct HTTP/1.1
Host: inventory-service:8080
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067bf0bc902b7-01
tracestate: rojo=00f067bf0bc902b7
| 字段 | 格式 | 说明 |
|---|---|---|
| version | 00 |
W3C版本 |
| trace-id | 32位hex | 全局唯一的Trace标识 |
| parent-id | 16位hex | 当前Span的父Span |
| trace-flags | 2位hex | 01=采样,00=不采样 |
自定义Span与Attributes:业务语义埋点
自动埋点只能覆盖技术层面,业务语义需要手动添加自定义Span和Attributes。
Spring Boot集成
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-spring-boot-starter</artifactId>
</dependency>
自定义Span示例
@Service
public class OrderService {
private final Tracer tracer;
public OrderService(Tracer tracer) {
this.tracer = tracer;
}
public OrderResult createOrder(CreateOrderRequest request) {
Span span = tracer.spanBuilder("order.create")
.setAttribute("order.userId", request.getUserId())
.setAttribute("order.productCount", request.getItems().size())
.setAttribute("order.totalAmount", request.getTotalAmount())
.startSpan();
try (Scope scope = span.makeCurrent()) {
OrderResult result = doCreateOrder(request);
span.setAttribute("order.id", result.getOrderId());
span.setAttribute("order.status", result.getStatus().name());
span.addEvent("order.created", Attributes.builder()
.put("order.id", result.getOrderId())
.build());
return result;
} catch (Exception e) {
span.recordException(e);
span.setStatus(StatusCode.ERROR, e.getMessage());
throw e;
} finally {
span.end();
}
}
}
Attributes命名规范
| 前缀 | 语义 | 示例 |
|---|---|---|
order. |
订单业务 | order.id, order.status |
user. |
用户业务 | user.id, user.tier |
db. |
数据库 | db.operation, db.table |
messaging. |
消息 | messaging.destination, messaging.operation |
http. |
HTTP | http.method, http.status_code |
建议:遵循OpenTelemetry语义约定(Semantic Conventions),使用标准前缀和命名,便于后端工具理解。
Metrics采集:Counter、Histogram、Gauge实战
OpenTelemetry Metrics提供三种主要的仪器类型:
| 类型 | 语义 | 单调性 | 聚合方式 | 典型场景 |
|---|---|---|---|---|
| Counter | 只增不减的计数 | 单调递增 | Sum | 请求总数、错误总数 |
| Histogram | 值的分布统计 | - | Histogram | 请求延迟、响应大小 |
| Gauge | 当前瞬时值 | 可增可减 | Last Value | 当前连接数、队列长度 |
Spring Boot Metrics示例
@Service
public class PaymentService {
private final LongCounter requestCounter;
private final LongHistogram latencyHistogram;
private final DoubleGauge activeConnectionsGauge;
public PaymentService(MeterProvider meterProvider) {
Meter meter = meterProvider.meterBuilder("payment-service")
.setInstrumentationVersion("1.0.0")
.build();
this.requestCounter = meter.counterBuilder("payment.requests.total")
.setDescription("Total payment requests")
.setUnit("1")
.build();
this.latencyHistogram = meter.histogramBuilder("payment.request.duration")
.setDescription("Payment request duration")
.setUnit("ms")
.ofLongs()
.setExplicitBucketBoundariesAdvice(
Advice.explicitBuckets(
List.of(10.0, 25.0, 50.0, 100.0, 250.0, 500.0, 1000.0)
)
)
.build();
this.activeConnectionsGauge = meter.gaugeBuilder("payment.active.connections")
.setDescription("Active payment connections")
.setUnit("1")
.buildWithCallback(measurement -> {
measurement.record(getActiveConnectionCount());
});
}
public PaymentResult processPayment(PaymentRequest request) {
long startTime = System.currentTimeMillis();
requestCounter.add(1, Attributes.builder()
.put("payment.method", request.getMethod().name())
.put("payment.currency", request.getCurrency())
.build());
try {
PaymentResult result = doProcessPayment(request);
long duration = System.currentTimeMillis() - startTime;
latencyHistogram.record(duration, Attributes.builder()
.put("payment.method", request.getMethod().name())
.put("http.status_code", 200)
.build());
return result;
} catch (PaymentException e) {
long duration = System.currentTimeMillis() - startTime;
latencyHistogram.record(duration, Attributes.builder()
.put("payment.method", request.getMethod().name())
.put("error.type", e.getClass().getSimpleName())
.build());
throw e;
}
}
}
Collector部署模式
OTel Collector是数据处理中枢,支持三种部署模式:
| 模式 | 架构 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| Sidecar | 每个Pod一个Collector | 隔离性好,配置灵活 | 资源开销大 | 大规模K8s集群 |
| DaemonSet | 每个Node一个Collector | 资源利用率高 | 单点故障风险 | 通用推荐 |
| Gateway | 独立集群 | 集中管理,可扩展 | 网络跳数多 | 多集群/多环境 |
DaemonSet模式配置
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: otel-collector
namespace: observability
spec:
selector:
matchLabels:
app: otel-collector
template:
metadata:
labels:
app: otel-collector
spec:
containers:
- name: collector
image: otel/opentelemetry-collector-contrib:0.96.0
ports:
- containerPort: 4317 # OTLP gRPC
- containerPort: 4318 # OTLP HTTP
- containerPort: 8889 # Metrics
volumeMounts:
- name: config
mountPath: /etc/otelcol
volumes:
- name: config
configMap:
name: otel-collector-config
Collector配置文件
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_mib: 512
filter:
error_mode: ignore
traces:
span:
- 'attributes["http.route"] == "/health"'
- 'attributes["http.route"] == "/metrics"'
exporters:
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
otlp/tempo:
endpoint: tempo:4317
prometheusremotewrite:
endpoint: http://prometheus:9090/api/v1/write
elasticsearch:
endpoints:
- http://elasticsearch:9200
logs_index: otel-logs
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch, filter]
exporters: [otlp/jaeger]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheusremotewrite]
logs:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [elasticsearch]
采样策略:头部采样 vs 尾部采样
在生产环境中,采集所有Trace会产生巨大的数据量和成本。采样策略是平衡可见性和成本的关键。
头部采样(Head-Based Sampling)
在Trace开始时决定是否采样,简单高效:
# 采样10%的请求
otel.traces.sampler=parentbased_traceidratio
otel.traces.sampler.arg=0.1
| 优点 | 缺点 |
|---|---|
| 实现简单 | 可能错过异常请求 |
| 无额外开销 | 错误请求可能被丢弃 |
| 适合高流量 | 无法保证错误100%采样 |
尾部采样(Tail-Based Sampling)
在Trace结束后根据完整信息决定是否采样,能保证错误请求100%被采集:
# Collector尾部采样配置
processors:
tail_sampling:
decision_wait: 10s
num_traces: 100000
expected_new_traces_per_sec: 100
sampling_policies:
- name: errors-policy
type: status_code
status_code:
status_codes:
- ERROR
- name: slow-policy
type: latency
latency:
threshold_ms: 1000
- name: health-policy
type: and
and:
sub_policies:
- name: health-route
type: string_attribute
string_attribute:
key: http.route
values:
- /api/health
invert_match: true
- name: fallback-policy
type: probabilistic
probabilistic:
sampling_percentage: 10
| 采样策略 | 规则 | 说明 |
|---|---|---|
| errors-policy | 状态码=ERROR | 所有错误请求100%采样 |
| slow-policy | 延迟>1000ms | 所有慢请求100%采样 |
| health-policy | 路由≠/api/health | 排除健康检查 |
| fallback-policy | 10%概率 | 其他请求采样10% |
推荐:生产环境使用尾部采样,保证错误和慢请求100%可见,正常请求按比例采样。
前端RUM集成:OTLP HTTP Exporter + Web Tracer
OpenTelemetry不仅适用于后端,前端RUM(Real User Monitoring)同样可以接入:
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { ZoneContextManager } from '@opentelemetry/context-zone';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request';
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
const exporter = new OTLPTraceExporter({
url: 'https://collector.example.com/v1/traces',
headers: {
'X-API-Key': 'your-api-key',
},
});
const provider = new WebTracerProvider();
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
provider.register({ contextManager: new ZoneContextManager() });
registerInstrumentations({
instrumentations: [
new XMLHttpRequestInstrumentation({
propagateTraceHeaderCorsUrls: [/api\.example\.com/],
}),
new FetchInstrumentation({
propagateTraceHeaderCorsUrls: [/api\.example\.com/],
}),
],
});
效果:前端发起的HTTP请求自动携带
traceparent头,后端接收后继续追踪,实现从前端到后端的完整调用链。
与Prometheus + Grafana的经典组合
OpenTelemetry + Prometheus + Grafana是2026年最主流的可观测性技术栈:
OpenTelemetry SDK → OTel Collector → Prometheus → Grafana
→ Jaeger/Tempo → Grafana
→ Loki → Grafana
Prometheus暴露Metrics
# Collector配置:同时导出到Prometheus和OTLP
exporters:
prometheus:
endpoint: 0.0.0.0:8889
namespace: otel
otlp:
endpoint: tempo:4317
service:
pipelines:
metrics:
receivers: [otlp]
processors: [batch]
exporters: [prometheus]
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
Grafana Dashboard关键指标
| 面板 | 指标 | PromQL | 告警阈值 |
|---|---|---|---|
| 请求速率 | QPS | rate(http_server_request_total[5m]) |
>10000 |
| P99延迟 | 延迟分布 | histogram_quantile(0.99, rate(http_server_duration_bucket[5m])) |
>500ms |
| 错误率 | 错误比例 | rate(http_server_request_total{status=~"5.."}[5m]) / rate(http_server_request_total[5m]) |
>1% |
| 饱和度 | 资源使用 | process_runtime_jvm_memory_used / process_runtime_jvm_memory_max |
>85% |
RED原则:Rate(请求速率)、Errors(错误率)、Duration(延迟),是服务监控的三个黄金指标。
总结
OpenTelemetry在2026年已经成为可观测性的事实标准。它的核心价值:
- 统一采集:一套SDK同时采集Traces、Metrics、Logs
- 厂商中立:不绑定任何后端,随时切换Jaeger/Tempo/Prometheus
- 零侵入:Java Agent自动埋点,无需修改业务代码
- 端到端:从前端RUM到后端微服务,完整调用链追踪
- 生态成熟:CNCF孵化项目,所有主流语言和框架均有SDK
可观测性不是奢侈品,而是微服务架构的必需品。 没有可观测性,微服务就是黑盒;有了OpenTelemetry,系统行为尽在掌握。
本站提供浏览器本地工具,免注册即可试用 →