我刚刚开始使用 OpenTelemetry,并为此创建了两个(微)服务:Standard 和 GeoMap。
最终用户向标准服务发送请求,标准服务又向 GeoMap 发送请求以获取信息,然后将结果返回给最终用户。我使用 gRPC 进行所有通信。
我已经这样设置了我的功能:
对于标准:
func (s *server) GetStandard(ctx context.Context, in *pb.GetStandardRequest) (*pb.GetStandardResponse, error) {
newCtx, span := otel.Tracer(name).Start(ctx, "GetStandard")
span.SpanContext()
defer span.End()
countryInfo, err := geoMapServiceClient.GetCountry(newCtx,
&pb.GetCountryRequest{
Name: in.Name,
})
....
return &pb.GetStandardResponse{
Standard: standard,
}, nil
}
对于地理地图
func (s *geomapService) GetCountry(ctx context.Context, in *pb.GetCountryRequest) (*pb.GetCountryResponse, error) {
_, span := otel.Tracer(name).Start(ctx, "GetCountry")
defer span.End()
span.SetAttributes(attribute.String("country", in.Name))
...
return &pb.GetCountryResponse{
Country: &country,
}, nil
}
这两个服务都配置为将其跟踪发送到 Jaeger 后端:
func tracerProvider(url string) (*tracesdk.TracerProvider, error) {
// Create the Jaeger exporter
exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
if err != nil {
return nil, err
}
tp := tracesdk.NewTracerProvider(
tracesdk.WithBatcher(exp),
tracesdk.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName(service),
attribute.String("environment", environment),
attribute.Int64("ID", id),
)),
)
return tp, nil
}
func main() {
tp, err := tracerProvider("http://localhost:14268/api/traces")
if err != nil {
log.Fatal(err)
}
defer func() {
if err := tp.Shutdown(context.Background()); err != nil {
log.Fatal(err)
}
}()
otel.SetTracerProvider(tp)
...
}
我希望 GeoMap 服务中生成的跨度是标准服务的子跨度(因为标准服务通过 geoMapServiceClient 发出请求)。然而这些跨度在 Jaeger 中是完全不相关的:
我在这里缺少什么?
编辑:
我已经成功地按照@Peter 在评论中建议的方式包含了拦截器。现在跨度相互链接,我可以看到来自 StandardService 的父跨度正在向 GeoMapService 发出请求:
但是,我在函数调用中定义了一个属性(请参阅
span.SetAttributes(attribute.String("country", in.Name))
),但我无法从子范围内看到该值。
为此,我必须切换到服务 GeoMap 生成的其他跟踪:
实际上我不需要有两个不同的跟踪,我希望所有内容都在同一个跟踪中(一个最终用户查询应该产生一个跟踪)。我应该如何从这里更改我的代码?
你的孩子跨度无法知道它的父母来自哪里。为此,您需要您的服务将正确的 HTTP 标头注入到您的 gRPC 请求中,并随后将其读回。
OpenTelemetry 提供了一个 propagators API,它允许您做到这一点。
虽然您可以在代码中手动执行此操作以注入/提取标头,但不建议这样做。 如果您将 otelgrpc 拦截器添加到您的应用程序中,该传播将自动完成,并且您的跨度将成为彼此的子级。