-
Notifications
You must be signed in to change notification settings - Fork 0
/
tracer.go
222 lines (193 loc) · 6.35 KB
/
tracer.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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
package migration
import (
"context"
"fmt"
"log"
octrace "go.opencensus.io/trace"
"go.opentelemetry.io/otel/api/global"
otel "go.opentelemetry.io/otel/api/trace"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/label"
)
// NewTracer returns an implementation of the OpenCensus Tracer interface which
// uses OpenTelemetry APIs. Using this implementation of Tracer "upgrades"
// libraries that use OpenCensus to OpenTelemetry to facilitate a migration.
func NewTracer() octrace.Tracer {
return &otelTracer{tracer: global.Tracer("ocmigration")}
}
type otelTracer struct {
tracer otel.Tracer
}
func (o *otelTracer) StartSpan(ctx context.Context, name string, s ...octrace.StartOption) (context.Context, octrace.Span) {
ctx, sp := o.tracer.Start(ctx, name, convertStartOptions(s, name)...)
return ctx, &span{otSpan: sp}
}
func convertStartOptions(optFns []octrace.StartOption, name string) []otel.SpanOption {
var ocOpts octrace.StartOptions
for _, fn := range optFns {
fn(&ocOpts)
}
otOpts := []otel.SpanOption{}
switch ocOpts.SpanKind {
case octrace.SpanKindClient:
otOpts = append(otOpts, otel.WithSpanKind(otel.SpanKindClient))
case octrace.SpanKindServer:
otOpts = append(otOpts, otel.WithSpanKind(otel.SpanKindServer))
case octrace.SpanKindUnspecified:
otOpts = append(otOpts, otel.WithSpanKind(otel.SpanKindUnspecified))
}
if ocOpts.Sampler != nil {
// OTel doesn't allow setting a sampler in SpanOptions
log.Printf("Ignoring custom sampler for span %q in OpenCensus -> OpenTelemetry migration sdk.\n", name)
}
return otOpts
}
func (o *otelTracer) StartSpanWithRemoteParent(ctx context.Context, name string, parent octrace.SpanContext, s ...octrace.StartOption) (context.Context, octrace.Span) {
// make sure span context is zero'd out so we use the remote parent
ctx = otel.ContextWithSpan(ctx, nil)
ctx = otel.ContextWithRemoteSpanContext(ctx, ocSpanContextToOtel(parent))
return o.StartSpan(ctx, name, s...)
}
func (o *otelTracer) FromContext(ctx context.Context) octrace.Span {
otSpan := otel.SpanFromContext(ctx)
return &span{otSpan: otSpan}
}
func (o *otelTracer) NewContext(parent context.Context, s octrace.Span) context.Context {
if otSpan, ok := s.(*span); ok {
return otel.ContextWithSpan(parent, otSpan.otSpan)
}
// The user must have created the octrace Span using a different tracer, and we don't know how to store it.
log.Printf("Unable to create context with span %q, since it was created using a different tracer.\n", s.String())
return parent
}
type span struct {
// We can't implement the unexported functions, so add the interface here.
octrace.Span
otSpan otel.Span
accumulatedLinks []otel.Link
}
func (s *span) IsRecordingEvents() bool {
return s.otSpan.IsRecording()
}
func (s *span) End() {
s.otSpan.End(otel.WithLinks(s.accumulatedLinks...))
}
func (s *span) SpanContext() octrace.SpanContext {
return otelSpanContextToOc(s.otSpan.SpanContext())
}
func (s *span) SetName(name string) {
s.otSpan.SetName(name)
}
func (s *span) SetStatus(status octrace.Status) {
s.otSpan.SetStatus(codes.Code(status.Code), status.Message)
}
func (s *span) AddAttributes(attributes ...octrace.Attribute) {
s.otSpan.SetAttributes(convertAttributes(attributes)...)
}
func convertAttributes(attributes []octrace.Attribute) []label.KeyValue {
otAttributes := make([]label.KeyValue, len(attributes))
for i, a := range attributes {
otAttributes[i] = label.KeyValue{
Key: label.Key(a.Key()),
Value: convertValue(a.Value()),
}
}
return otAttributes
}
func convertValue(ocval interface{}) label.Value {
switch v := ocval.(type) {
case bool:
return label.BoolValue(v)
case int64:
return label.Int64Value(v)
case float64:
return label.Float64Value(v)
case string:
return label.StringValue(v)
default:
return label.StringValue("unknown")
}
}
func (s *span) Annotate(attributes []octrace.Attribute, str string) {
s.otSpan.AddEvent(context.Background(), str, convertAttributes(attributes)...)
}
func (s *span) Annotatef(attributes []octrace.Attribute, format string, a ...interface{}) {
s.Annotate(attributes, fmt.Sprintf(format, a...))
}
var (
uncompressedKey = label.Key("uncompressed byte size")
compressedKey = label.Key("compressed byte size")
)
func (s *span) AddMessageSendEvent(messageID, uncompressedByteSize, compressedByteSize int64) {
s.otSpan.AddEvent(context.Background(), "message send",
label.KeyValue{
Key: uncompressedKey,
Value: label.Int64Value(uncompressedByteSize),
},
label.KeyValue{
Key: compressedKey,
Value: label.Int64Value(compressedByteSize),
})
}
func (s *span) AddMessageReceiveEvent(messageID, uncompressedByteSize, compressedByteSize int64) {
s.otSpan.AddEvent(context.Background(), "message receive",
label.KeyValue{
Key: uncompressedKey,
Value: label.Int64Value(uncompressedByteSize),
},
label.KeyValue{
Key: compressedKey,
Value: label.Int64Value(compressedByteSize),
})
}
func (s *span) AddLink(l octrace.Link) {
otLink := otel.Link{
SpanContext: otel.SpanContext{
TraceID: otel.ID(l.TraceID),
SpanID: otel.SpanID(l.SpanID),
},
Attributes: convertAttributeMap(l.Attributes),
}
s.accumulatedLinks = append(s.accumulatedLinks, otLink)
}
func convertAttributeMap(attributes map[string]interface{}) []label.KeyValue {
otAttributes := []label.KeyValue{}
for k, v := range attributes {
otAttributes = append(otAttributes, label.KeyValue{
Key: label.Key(k),
Value: convertValue(v),
})
}
return otAttributes
}
func (s *span) String() string {
return fmt.Sprintf("span %s",
s.otSpan.SpanContext().SpanID.String())
}
func otelSpanContextToOc(sc otel.SpanContext) octrace.SpanContext {
if sc.IsDebug() || sc.IsDeferred() {
// OTel don't support these options
log.Printf("Ignoring OpenCensus Debug or Deferred trace flags for span %q because they are not supported by OpenTelemetry.\n", sc.SpanID)
}
var to octrace.TraceOptions
if sc.IsSampled() {
// OpenCensus doesn't expose functions to directly set sampled
to = 0x1
}
return octrace.SpanContext{
TraceID: octrace.TraceID(sc.TraceID),
SpanID: octrace.SpanID(sc.SpanID),
TraceOptions: to,
}
}
func ocSpanContextToOtel(sc octrace.SpanContext) otel.SpanContext {
var traceFlags byte
if sc.IsSampled() {
traceFlags = otel.FlagsSampled
}
return otel.SpanContext{
TraceID: otel.ID(sc.TraceID),
SpanID: otel.SpanID(sc.SpanID),
TraceFlags: traceFlags,
}
}