]>
Commit | Line | Data |
---|---|---|
107c1cdb ND |
1 | // Copyright 2017, OpenCensus Authors |
2 | // | |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | // you may not use this file except in compliance with the License. | |
5 | // You may obtain a copy of the License at | |
6 | // | |
7 | // http://www.apache.org/licenses/LICENSE-2.0 | |
8 | // | |
9 | // Unless required by applicable law or agreed to in writing, software | |
10 | // distributed under the License is distributed on an "AS IS" BASIS, | |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | // See the License for the specific language governing permissions and | |
13 | // limitations under the License. | |
14 | ||
15 | package trace | |
16 | ||
17 | import ( | |
18 | "context" | |
19 | crand "crypto/rand" | |
20 | "encoding/binary" | |
21 | "fmt" | |
22 | "math/rand" | |
23 | "sync" | |
24 | "sync/atomic" | |
25 | "time" | |
26 | ||
27 | "go.opencensus.io/internal" | |
28 | "go.opencensus.io/trace/tracestate" | |
29 | ) | |
30 | ||
31 | // Span represents a span of a trace. It has an associated SpanContext, and | |
32 | // stores data accumulated while the span is active. | |
33 | // | |
34 | // Ideally users should interact with Spans by calling the functions in this | |
35 | // package that take a Context parameter. | |
36 | type Span struct { | |
37 | // data contains information recorded about the span. | |
38 | // | |
39 | // It will be non-nil if we are exporting the span or recording events for it. | |
40 | // Otherwise, data is nil, and the Span is simply a carrier for the | |
41 | // SpanContext, so that the trace ID is propagated. | |
42 | data *SpanData | |
43 | mu sync.Mutex // protects the contents of *data (but not the pointer value.) | |
44 | spanContext SpanContext | |
45 | // spanStore is the spanStore this span belongs to, if any, otherwise it is nil. | |
46 | *spanStore | |
47 | endOnce sync.Once | |
48 | ||
49 | executionTracerTaskEnd func() // ends the execution tracer span | |
50 | } | |
51 | ||
52 | // IsRecordingEvents returns true if events are being recorded for this span. | |
53 | // Use this check to avoid computing expensive annotations when they will never | |
54 | // be used. | |
55 | func (s *Span) IsRecordingEvents() bool { | |
56 | if s == nil { | |
57 | return false | |
58 | } | |
59 | return s.data != nil | |
60 | } | |
61 | ||
62 | // TraceOptions contains options associated with a trace span. | |
63 | type TraceOptions uint32 | |
64 | ||
65 | // IsSampled returns true if the span will be exported. | |
66 | func (sc SpanContext) IsSampled() bool { | |
67 | return sc.TraceOptions.IsSampled() | |
68 | } | |
69 | ||
70 | // setIsSampled sets the TraceOptions bit that determines whether the span will be exported. | |
71 | func (sc *SpanContext) setIsSampled(sampled bool) { | |
72 | if sampled { | |
73 | sc.TraceOptions |= 1 | |
74 | } else { | |
75 | sc.TraceOptions &= ^TraceOptions(1) | |
76 | } | |
77 | } | |
78 | ||
79 | // IsSampled returns true if the span will be exported. | |
80 | func (t TraceOptions) IsSampled() bool { | |
81 | return t&1 == 1 | |
82 | } | |
83 | ||
84 | // SpanContext contains the state that must propagate across process boundaries. | |
85 | // | |
86 | // SpanContext is not an implementation of context.Context. | |
87 | // TODO: add reference to external Census docs for SpanContext. | |
88 | type SpanContext struct { | |
89 | TraceID TraceID | |
90 | SpanID SpanID | |
91 | TraceOptions TraceOptions | |
92 | Tracestate *tracestate.Tracestate | |
93 | } | |
94 | ||
95 | type contextKey struct{} | |
96 | ||
97 | // FromContext returns the Span stored in a context, or nil if there isn't one. | |
98 | func FromContext(ctx context.Context) *Span { | |
99 | s, _ := ctx.Value(contextKey{}).(*Span) | |
100 | return s | |
101 | } | |
102 | ||
103 | // NewContext returns a new context with the given Span attached. | |
104 | func NewContext(parent context.Context, s *Span) context.Context { | |
105 | return context.WithValue(parent, contextKey{}, s) | |
106 | } | |
107 | ||
108 | // All available span kinds. Span kind must be either one of these values. | |
109 | const ( | |
110 | SpanKindUnspecified = iota | |
111 | SpanKindServer | |
112 | SpanKindClient | |
113 | ) | |
114 | ||
115 | // StartOptions contains options concerning how a span is started. | |
116 | type StartOptions struct { | |
117 | // Sampler to consult for this Span. If provided, it is always consulted. | |
118 | // | |
119 | // If not provided, then the behavior differs based on whether | |
120 | // the parent of this Span is remote, local, or there is no parent. | |
121 | // In the case of a remote parent or no parent, the | |
122 | // default sampler (see Config) will be consulted. Otherwise, | |
123 | // when there is a non-remote parent, no new sampling decision will be made: | |
124 | // we will preserve the sampling of the parent. | |
125 | Sampler Sampler | |
126 | ||
127 | // SpanKind represents the kind of a span. If none is set, | |
128 | // SpanKindUnspecified is used. | |
129 | SpanKind int | |
130 | } | |
131 | ||
132 | // StartOption apply changes to StartOptions. | |
133 | type StartOption func(*StartOptions) | |
134 | ||
135 | // WithSpanKind makes new spans to be created with the given kind. | |
136 | func WithSpanKind(spanKind int) StartOption { | |
137 | return func(o *StartOptions) { | |
138 | o.SpanKind = spanKind | |
139 | } | |
140 | } | |
141 | ||
142 | // WithSampler makes new spans to be be created with a custom sampler. | |
143 | // Otherwise, the global sampler is used. | |
144 | func WithSampler(sampler Sampler) StartOption { | |
145 | return func(o *StartOptions) { | |
146 | o.Sampler = sampler | |
147 | } | |
148 | } | |
149 | ||
150 | // StartSpan starts a new child span of the current span in the context. If | |
151 | // there is no span in the context, creates a new trace and span. | |
152 | // | |
153 | // Returned context contains the newly created span. You can use it to | |
154 | // propagate the returned span in process. | |
155 | func StartSpan(ctx context.Context, name string, o ...StartOption) (context.Context, *Span) { | |
156 | var opts StartOptions | |
157 | var parent SpanContext | |
158 | if p := FromContext(ctx); p != nil { | |
159 | parent = p.spanContext | |
160 | } | |
161 | for _, op := range o { | |
162 | op(&opts) | |
163 | } | |
164 | span := startSpanInternal(name, parent != SpanContext{}, parent, false, opts) | |
165 | ||
166 | ctx, end := startExecutionTracerTask(ctx, name) | |
167 | span.executionTracerTaskEnd = end | |
168 | return NewContext(ctx, span), span | |
169 | } | |
170 | ||
171 | // StartSpanWithRemoteParent starts a new child span of the span from the given parent. | |
172 | // | |
173 | // If the incoming context contains a parent, it ignores. StartSpanWithRemoteParent is | |
174 | // preferred for cases where the parent is propagated via an incoming request. | |
175 | // | |
176 | // Returned context contains the newly created span. You can use it to | |
177 | // propagate the returned span in process. | |
178 | func StartSpanWithRemoteParent(ctx context.Context, name string, parent SpanContext, o ...StartOption) (context.Context, *Span) { | |
179 | var opts StartOptions | |
180 | for _, op := range o { | |
181 | op(&opts) | |
182 | } | |
183 | span := startSpanInternal(name, parent != SpanContext{}, parent, true, opts) | |
184 | ctx, end := startExecutionTracerTask(ctx, name) | |
185 | span.executionTracerTaskEnd = end | |
186 | return NewContext(ctx, span), span | |
187 | } | |
188 | ||
189 | func startSpanInternal(name string, hasParent bool, parent SpanContext, remoteParent bool, o StartOptions) *Span { | |
190 | span := &Span{} | |
191 | span.spanContext = parent | |
192 | ||
193 | cfg := config.Load().(*Config) | |
194 | ||
195 | if !hasParent { | |
196 | span.spanContext.TraceID = cfg.IDGenerator.NewTraceID() | |
197 | } | |
198 | span.spanContext.SpanID = cfg.IDGenerator.NewSpanID() | |
199 | sampler := cfg.DefaultSampler | |
200 | ||
201 | if !hasParent || remoteParent || o.Sampler != nil { | |
202 | // If this span is the child of a local span and no Sampler is set in the | |
203 | // options, keep the parent's TraceOptions. | |
204 | // | |
205 | // Otherwise, consult the Sampler in the options if it is non-nil, otherwise | |
206 | // the default sampler. | |
207 | if o.Sampler != nil { | |
208 | sampler = o.Sampler | |
209 | } | |
210 | span.spanContext.setIsSampled(sampler(SamplingParameters{ | |
211 | ParentContext: parent, | |
212 | TraceID: span.spanContext.TraceID, | |
213 | SpanID: span.spanContext.SpanID, | |
214 | Name: name, | |
215 | HasRemoteParent: remoteParent}).Sample) | |
216 | } | |
217 | ||
218 | if !internal.LocalSpanStoreEnabled && !span.spanContext.IsSampled() { | |
219 | return span | |
220 | } | |
221 | ||
222 | span.data = &SpanData{ | |
223 | SpanContext: span.spanContext, | |
224 | StartTime: time.Now(), | |
225 | SpanKind: o.SpanKind, | |
226 | Name: name, | |
227 | HasRemoteParent: remoteParent, | |
228 | } | |
229 | if hasParent { | |
230 | span.data.ParentSpanID = parent.SpanID | |
231 | } | |
232 | if internal.LocalSpanStoreEnabled { | |
233 | var ss *spanStore | |
234 | ss = spanStoreForNameCreateIfNew(name) | |
235 | if ss != nil { | |
236 | span.spanStore = ss | |
237 | ss.add(span) | |
238 | } | |
239 | } | |
240 | ||
241 | return span | |
242 | } | |
243 | ||
244 | // End ends the span. | |
245 | func (s *Span) End() { | |
246 | if s == nil { | |
247 | return | |
248 | } | |
249 | if s.executionTracerTaskEnd != nil { | |
250 | s.executionTracerTaskEnd() | |
251 | } | |
252 | if !s.IsRecordingEvents() { | |
253 | return | |
254 | } | |
255 | s.endOnce.Do(func() { | |
256 | exp, _ := exporters.Load().(exportersMap) | |
257 | mustExport := s.spanContext.IsSampled() && len(exp) > 0 | |
258 | if s.spanStore != nil || mustExport { | |
259 | sd := s.makeSpanData() | |
260 | sd.EndTime = internal.MonotonicEndTime(sd.StartTime) | |
261 | if s.spanStore != nil { | |
262 | s.spanStore.finished(s, sd) | |
263 | } | |
264 | if mustExport { | |
265 | for e := range exp { | |
266 | e.ExportSpan(sd) | |
267 | } | |
268 | } | |
269 | } | |
270 | }) | |
271 | } | |
272 | ||
273 | // makeSpanData produces a SpanData representing the current state of the Span. | |
274 | // It requires that s.data is non-nil. | |
275 | func (s *Span) makeSpanData() *SpanData { | |
276 | var sd SpanData | |
277 | s.mu.Lock() | |
278 | sd = *s.data | |
279 | if s.data.Attributes != nil { | |
280 | sd.Attributes = make(map[string]interface{}) | |
281 | for k, v := range s.data.Attributes { | |
282 | sd.Attributes[k] = v | |
283 | } | |
284 | } | |
285 | s.mu.Unlock() | |
286 | return &sd | |
287 | } | |
288 | ||
289 | // SpanContext returns the SpanContext of the span. | |
290 | func (s *Span) SpanContext() SpanContext { | |
291 | if s == nil { | |
292 | return SpanContext{} | |
293 | } | |
294 | return s.spanContext | |
295 | } | |
296 | ||
297 | // SetName sets the name of the span, if it is recording events. | |
298 | func (s *Span) SetName(name string) { | |
299 | if !s.IsRecordingEvents() { | |
300 | return | |
301 | } | |
302 | s.mu.Lock() | |
303 | s.data.Name = name | |
304 | s.mu.Unlock() | |
305 | } | |
306 | ||
307 | // SetStatus sets the status of the span, if it is recording events. | |
308 | func (s *Span) SetStatus(status Status) { | |
309 | if !s.IsRecordingEvents() { | |
310 | return | |
311 | } | |
312 | s.mu.Lock() | |
313 | s.data.Status = status | |
314 | s.mu.Unlock() | |
315 | } | |
316 | ||
317 | // AddAttributes sets attributes in the span. | |
318 | // | |
319 | // Existing attributes whose keys appear in the attributes parameter are overwritten. | |
320 | func (s *Span) AddAttributes(attributes ...Attribute) { | |
321 | if !s.IsRecordingEvents() { | |
322 | return | |
323 | } | |
324 | s.mu.Lock() | |
325 | if s.data.Attributes == nil { | |
326 | s.data.Attributes = make(map[string]interface{}) | |
327 | } | |
328 | copyAttributes(s.data.Attributes, attributes) | |
329 | s.mu.Unlock() | |
330 | } | |
331 | ||
332 | // copyAttributes copies a slice of Attributes into a map. | |
333 | func copyAttributes(m map[string]interface{}, attributes []Attribute) { | |
334 | for _, a := range attributes { | |
335 | m[a.key] = a.value | |
336 | } | |
337 | } | |
338 | ||
339 | func (s *Span) lazyPrintfInternal(attributes []Attribute, format string, a ...interface{}) { | |
340 | now := time.Now() | |
341 | msg := fmt.Sprintf(format, a...) | |
342 | var m map[string]interface{} | |
343 | s.mu.Lock() | |
344 | if len(attributes) != 0 { | |
345 | m = make(map[string]interface{}) | |
346 | copyAttributes(m, attributes) | |
347 | } | |
348 | s.data.Annotations = append(s.data.Annotations, Annotation{ | |
349 | Time: now, | |
350 | Message: msg, | |
351 | Attributes: m, | |
352 | }) | |
353 | s.mu.Unlock() | |
354 | } | |
355 | ||
356 | func (s *Span) printStringInternal(attributes []Attribute, str string) { | |
357 | now := time.Now() | |
358 | var a map[string]interface{} | |
359 | s.mu.Lock() | |
360 | if len(attributes) != 0 { | |
361 | a = make(map[string]interface{}) | |
362 | copyAttributes(a, attributes) | |
363 | } | |
364 | s.data.Annotations = append(s.data.Annotations, Annotation{ | |
365 | Time: now, | |
366 | Message: str, | |
367 | Attributes: a, | |
368 | }) | |
369 | s.mu.Unlock() | |
370 | } | |
371 | ||
372 | // Annotate adds an annotation with attributes. | |
373 | // Attributes can be nil. | |
374 | func (s *Span) Annotate(attributes []Attribute, str string) { | |
375 | if !s.IsRecordingEvents() { | |
376 | return | |
377 | } | |
378 | s.printStringInternal(attributes, str) | |
379 | } | |
380 | ||
381 | // Annotatef adds an annotation with attributes. | |
382 | func (s *Span) Annotatef(attributes []Attribute, format string, a ...interface{}) { | |
383 | if !s.IsRecordingEvents() { | |
384 | return | |
385 | } | |
386 | s.lazyPrintfInternal(attributes, format, a...) | |
387 | } | |
388 | ||
389 | // AddMessageSendEvent adds a message send event to the span. | |
390 | // | |
391 | // messageID is an identifier for the message, which is recommended to be | |
392 | // unique in this span and the same between the send event and the receive | |
393 | // event (this allows to identify a message between the sender and receiver). | |
394 | // For example, this could be a sequence id. | |
395 | func (s *Span) AddMessageSendEvent(messageID, uncompressedByteSize, compressedByteSize int64) { | |
396 | if !s.IsRecordingEvents() { | |
397 | return | |
398 | } | |
399 | now := time.Now() | |
400 | s.mu.Lock() | |
401 | s.data.MessageEvents = append(s.data.MessageEvents, MessageEvent{ | |
402 | Time: now, | |
403 | EventType: MessageEventTypeSent, | |
404 | MessageID: messageID, | |
405 | UncompressedByteSize: uncompressedByteSize, | |
406 | CompressedByteSize: compressedByteSize, | |
407 | }) | |
408 | s.mu.Unlock() | |
409 | } | |
410 | ||
411 | // AddMessageReceiveEvent adds a message receive event to the span. | |
412 | // | |
413 | // messageID is an identifier for the message, which is recommended to be | |
414 | // unique in this span and the same between the send event and the receive | |
415 | // event (this allows to identify a message between the sender and receiver). | |
416 | // For example, this could be a sequence id. | |
417 | func (s *Span) AddMessageReceiveEvent(messageID, uncompressedByteSize, compressedByteSize int64) { | |
418 | if !s.IsRecordingEvents() { | |
419 | return | |
420 | } | |
421 | now := time.Now() | |
422 | s.mu.Lock() | |
423 | s.data.MessageEvents = append(s.data.MessageEvents, MessageEvent{ | |
424 | Time: now, | |
425 | EventType: MessageEventTypeRecv, | |
426 | MessageID: messageID, | |
427 | UncompressedByteSize: uncompressedByteSize, | |
428 | CompressedByteSize: compressedByteSize, | |
429 | }) | |
430 | s.mu.Unlock() | |
431 | } | |
432 | ||
433 | // AddLink adds a link to the span. | |
434 | func (s *Span) AddLink(l Link) { | |
435 | if !s.IsRecordingEvents() { | |
436 | return | |
437 | } | |
438 | s.mu.Lock() | |
439 | s.data.Links = append(s.data.Links, l) | |
440 | s.mu.Unlock() | |
441 | } | |
442 | ||
443 | func (s *Span) String() string { | |
444 | if s == nil { | |
445 | return "<nil>" | |
446 | } | |
447 | if s.data == nil { | |
448 | return fmt.Sprintf("span %s", s.spanContext.SpanID) | |
449 | } | |
450 | s.mu.Lock() | |
451 | str := fmt.Sprintf("span %s %q", s.spanContext.SpanID, s.data.Name) | |
452 | s.mu.Unlock() | |
453 | return str | |
454 | } | |
455 | ||
456 | var config atomic.Value // access atomically | |
457 | ||
458 | func init() { | |
459 | gen := &defaultIDGenerator{} | |
460 | // initialize traceID and spanID generators. | |
461 | var rngSeed int64 | |
462 | for _, p := range []interface{}{ | |
463 | &rngSeed, &gen.traceIDAdd, &gen.nextSpanID, &gen.spanIDInc, | |
464 | } { | |
465 | binary.Read(crand.Reader, binary.LittleEndian, p) | |
466 | } | |
467 | gen.traceIDRand = rand.New(rand.NewSource(rngSeed)) | |
468 | gen.spanIDInc |= 1 | |
469 | ||
470 | config.Store(&Config{ | |
471 | DefaultSampler: ProbabilitySampler(defaultSamplingProbability), | |
472 | IDGenerator: gen, | |
473 | }) | |
474 | } | |
475 | ||
476 | type defaultIDGenerator struct { | |
477 | sync.Mutex | |
478 | ||
479 | // Please keep these as the first fields | |
480 | // so that these 8 byte fields will be aligned on addresses | |
481 | // divisible by 8, on both 32-bit and 64-bit machines when | |
482 | // performing atomic increments and accesses. | |
483 | // See: | |
484 | // * https://github.com/census-instrumentation/opencensus-go/issues/587 | |
485 | // * https://github.com/census-instrumentation/opencensus-go/issues/865 | |
486 | // * https://golang.org/pkg/sync/atomic/#pkg-note-BUG | |
487 | nextSpanID uint64 | |
488 | spanIDInc uint64 | |
489 | ||
490 | traceIDAdd [2]uint64 | |
491 | traceIDRand *rand.Rand | |
492 | } | |
493 | ||
494 | // NewSpanID returns a non-zero span ID from a randomly-chosen sequence. | |
495 | func (gen *defaultIDGenerator) NewSpanID() [8]byte { | |
496 | var id uint64 | |
497 | for id == 0 { | |
498 | id = atomic.AddUint64(&gen.nextSpanID, gen.spanIDInc) | |
499 | } | |
500 | var sid [8]byte | |
501 | binary.LittleEndian.PutUint64(sid[:], id) | |
502 | return sid | |
503 | } | |
504 | ||
505 | // NewTraceID returns a non-zero trace ID from a randomly-chosen sequence. | |
506 | // mu should be held while this function is called. | |
507 | func (gen *defaultIDGenerator) NewTraceID() [16]byte { | |
508 | var tid [16]byte | |
509 | // Construct the trace ID from two outputs of traceIDRand, with a constant | |
510 | // added to each half for additional entropy. | |
511 | gen.Lock() | |
512 | binary.LittleEndian.PutUint64(tid[0:8], gen.traceIDRand.Uint64()+gen.traceIDAdd[0]) | |
513 | binary.LittleEndian.PutUint64(tid[8:16], gen.traceIDRand.Uint64()+gen.traceIDAdd[1]) | |
514 | gen.Unlock() | |
515 | return tid | |
516 | } |