2. 添加 otelgin 和 otelhttp 中间件
步骤描述:
• 使用 OpenTelemetry 的 Gin 或 HTTP 中间件自动为每个请求创建根 Span,并记录 HTTP 相关的元数据(如方法、路径、状态码)。
代码示例(Gin):
go
import (
"github.com/gin-gonic/gin"
"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
)
func main() {
r := gin.Default()
// 添加 OpenTelemetry 中间件
r.Use(otelgin.Middleware("order-service")) // 服务名称需与 TracerProvider 配置一致
r.GET("/api/orders", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Hello, orders!"})
})
r.Run(":8080")
}
代码示例(HTTP):
go
import (
"net/http"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api/orders", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, orders!"))
})
// 包装 HTTP 处理器
handler := otelhttp.NewHandler(http.DefaultServeMux, "order-service")
http.ListenAndServe(":8080", handler)
}
补充说明:
-
服务名称一致性 : • 中间件中的服务名称(如
"order-service"
)必须与TracerProvider
中配置的service.name
一致,以确保链路追踪工具正确识别服务。 -
上下文传递 : • 中间件会自动将 Trace 上下文注入到 HTTP 请求头中(如
traceparent
),确保跨服务调用时上下文连续。 -
自定义属性: • 可在中间件中添加自定义属性(如用户 ID、请求 ID):
gor.Use(func(c *gin.Context) { ctx := metadata.AppendToOutgoingContext(c.Request.Context(), "user-id", "123") c.Request = c.Request.WithContext(ctx) c.Next() })
-
日志集成: • 结合 OpenTelemetry Logs SDK,将日志与 Trace 关联,提升可观测性。
3. 在服务调用其他服务时通过 otel.Tracer.Start
创建子 Span
步骤描述:
• 在跨服务调用时,手动创建子 Span,并将 Trace 上下文注入到请求头中,确保调用链的连续性。
代码示例(gRPC):
go
func (s *OrderService) CallAnotherService(ctx context.Context, req *pb.Request) (*pb.Response, error) {
// 创建子 Span
ctx, span := otel.Tracer("call-another-service").Start(
ctx,
"call-another-service",
trace.WithSpanKind(trace.SpanKindClient), // 标记为客户端调用
)
defer span.End()
// 将 Trace 上下文注入到 gRPC 元数据中
md, _ := metadata.FromOutgoingContext(ctx)
ctx = metadata.NewOutgoingContext(ctx, metadata.Join(md, propagation.HeaderCarrier(propagator)))
// 调用远程服务
client := pb.NewAnotherServiceClient(s.conn)
return client.AnotherMethod(ctx, req)
}
代码示例(HTTP):
go
func CallExternalAPI(ctx context.Context, url string) (*http.Response, error) {
// 创建子 Span
ctx, span := otel.Tracer("call-external-api").Start(
ctx,
"call-external-api",
trace.WithSpanKind(trace.SpanKindClient),
)
defer span.End()
// 将 Trace 上下文注入到 HTTP 请求头中
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
propagator.Inject(ctx, propagation.HeaderCarrier(req.Header))
// 发送请求
client := &http.Client{}
return client.Do(req)
}
补充说明:
-
Span 类型 : • 使用
trace.SpanKindClient
标记客户端调用,确保链路追踪工具正确识别调用方向。 -
上下文注入 : • 使用 OpenTelemetry 的
propagator
将 Trace 上下文注入到请求头中(如 gRPC 的metadata
或 HTTP 的Header
)。 • 示例(HTTP):gopropagator.Inject(ctx, propagation.HeaderCarrier(req.Header))
-
跨语言支持: • 确保所有服务使用相同的 Propagator(如 W3C TraceContext),以实现跨语言链路追踪。
-
错误处理: • 在子 Span 中记录错误状态和耗时,便于排查问题。
4. 补充步骤和建议
虽然上述步骤已覆盖核心流程,但仍有一些补充步骤和优化建议:
(1) 初始化 Propagator
• 确保正确配置上下文传播器(Propagator),以支持跨服务调用时的 Trace 上下文传递。 • 示例:
go
import "go.opentelemetry.io/otel/propagation"
func init() {
propagator := propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{}, // W3C TraceContext
propagation.Baggage{}, // Baggage
)
otel.SetTextMapPropagator(propagator)
}
(2) 添加 Metrics 和 Logs
• Metrics: • 使用 OpenTelemetry Metrics SDK 收集服务性能指标(如请求延迟、错误率)。 • 示例: ```go import "go.opentelemetry.io/otel/metric"
css
meter := otel.Meter("order-service")
requestCounter := meter.Int64Counter("requests_total")
requestCounter.Add(ctx, 1)
```
• Logs: • 使用 OpenTelemetry Logs SDK 记录结构化日志,并与 Trace 关联。 • 示例: ```go import "go.opentelemetry.io/otel/sdk/log"
css
logger := log.NewLogger(log.WithSink(os.Stdout))
logger.Log(context.Background(), "info", "request received")
```
(3) 配置 Exporter
• 根据需求选择合适的 Exporter(如 Jaeger、Zipkin、Prometheus、OTLP)。 • 示例(OTLP):
go
exporter, _ := otlp.NewExporter(otlp.WithInsecure())
tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exporter))
(4) 动态配置
• 使用环境变量或配置文件动态设置服务名称、采样率和导出器。 • 示例:
go
serviceName := os.Getenv("SERVICE_NAME")
if serviceName == "" {
serviceName = "default-service"
}
(5) 测试和验证
• 使用链路追踪工具(如 Jaeger、Zipkin)验证 Span 是否正确生成和传递。 • 检查 Trace 的完整性(如服务间调用链是否连续)。
完整流程总结
步骤 | 描述 | 工具/组件 |
---|---|---|
1. 初始化 Tracer Provider | 配置全局 Tracer Provider 和 Exporter,定义资源属性和采样策略。 | sdktrace.NewTracerProvider |
2. 添加中间件 | 使用 otelgin.Middleware 或 otelhttp 自动创建根 Span,记录 HTTP 元数据。 |
otelgin 或 otelhttp |
3. 创建子 Span | 在跨服务调用时手动创建子 Span,并注入 Trace 上下文。 | otel.Tracer.Start |
4. 配置 Propagator | 确保 Trace 上下文在服务间正确传递。 | propagation.NewCompositeTextMapPropagator |
5. 添加 Metrics 和 Logs | 收集性能指标和结构化日志,增强可观测性。 | otel.Meter , otel/sdk/log |
6. 动态配置和测试 | 根据环境动态配置 Tracer,并验证链路追踪数据的完整性。 | 环境变量、测试工具 |
最终建议
• 核心流程 :上述步骤已覆盖单个服务添加 OpenTelemetry 的主要流程。 • 补充优化 :根据实际需求,添加 Propagator 配置、Metrics、Logs 和动态配置功能,进一步提升可观测性。 • 验证和监控:通过链路追踪工具(如 Jaeger)和监控系统(如 Prometheus)验证和优化链路追踪数据。
通过以上补充,可以确保单个服务的 OpenTelemetry 实现更加完整和健壮。