-
Notifications
You must be signed in to change notification settings - Fork 501
/
jaeger_propagator.go
156 lines (134 loc) · 4.5 KB
/
jaeger_propagator.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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package jaeger // import "go.opentelemetry.io/contrib/propagators/jaeger"
import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"
)
const (
jaegerHeader = "uber-trace-id"
separator = ":"
traceID128bitsWidth = 128 / 4
spanIDWidth = 64 / 4
idPaddingChar = "0"
flagsDebug = 0x02
flagsSampled = 0x01
flagsNotSampled = 0x00
deprecatedParentSpanID = "0"
)
var (
empty = trace.SpanContext{}
errMalformedTraceContextVal = errors.New("header value of uber-trace-id should contain four different part separated by : ")
errInvalidTraceIDLength = errors.New("invalid trace id length, must be either 16 or 32")
errMalformedTraceID = errors.New("cannot decode trace id from header, should be a string of hex, lowercase trace id can't be all zero")
errInvalidSpanIDLength = errors.New("invalid span id length, must be 16")
errMalformedSpanID = errors.New("cannot decode span id from header, should be a string of hex, lowercase span id can't be all zero")
errMalformedFlag = errors.New("cannot decode flag")
)
// Jaeger propagator serializes SpanContext to/from Jaeger Headers
//
// Jaeger format:
//
// uber-trace-id: {trace-id}:{span-id}:{parent-span-id}:{flags}.
type Jaeger struct{}
var _ propagation.TextMapPropagator = &Jaeger{}
// Inject injects a context to the carrier following jaeger format.
// The parent span ID is set to an dummy parent span id as the most implementations do.
func (jaeger Jaeger) Inject(ctx context.Context, carrier propagation.TextMapCarrier) {
sc := trace.SpanFromContext(ctx).SpanContext()
headers := []string{}
if !sc.TraceID().IsValid() || !sc.SpanID().IsValid() {
return
}
headers = append(headers, sc.TraceID().String(), sc.SpanID().String(), deprecatedParentSpanID)
if debugFromContext(ctx) {
headers = append(headers, fmt.Sprintf("%x", flagsDebug|flagsSampled))
} else if sc.IsSampled() {
headers = append(headers, fmt.Sprintf("%x", flagsSampled))
} else {
headers = append(headers, fmt.Sprintf("%x", flagsNotSampled))
}
carrier.Set(jaegerHeader, strings.Join(headers, separator))
}
// Extract extracts a context from the carrier if it contains Jaeger headers.
func (jaeger Jaeger) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context {
// extract tracing information
if h := carrier.Get(jaegerHeader); h != "" {
ctx, sc, err := extract(ctx, h)
if err == nil && sc.IsValid() {
return trace.ContextWithRemoteSpanContext(ctx, sc)
}
}
return ctx
}
func extract(ctx context.Context, headerVal string) (context.Context, trace.SpanContext, error) {
var (
scc = trace.SpanContextConfig{}
err error
)
parts := strings.Split(headerVal, separator)
if len(parts) != 4 {
return ctx, empty, errMalformedTraceContextVal
}
// extract trace ID
if parts[0] != "" {
id := parts[0]
if len(id) > traceID128bitsWidth {
return ctx, empty, errInvalidTraceIDLength
}
// padding when length is less than 32
if len(id) < traceID128bitsWidth {
padCharCount := traceID128bitsWidth - len(id)
id = strings.Repeat(idPaddingChar, padCharCount) + id
}
scc.TraceID, err = trace.TraceIDFromHex(id)
if err != nil {
return ctx, empty, errMalformedTraceID
}
}
// extract span ID
if parts[1] != "" {
id := parts[1]
if len(id) > spanIDWidth {
return ctx, empty, errInvalidSpanIDLength
}
// padding when length is less than 16
if len(id) < spanIDWidth {
padCharCount := spanIDWidth - len(id)
id = strings.Repeat(idPaddingChar, padCharCount) + id
}
scc.SpanID, err = trace.SpanIDFromHex(id)
if err != nil {
return ctx, empty, errMalformedSpanID
}
}
// skip third part as it is deprecated
// extract flag
if parts[3] != "" {
flagStr := parts[3]
flag, err := strconv.ParseInt(flagStr, 16, 64)
if err != nil {
return ctx, empty, errMalformedFlag
}
if flag&flagsSampled == flagsSampled {
// if sample bit is set, we check if debug bit is also set
if flag&flagsDebug == flagsDebug {
scc.TraceFlags |= trace.FlagsSampled
ctx = withDebug(ctx, true)
} else {
scc.TraceFlags |= trace.FlagsSampled
}
}
// ignore other bit, including firehose since we don't have corresponding flag in trace context.
}
return ctx, trace.NewSpanContext(scc), nil
}
// Fields returns the Jaeger header key whose value is set with Inject.
func (jaeger Jaeger) Fields() []string {
return []string{jaegerHeader}
}