2019-11-27 02:14:09 +02:00
|
|
|
// 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"
|
2020-01-28 20:13:46 +02:00
|
|
|
"go.opentelemetry.io/otel/api/correlation"
|
2019-11-27 02:14:09 +02:00
|
|
|
"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)
|
2020-01-28 20:13:46 +02:00
|
|
|
ctx = correlation.WithMap(ctx, correlation.NewMap(correlation.MapUpdate{
|
2019-11-27 02:14:09 +02:00
|
|
|
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(
|
2020-02-04 18:55:03 +02:00
|
|
|
trace.ContextWithRemoteSpanContext(ctx, spanCtx),
|
2019-11-27 02:14:09 +02:00
|
|
|
"hello-api-op",
|
|
|
|
trace.WithAttributes(serverSpanAttrs...),
|
|
|
|
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 {
|
2019-12-09 23:03:11 +02:00
|
|
|
s, _ := status.FromError(err)
|
2019-12-11 18:51:32 +02:00
|
|
|
trace.SpanFromContext(ctx).SetStatus(s.Code())
|
2019-11-27 02:14:09 +02:00
|
|
|
} else {
|
2019-12-11 18:51:32 +02:00
|
|
|
trace.SpanFromContext(ctx).SetStatus(codes.OK)
|
2019-11-27 02:14:09 +02:00
|
|
|
}
|
|
|
|
}
|