链路追踪与可观测性
go-zero 从 v1.5.0 起全面基于 OpenTelemetry 实现可观测性,自动为 HTTP、gRPC、SQL、Redis 创建 Span,配合 Prometheus 指标和结构化日志,形成完整的可观测性体系。
可观测性三支柱
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Traces │ │ Metrics │ │ Logs │
│ 链路追踪 │ │ 指标监控 │ │ 结构化日志 │
├──────────────┤ ├──────────────┤ ├──────────────┤
│ OpenTelemetry│ │ Prometheus │ │ logx (JSON) │
│ → Jaeger │ │ → Grafana │ │ → ELK/Loki │
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
└──────────────────┼──────────────────┘
trace_id 关联链路追踪配置
服务端配置
Telemetry:
Name: user-api # 服务名称(在 Jaeger 中显示)
Endpoint: jaeger:4317 # OTLP gRPC 端点
Sampler: 1.0 # 采样率 0.0 ~ 1.0
Batcher: otlpgrpc # otlpgrpc | otlphttp代码加载
// etc/config.go
type Config struct {
rest.RestConf
Telemetry trace.Config
}go-zero 在第一次请求时自动注册追踪器,无需额外代码。
采样策略
| 策略 | 配置 | 适用场景 |
|---|---|---|
| 全量采样 | Sampler: 1.0 |
开发/预发布环境 |
| 10% 采样 | Sampler: 0.1 |
普通生产环境 |
| 1% 采样 | Sampler: 0.01 |
高吞吐(>10k QPS) |
💡 采样在 Trace 根节点(API 网关)决策,同一 Trace 的所有下游 Span 自动跟随。
Jaeger 部署(OTLP 协议)
🚨 重要:go-zero v1.10.0 起移除了
jaegerbatcher,统一使用 OTLP 协议。Jaeger 版本需 ≥1.35。
Docker Compose 部署
version: "3"
services:
jaeger:
image: jaegertracing/all-in-one:1.57
ports:
- "16686:16686" # Web UI
- "4317:4317" # OTLP gRPC(go-zero 使用)
- "4318:4318" # OTLP HTTP(可选)
environment:
- COLLECTOR_OTLP_ENABLED=truego-zero 配置对应
Telemetry:
Name: my-service
Endpoint: localhost:4317 # 对应 OTLP gRPC 端口
Sampler: 1.0
Batcher: otlpgrpcJaeger UI
访问 http://localhost:16686 查看链路追踪。
自动追踪内容
go-zero 自动为以下组件创建 Span(零代码改动):
| 组件 | 追踪内容 |
|---|---|
| HTTP 入站 | URL、方法、状态码、耗时 |
| HTTP 出站 | 目标 URL、方法、状态码 |
| gRPC 调用 | 服务名、方法名、状态码 |
| SQL 查询(sqlx) | SQL 语句、影响行数、耗时 |
| Redis 命令 | 命令名称、Key 前缀、耗时 |
跨服务传播
使用 W3C TraceContext 标准,通过 traceparent Header 自动传递 trace_id 和 span_id。
API → RPC: request header { "traceparent": "00-<traceId>-<spanId>-01" }
RPC → DB: context 传递 trace_id自定义业务 Span
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
)
func (l *OrderLogic) CreateOrder(ctx context.Context) error {
tracer := otel.Tracer("biz")
ctx, span := tracer.Start(ctx, "create-order")
defer span.End()
span.SetAttributes(
attribute.Int64("order_id", orderId),
attribute.String("operation", "create"),
attribute.Float64("amount", amount),
)
// 记录事件
span.AddEvent("payment-verified", trace.WithAttributes(
attribute.String("method", "wechat"),
))
// 记录错误
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
}
// 业务逻辑...
}Prometheus 指标监控
内置指标(默认启用)
go-zero 自动暴露以下 Prometheus 指标:
| 指标名称 | 类型 | 说明 |
|---|---|---|
http_request_duration_ms |
Histogram | 请求耗时分布 |
http_request_error_total |
Counter | 错误请求计数 |
默认 buckets(毫秒):[5, 10, 25, 50, 100, 250, 500, 1000]
配置
Prometheus:
Host: 0.0.0.0
Port: 9090 # metrics 端口
Path: /metrics验证
# 访问 metrics 端点
curl http://localhost:9090/metrics
# 输出示例:
# http_request_duration_ms_bucket{path="/user/login",le="100"} 1523
# http_request_error_total{path="/user/login",code="500"} 2禁用监控
Prometheus: falseGrafana 对接
- Prometheus 采集 go-zero 的
/metrics端点 - Grafana 数据源配置为 Prometheus
- 导入或自建 Dashboard
推荐面板:
| 面板 | PromQL |
|---|---|
| QPS | rate(http_request_duration_ms_count[1m]) |
| P99 延迟 | histogram_quantile(0.99, rate(http_request_duration_ms_bucket[1m])) |
| 错误率 | rate(http_request_error_total[1m]) / rate(http_request_duration_ms_count[1m]) |
结构化日志
logx 配置
Log:
ServiceName: user-api
Mode: file
Level: info
Path: logs/user-api
Encoding: json # json | plain
KeepDays: 7使用
import "github.com/zeromicro/go-zero/core/logx"
// 基础使用
logx.Info("user created")
logx.Infof("user %d created", userId)
logx.Error("failed to create user")
// 带 context(自动注入 trace_id)
logx.WithContext(ctx).Infow("order created",
logx.Field("orderId", orderId),
logx.Field("userId", userId),
)日志输出示例
启用 Telemetry 后,日志自动包含 trace 和 span 字段:
{
"@timestamp": "2026-07-10T10:30:00.000Z",
"level": "info",
"content": "order created",
"trace": "3e2a9bc1f...",
"span": "abc123...",
"orderId": 123,
"userId": 456
}💡 通过 trace_id 可以在 Jaeger 中查看完整调用链,也可以在 Loki/ELK 中按 trace_id 聚合日志。
内置中间件(全部默认启用)
go-zero 的 HTTP 和 RPC 服务默认启用以下可观测中间件:
| 中间件 | 功能 | 配置项 |
|---|---|---|
TraceHandler |
链路追踪 | Telemetry |
PrometheusHandler |
指标暴露 | Prometheus |
LogHandler |
请求日志 | Log |
BreakerHandler |
熔断 | Breaker: true |
MaxConnsHandler |
并发限制 | MaxConns |
TimeoutHandler |
超时控制 | Timeout |
SheddingHandler |
过载降载 | Shedding |
RecoverHandler |
Panic 恢复 | 始终启用 |
💡 这些中间件自动生效,无需手动配置。你可以通过配置文件关闭或调整。
可观测性整体架构
┌──────────────┐
│ Client │
└──────┬───────┘
│ HTTP / gRPC
┌──────▼───────┐
│ API Gateway │
┌──────────┤ (go-zero) ├──────────┐
│ └──────┬───────┘ │
│ │ │
/metrics 自动埋点 结构化日志
│ │ │
┌──────▼──────┐ ┌─────▼─────┐ ┌──────▼──────┐
│ Prometheus │ │ OTLP │ │ File/ELK/ │
│ 采集指标 │ │ Collector │ │ Loki │
└──────┬──────┘ └─────┬─────┘ └──────┬──────┘
│ │ │
┌──────▼──────┐ ┌─────▼─────┐ ┌──────▼──────┐
│ Grafana │ │ Jaeger │ │ Grafana │
│ 监控面板 │ │ 链路追踪 │ │ 日志面板 │
└─────────────┘ └───────────┘ └─────────────┘常见陷阱
| 陷阱 | 说明 |
|---|---|
| 🚨 OTLP 端口混淆 | Jaeger v1.35 以前用 14268/14250,v1.35+ 统一用 4317/4318 |
| 🚨 生产环境全量采样 | Sampler: 1.0 会显著增加 CPU 和网络开销 |
| 🚨 Jaeger batcher 已废弃 | v1.10.0 起必须用 otlpgrpc 或 otlphttp |
| 🚨 Telemetry 配置缺失 | 缺少配置不会报错,但 trace 数据会静默丢失 |
| 🚨 logx.Info 不带 ctx | 不使用 logx.WithContext(ctx) 会导致日志丢失 trace_id |
| 🚨 Prometheus 端口冲突 | 多服务同机部署时注意 metrics 端口不要重复 |
| 🚨 过度自定义 Span | 每个小函数都加 Span 反而影响性能,聚焦在关键业务节点 |