// Copyright 2019, OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tracing // gRPC tracing middleware // https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-rpc.md import ( "context" "go.opentelemetry.io/otel/plugin/grpctrace" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/distributedcontext" "go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/api/key" "go.opentelemetry.io/otel/api/trace" ) // UnaryServerInterceptor intercepts and extracts incoming trace data func UnaryServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { requestMetadata, _ := metadata.FromIncomingContext(ctx) metadataCopy := requestMetadata.Copy() entries, spanCtx := grpctrace.Extract(ctx, &metadataCopy) ctx = distributedcontext.WithMap(ctx, distributedcontext.NewMap(distributedcontext.MapUpdate{ MultiKV: entries, })) grpcServerKey := key.New("grpc.server") serverSpanAttrs := []core.KeyValue{ grpcServerKey.String("hello-world-server"), } tr := global.TraceProvider().Tracer("example/grpc") ctx, span := tr.Start( ctx, "hello-api-op", trace.WithAttributes(serverSpanAttrs...), trace.ChildOf(spanCtx), trace.WithSpanKind(trace.SpanKindServer), ) defer span.End() return handler(ctx, req) } // UnaryClientInterceptor intercepts and injects outgoing trace func UnaryClientInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { requestMetadata, _ := metadata.FromOutgoingContext(ctx) metadataCopy := requestMetadata.Copy() tr := global.TraceProvider().Tracer("example/grpc") err := tr.WithSpan(ctx, "hello-api-op", func(ctx context.Context) error { grpctrace.Inject(ctx, &metadataCopy) ctx = metadata.NewOutgoingContext(ctx, metadataCopy) err := invoker(ctx, method, req, reply, cc, opts...) setTraceStatus(ctx, err) return err }) return err } func setTraceStatus(ctx context.Context, err error) { if err != nil { s, _ := status.FromError(err) trace.SpanFromContext(ctx).SetStatus(s.Code()) } else { trace.SpanFromContext(ctx).SetStatus(codes.OK) } }