-
Notifications
You must be signed in to change notification settings - Fork 416
/
grpc.go
105 lines (92 loc) · 3.27 KB
/
grpc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016 Datadog, Inc.
//go:generate sh gen_proto.sh
// Package grpc provides functions to trace the google.golang.org/grpc package v1.2.
package grpc // import "github.com/DataDog/dd-trace-go/v2/contrib/google.golang.org/grpc"
import (
"context"
"errors"
"fmt"
"io"
"strings"
"github.com/DataDog/dd-trace-go/v2/contrib/google.golang.org/grpc/internal/grpcutil"
"github.com/DataDog/dd-trace-go/v2/ddtrace/ext"
"github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"
"github.com/DataDog/dd-trace-go/v2/internal/telemetry"
"github.com/golang/protobuf/proto"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
const componentName = "google.golang.org/grpc"
func init() {
telemetry.LoadIntegration(componentName)
tracer.MarkIntegrationImported(componentName)
}
// cache a constant option: saves one allocation per call
var spanTypeRPC = tracer.SpanType(ext.AppTypeRPC)
func (cfg *config) startSpanOptions(opts ...tracer.StartSpanOption) []tracer.StartSpanOption {
if len(cfg.tags) == 0 && len(cfg.spanOpts) == 0 {
return opts
}
ret := make([]tracer.StartSpanOption, 0, 1+len(cfg.tags)+len(opts))
for _, opt := range opts {
ret = append(ret, opt)
}
for _, opt := range cfg.spanOpts {
ret = append(ret, opt)
}
for key, tag := range cfg.tags {
ret = append(ret, tracer.Tag(key, tag))
}
return ret
}
func startSpanFromContext(
ctx context.Context, method, operation string, serviceFn func() string, opts ...tracer.StartSpanOption,
) (*tracer.Span, context.Context) {
methodElements := strings.SplitN(strings.TrimPrefix(method, "/"), "/", 2)
opts = append(opts,
tracer.ServiceName(serviceFn()),
tracer.ResourceName(method),
tracer.Tag(tagMethodName, method),
spanTypeRPC,
tracer.Tag(ext.RPCSystem, ext.RPCSystemGRPC),
tracer.Tag(ext.GRPCFullMethod, method),
tracer.Tag(ext.RPCService, methodElements[0]),
)
md, _ := metadata.FromIncomingContext(ctx) // nil is ok
if sctx, err := tracer.Extract(grpcutil.MDCarrier(md)); err == nil {
opts = append(opts, tracer.ChildOf(sctx))
}
return tracer.StartSpanFromContext(ctx, operation, opts...)
}
// finishWithError applies finish option and a tag with gRPC status code, disregarding OK, EOF and Canceled errors.
func finishWithError(span *tracer.Span, err error, cfg *config) {
if errors.Is(err, io.EOF) || errors.Is(err, context.Canceled) {
err = nil
}
errcode := status.Code(err)
if errcode == codes.OK || cfg.nonErrorCodes[errcode] {
err = nil
}
span.SetTag(tagCode, errcode.String())
if e, ok := status.FromError(err); ok && cfg.withErrorDetailTags {
for i, d := range e.Details() {
if d, ok := d.(proto.Message); ok {
span.SetTag(tagStatusDetailsPrefix+fmt.Sprintf("_%d", i), d.String())
}
}
}
// only allocate finishOptions if needed, and allocate the exact right size
var finishOptions []tracer.FinishOption
if err != nil {
if cfg.noDebugStack {
finishOptions = []tracer.FinishOption{tracer.WithError(err), tracer.NoDebugStack()}
} else {
finishOptions = []tracer.FinishOption{tracer.WithError(err)}
}
}
span.Finish(finishOptions...)
}