forked from cloudevents/sdk-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
distributed_tracing_extension.go
126 lines (111 loc) · 3.46 KB
/
distributed_tracing_extension.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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package extensions
import (
"context"
"reflect"
"strings"
"github.com/ian-mi/sdk-go/v2/pkg/event"
"github.com/ian-mi/sdk-go/v2/pkg/types"
"github.com/lightstep/tracecontext.go/traceparent"
"github.com/lightstep/tracecontext.go/tracestate"
"go.opencensus.io/trace"
octs "go.opencensus.io/trace/tracestate"
)
const (
TraceParentExtension = "traceparent"
TraceStateExtension = "tracestate"
)
// DistributedTracingExtension represents the extension for cloudevents context
type DistributedTracingExtension struct {
TraceParent string `json:"traceparent"`
TraceState string `json:"tracestate"`
}
// AddTracingAttributes adds the tracing attributes traceparent and tracestate to the cloudevents context
func (d DistributedTracingExtension) AddTracingAttributes(e event.EventWriter) {
if d.TraceParent != "" {
value := reflect.ValueOf(d)
typeOf := value.Type()
for i := 0; i < value.NumField(); i++ {
k := strings.ToLower(typeOf.Field(i).Name)
v := value.Field(i).Interface()
if k == TraceStateExtension && v == "" {
continue
}
e.SetExtension(k, v)
}
}
}
func GetDistributedTracingExtension(event event.Event) (DistributedTracingExtension, bool) {
if tp, ok := event.Extensions()[TraceParentExtension]; ok {
if tpStr, err := types.ToString(tp); err == nil {
var tsStr string
if ts, ok := event.Extensions()[TraceStateExtension]; ok {
tsStr, _ = types.ToString(ts)
}
return DistributedTracingExtension{TraceParent: tpStr, TraceState: tsStr}, true
}
}
return DistributedTracingExtension{}, false
}
// FromSpanContext populates DistributedTracingExtension from a SpanContext.
func FromSpanContext(sc trace.SpanContext) DistributedTracingExtension {
tp := traceparent.TraceParent{
TraceID: sc.TraceID,
SpanID: sc.SpanID,
Flags: traceparent.Flags{
Recorded: sc.IsSampled(),
},
}
entries := make([]string, 0, len(sc.Tracestate.Entries()))
for _, entry := range sc.Tracestate.Entries() {
entries = append(entries, strings.Join([]string{entry.Key, entry.Value}, "="))
}
return DistributedTracingExtension{
TraceParent: tp.String(),
TraceState: strings.Join(entries, ","),
}
}
// ToSpanContext creates a SpanContext from a DistributedTracingExtension instance.
func (d DistributedTracingExtension) ToSpanContext() (trace.SpanContext, error) {
tp, err := traceparent.ParseString(d.TraceParent)
if err != nil {
return trace.SpanContext{}, err
}
sc := trace.SpanContext{
TraceID: tp.TraceID,
SpanID: tp.SpanID,
}
if tp.Flags.Recorded {
sc.TraceOptions &= 1
}
if ts, err := tracestate.ParseString(d.TraceState); err == nil {
entries := make([]octs.Entry, 0, len(ts))
for _, member := range ts {
var key string
if member.Vendor != "" {
key = member.Tenant + "@" + member.Vendor
} else {
key = member.Tenant
}
entries = append(entries, octs.Entry{Key: key, Value: member.Value})
}
sc.Tracestate, _ = octs.New(nil, entries...)
}
return sc, nil
}
func (d DistributedTracingExtension) StartChildSpan(ctx context.Context, name string, opts ...trace.StartOption) (context.Context, *trace.Span) {
if sc, err := d.ToSpanContext(); err == nil {
tSpan := trace.FromContext(ctx)
ctx, span := trace.StartSpanWithRemoteParent(ctx, name, sc, opts...)
if tSpan != nil {
// Add link to the previous in-process trace.
tsc := tSpan.SpanContext()
span.AddLink(trace.Link{
TraceID: tsc.TraceID,
SpanID: tsc.SpanID,
Type: trace.LinkTypeParent,
})
}
return ctx, span
}
return ctx, nil
}