forked from opentracing/basictracer-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtracer.go
212 lines (193 loc) · 6.7 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
package basictracer
import (
"time"
opentracing "github.com/opentracing/opentracing-go"
)
// Options allows creating a customized Tracer via NewWithOptions. The object
// must not be updated when there is an active tracer using it.
type Options struct {
// ShouldSample is a function which is called when creating a new Span and
// determines whether that Span is sampled. The randomized TraceID is supplied
// to allow deterministic sampling decisions to be made across different nodes.
// For example,
//
// func(traceID int64) { return traceID % 64 == 0 }
//
// samples every 64th trace on average.
ShouldSample func(int64) bool
// TrimUnsampledSpans turns potentially expensive operations on unsampled
// Spans into no-ops. More precisely, tags, baggage items, and log events
// are silently discarded. If NewSpanEventListener is set, the callbacks
// will still fire in that case.
TrimUnsampledSpans bool
// Recorder receives Spans which have been finished.
Recorder SpanRecorder
// NewSpanEventListener can be used to enhance the tracer by effectively
// attaching external code to trace events. See NetTraceIntegrator for a
// practical example, and event.go for the list of possible events.
NewSpanEventListener func() func(SpanEvent)
// DebugAssertSingleGoroutine internally records the ID of the goroutine
// creating each Span and verifies that no operation is carried out on
// it on a different goroutine.
// Provided strictly for development purposes.
// Passing Spans between goroutine without proper synchronization often
// results in use-after-Finish() errors. For a simple example, consider the
// following pseudocode:
//
// func (s *Server) Handle(req http.Request) error {
// sp := s.StartSpan("server")
// defer sp.Finish()
// wait := s.queueProcessing(opentracing.ContextWithSpan(context.Background(), ctx), req)
// select {
// case resp := <-wait:
// return resp.Error
// case <-time.After(10*time.Second):
// sp.LogEvent("timed out waiting for processing")
// return ErrTimedOut
// }
// }
//
// This looks reasonable at first, but a request which spends more than ten
// seconds in the queue is abandoned by the main goroutine and its trace
// finished, leading to use-after-finish when the request is finally
// processed. Note also that even joining on to a finished Span via
// StartSpanWithOptions constitutes an illegal operation.
//
// Code bases which do not require (or decide they do not want) Spans to
// be passed across goroutine boundaries can run with this flag enabled in
// tests to increase their chances of spotting wrong-doers.
DebugAssertSingleGoroutine bool
// DebugAssertUseAfterFinish is provided strictly for development purposes.
// When set, it attempts to exacerbate issues emanating from use of Spans
// after calling Finish by running additional assertions.
DebugAssertUseAfterFinish bool
}
// DefaultOptions returns an Options object with a 1 in 64 sampling rate and
// all options disabled. A Recorder needs to be set manually before using the
// returned object with a Tracer.
func DefaultOptions() Options {
var opts Options
opts.ShouldSample = func(traceID int64) bool { return traceID%64 == 0 }
opts.NewSpanEventListener = func() func(SpanEvent) { return nil }
return opts
}
// NewWithOptions creates a customized Tracer.
func NewWithOptions(opts Options) opentracing.Tracer {
rval := &tracerImpl{Options: opts}
rval.textPropagator = &textMapPropagator{rval}
rval.binaryPropagator = &binaryPropagator{rval}
rval.accessorPropagator = &accessorPropagator{rval}
return rval
}
// New creates and returns a standard Tracer which defers completed Spans to
// `recorder`.
// Spans created by this Tracer support the ext.SamplingPriority tag: Setting
// ext.SamplingPriority causes the Span to be Sampled from that point on.
func New(recorder SpanRecorder) opentracing.Tracer {
opts := DefaultOptions()
opts.Recorder = recorder
return NewWithOptions(opts)
}
// Implements the `Tracer` interface.
type tracerImpl struct {
Options
textPropagator *textMapPropagator
binaryPropagator *binaryPropagator
accessorPropagator *accessorPropagator
}
func (t *tracerImpl) StartSpan(
operationName string,
) opentracing.Span {
return t.StartSpanWithOptions(
opentracing.StartSpanOptions{
OperationName: operationName,
})
}
func (t *tracerImpl) getSpan() *spanImpl {
sp := spanPool.Get().(*spanImpl)
sp.reset()
return sp
}
func (t *tracerImpl) StartSpanWithOptions(
opts opentracing.StartSpanOptions,
) opentracing.Span {
// Start time.
startTime := opts.StartTime
if startTime.IsZero() {
startTime = time.Now()
}
// Tags.
tags := opts.Tags
// Build the new span. This is the only allocation: We'll return this as
// a opentracing.Span.
sp := t.getSpan()
if opts.Parent == nil {
sp.raw.TraceID, sp.raw.SpanID = randomID2()
sp.raw.Sampled = t.ShouldSample(sp.raw.TraceID)
} else {
pr := opts.Parent.(*spanImpl)
sp.raw.TraceID = pr.raw.TraceID
sp.raw.SpanID = randomID()
sp.raw.ParentSpanID = pr.raw.SpanID
sp.raw.Sampled = pr.raw.Sampled
pr.Lock()
if l := len(pr.raw.Baggage); l > 0 {
sp.raw.Baggage = make(map[string]string, len(pr.raw.Baggage))
for k, v := range pr.raw.Baggage {
sp.raw.Baggage[k] = v
}
}
pr.Unlock()
}
return t.startSpanInternal(
sp,
opts.OperationName,
startTime,
tags,
)
}
func (t *tracerImpl) startSpanInternal(
sp *spanImpl,
operationName string,
startTime time.Time,
tags opentracing.Tags,
) opentracing.Span {
sp.tracer = t
sp.event = t.NewSpanEventListener()
sp.raw.Operation = operationName
sp.raw.Start = startTime
sp.raw.Duration = -1
sp.raw.Tags = tags
if t.Options.DebugAssertSingleGoroutine {
sp.SetTag(debugGoroutineIDTag, curGoroutineID())
}
defer sp.onCreate(operationName)
return sp
}
type delegatorType struct{}
// Delegator is the format to use for DelegatingCarrier.
var Delegator delegatorType
func (t *tracerImpl) Inject(sp opentracing.Span, format interface{}, carrier interface{}) error {
switch format {
case opentracing.TextMap:
return t.textPropagator.Inject(sp, carrier)
case opentracing.Binary:
return t.binaryPropagator.Inject(sp, carrier)
}
if _, ok := format.(delegatorType); ok {
return t.accessorPropagator.Inject(sp, carrier)
}
return opentracing.ErrUnsupportedFormat
}
func (t *tracerImpl) Join(operationName string, format interface{}, carrier interface{}) (opentracing.Span, error) {
switch format {
case opentracing.TextMap:
return t.textPropagator.Join(operationName, carrier)
case opentracing.Binary:
return t.binaryPropagator.Join(operationName, carrier)
}
if _, ok := format.(delegatorType); ok {
return t.accessorPropagator.Join(operationName, carrier)
}
return nil, opentracing.ErrUnsupportedFormat
}