mirror of https://github.com/k3s-io/k3s
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
598 lines
17 KiB
598 lines
17 KiB
// Copyright 2017, OpenCensus Authors |
|
// |
|
// Licensed under the Apache License, Version 2.0 (the "License"); |
|
// you may not use this file except in compliance with the License. |
|
// You may obtain a copy of the License at |
|
// |
|
// http://www.apache.org/licenses/LICENSE-2.0 |
|
// |
|
// Unless required by applicable law or agreed to in writing, software |
|
// distributed under the License is distributed on an "AS IS" BASIS, |
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
// See the License for the specific language governing permissions and |
|
// limitations under the License. |
|
|
|
package trace |
|
|
|
import ( |
|
"context" |
|
crand "crypto/rand" |
|
"encoding/binary" |
|
"fmt" |
|
"math/rand" |
|
"sync" |
|
"sync/atomic" |
|
"time" |
|
|
|
"go.opencensus.io/internal" |
|
"go.opencensus.io/trace/tracestate" |
|
) |
|
|
|
// Span represents a span of a trace. It has an associated SpanContext, and |
|
// stores data accumulated while the span is active. |
|
// |
|
// Ideally users should interact with Spans by calling the functions in this |
|
// package that take a Context parameter. |
|
type Span struct { |
|
// data contains information recorded about the span. |
|
// |
|
// It will be non-nil if we are exporting the span or recording events for it. |
|
// Otherwise, data is nil, and the Span is simply a carrier for the |
|
// SpanContext, so that the trace ID is propagated. |
|
data *SpanData |
|
mu sync.Mutex // protects the contents of *data (but not the pointer value.) |
|
spanContext SpanContext |
|
|
|
// lruAttributes are capped at configured limit. When the capacity is reached an oldest entry |
|
// is removed to create room for a new entry. |
|
lruAttributes *lruMap |
|
|
|
// annotations are stored in FIFO queue capped by configured limit. |
|
annotations *evictedQueue |
|
|
|
// messageEvents are stored in FIFO queue capped by configured limit. |
|
messageEvents *evictedQueue |
|
|
|
// links are stored in FIFO queue capped by configured limit. |
|
links *evictedQueue |
|
|
|
// spanStore is the spanStore this span belongs to, if any, otherwise it is nil. |
|
*spanStore |
|
endOnce sync.Once |
|
|
|
executionTracerTaskEnd func() // ends the execution tracer span |
|
} |
|
|
|
// IsRecordingEvents returns true if events are being recorded for this span. |
|
// Use this check to avoid computing expensive annotations when they will never |
|
// be used. |
|
func (s *Span) IsRecordingEvents() bool { |
|
if s == nil { |
|
return false |
|
} |
|
return s.data != nil |
|
} |
|
|
|
// TraceOptions contains options associated with a trace span. |
|
type TraceOptions uint32 |
|
|
|
// IsSampled returns true if the span will be exported. |
|
func (sc SpanContext) IsSampled() bool { |
|
return sc.TraceOptions.IsSampled() |
|
} |
|
|
|
// setIsSampled sets the TraceOptions bit that determines whether the span will be exported. |
|
func (sc *SpanContext) setIsSampled(sampled bool) { |
|
if sampled { |
|
sc.TraceOptions |= 1 |
|
} else { |
|
sc.TraceOptions &= ^TraceOptions(1) |
|
} |
|
} |
|
|
|
// IsSampled returns true if the span will be exported. |
|
func (t TraceOptions) IsSampled() bool { |
|
return t&1 == 1 |
|
} |
|
|
|
// SpanContext contains the state that must propagate across process boundaries. |
|
// |
|
// SpanContext is not an implementation of context.Context. |
|
// TODO: add reference to external Census docs for SpanContext. |
|
type SpanContext struct { |
|
TraceID TraceID |
|
SpanID SpanID |
|
TraceOptions TraceOptions |
|
Tracestate *tracestate.Tracestate |
|
} |
|
|
|
type contextKey struct{} |
|
|
|
// FromContext returns the Span stored in a context, or nil if there isn't one. |
|
func FromContext(ctx context.Context) *Span { |
|
s, _ := ctx.Value(contextKey{}).(*Span) |
|
return s |
|
} |
|
|
|
// NewContext returns a new context with the given Span attached. |
|
func NewContext(parent context.Context, s *Span) context.Context { |
|
return context.WithValue(parent, contextKey{}, s) |
|
} |
|
|
|
// All available span kinds. Span kind must be either one of these values. |
|
const ( |
|
SpanKindUnspecified = iota |
|
SpanKindServer |
|
SpanKindClient |
|
) |
|
|
|
// StartOptions contains options concerning how a span is started. |
|
type StartOptions struct { |
|
// Sampler to consult for this Span. If provided, it is always consulted. |
|
// |
|
// If not provided, then the behavior differs based on whether |
|
// the parent of this Span is remote, local, or there is no parent. |
|
// In the case of a remote parent or no parent, the |
|
// default sampler (see Config) will be consulted. Otherwise, |
|
// when there is a non-remote parent, no new sampling decision will be made: |
|
// we will preserve the sampling of the parent. |
|
Sampler Sampler |
|
|
|
// SpanKind represents the kind of a span. If none is set, |
|
// SpanKindUnspecified is used. |
|
SpanKind int |
|
} |
|
|
|
// StartOption apply changes to StartOptions. |
|
type StartOption func(*StartOptions) |
|
|
|
// WithSpanKind makes new spans to be created with the given kind. |
|
func WithSpanKind(spanKind int) StartOption { |
|
return func(o *StartOptions) { |
|
o.SpanKind = spanKind |
|
} |
|
} |
|
|
|
// WithSampler makes new spans to be be created with a custom sampler. |
|
// Otherwise, the global sampler is used. |
|
func WithSampler(sampler Sampler) StartOption { |
|
return func(o *StartOptions) { |
|
o.Sampler = sampler |
|
} |
|
} |
|
|
|
// StartSpan starts a new child span of the current span in the context. If |
|
// there is no span in the context, creates a new trace and span. |
|
// |
|
// Returned context contains the newly created span. You can use it to |
|
// propagate the returned span in process. |
|
func StartSpan(ctx context.Context, name string, o ...StartOption) (context.Context, *Span) { |
|
var opts StartOptions |
|
var parent SpanContext |
|
if p := FromContext(ctx); p != nil { |
|
p.addChild() |
|
parent = p.spanContext |
|
} |
|
for _, op := range o { |
|
op(&opts) |
|
} |
|
span := startSpanInternal(name, parent != SpanContext{}, parent, false, opts) |
|
|
|
ctx, end := startExecutionTracerTask(ctx, name) |
|
span.executionTracerTaskEnd = end |
|
return NewContext(ctx, span), span |
|
} |
|
|
|
// StartSpanWithRemoteParent starts a new child span of the span from the given parent. |
|
// |
|
// If the incoming context contains a parent, it ignores. StartSpanWithRemoteParent is |
|
// preferred for cases where the parent is propagated via an incoming request. |
|
// |
|
// Returned context contains the newly created span. You can use it to |
|
// propagate the returned span in process. |
|
func StartSpanWithRemoteParent(ctx context.Context, name string, parent SpanContext, o ...StartOption) (context.Context, *Span) { |
|
var opts StartOptions |
|
for _, op := range o { |
|
op(&opts) |
|
} |
|
span := startSpanInternal(name, parent != SpanContext{}, parent, true, opts) |
|
ctx, end := startExecutionTracerTask(ctx, name) |
|
span.executionTracerTaskEnd = end |
|
return NewContext(ctx, span), span |
|
} |
|
|
|
func startSpanInternal(name string, hasParent bool, parent SpanContext, remoteParent bool, o StartOptions) *Span { |
|
span := &Span{} |
|
span.spanContext = parent |
|
|
|
cfg := config.Load().(*Config) |
|
|
|
if !hasParent { |
|
span.spanContext.TraceID = cfg.IDGenerator.NewTraceID() |
|
} |
|
span.spanContext.SpanID = cfg.IDGenerator.NewSpanID() |
|
sampler := cfg.DefaultSampler |
|
|
|
if !hasParent || remoteParent || o.Sampler != nil { |
|
// If this span is the child of a local span and no Sampler is set in the |
|
// options, keep the parent's TraceOptions. |
|
// |
|
// Otherwise, consult the Sampler in the options if it is non-nil, otherwise |
|
// the default sampler. |
|
if o.Sampler != nil { |
|
sampler = o.Sampler |
|
} |
|
span.spanContext.setIsSampled(sampler(SamplingParameters{ |
|
ParentContext: parent, |
|
TraceID: span.spanContext.TraceID, |
|
SpanID: span.spanContext.SpanID, |
|
Name: name, |
|
HasRemoteParent: remoteParent}).Sample) |
|
} |
|
|
|
if !internal.LocalSpanStoreEnabled && !span.spanContext.IsSampled() { |
|
return span |
|
} |
|
|
|
span.data = &SpanData{ |
|
SpanContext: span.spanContext, |
|
StartTime: time.Now(), |
|
SpanKind: o.SpanKind, |
|
Name: name, |
|
HasRemoteParent: remoteParent, |
|
} |
|
span.lruAttributes = newLruMap(cfg.MaxAttributesPerSpan) |
|
span.annotations = newEvictedQueue(cfg.MaxAnnotationEventsPerSpan) |
|
span.messageEvents = newEvictedQueue(cfg.MaxMessageEventsPerSpan) |
|
span.links = newEvictedQueue(cfg.MaxLinksPerSpan) |
|
|
|
if hasParent { |
|
span.data.ParentSpanID = parent.SpanID |
|
} |
|
if internal.LocalSpanStoreEnabled { |
|
var ss *spanStore |
|
ss = spanStoreForNameCreateIfNew(name) |
|
if ss != nil { |
|
span.spanStore = ss |
|
ss.add(span) |
|
} |
|
} |
|
|
|
return span |
|
} |
|
|
|
// End ends the span. |
|
func (s *Span) End() { |
|
if s == nil { |
|
return |
|
} |
|
if s.executionTracerTaskEnd != nil { |
|
s.executionTracerTaskEnd() |
|
} |
|
if !s.IsRecordingEvents() { |
|
return |
|
} |
|
s.endOnce.Do(func() { |
|
exp, _ := exporters.Load().(exportersMap) |
|
mustExport := s.spanContext.IsSampled() && len(exp) > 0 |
|
if s.spanStore != nil || mustExport { |
|
sd := s.makeSpanData() |
|
sd.EndTime = internal.MonotonicEndTime(sd.StartTime) |
|
if s.spanStore != nil { |
|
s.spanStore.finished(s, sd) |
|
} |
|
if mustExport { |
|
for e := range exp { |
|
e.ExportSpan(sd) |
|
} |
|
} |
|
} |
|
}) |
|
} |
|
|
|
// makeSpanData produces a SpanData representing the current state of the Span. |
|
// It requires that s.data is non-nil. |
|
func (s *Span) makeSpanData() *SpanData { |
|
var sd SpanData |
|
s.mu.Lock() |
|
sd = *s.data |
|
if s.lruAttributes.simpleLruMap.Len() > 0 { |
|
sd.Attributes = s.lruAttributesToAttributeMap() |
|
sd.DroppedAttributeCount = s.lruAttributes.droppedCount |
|
} |
|
if len(s.annotations.queue) > 0 { |
|
sd.Annotations = s.interfaceArrayToAnnotationArray() |
|
sd.DroppedAnnotationCount = s.annotations.droppedCount |
|
} |
|
if len(s.messageEvents.queue) > 0 { |
|
sd.MessageEvents = s.interfaceArrayToMessageEventArray() |
|
sd.DroppedMessageEventCount = s.messageEvents.droppedCount |
|
} |
|
if len(s.links.queue) > 0 { |
|
sd.Links = s.interfaceArrayToLinksArray() |
|
sd.DroppedLinkCount = s.links.droppedCount |
|
} |
|
s.mu.Unlock() |
|
return &sd |
|
} |
|
|
|
// SpanContext returns the SpanContext of the span. |
|
func (s *Span) SpanContext() SpanContext { |
|
if s == nil { |
|
return SpanContext{} |
|
} |
|
return s.spanContext |
|
} |
|
|
|
// SetName sets the name of the span, if it is recording events. |
|
func (s *Span) SetName(name string) { |
|
if !s.IsRecordingEvents() { |
|
return |
|
} |
|
s.mu.Lock() |
|
s.data.Name = name |
|
s.mu.Unlock() |
|
} |
|
|
|
// SetStatus sets the status of the span, if it is recording events. |
|
func (s *Span) SetStatus(status Status) { |
|
if !s.IsRecordingEvents() { |
|
return |
|
} |
|
s.mu.Lock() |
|
s.data.Status = status |
|
s.mu.Unlock() |
|
} |
|
|
|
func (s *Span) interfaceArrayToLinksArray() []Link { |
|
linksArr := make([]Link, 0) |
|
for _, value := range s.links.queue { |
|
linksArr = append(linksArr, value.(Link)) |
|
} |
|
return linksArr |
|
} |
|
|
|
func (s *Span) interfaceArrayToMessageEventArray() []MessageEvent { |
|
messageEventArr := make([]MessageEvent, 0) |
|
for _, value := range s.messageEvents.queue { |
|
messageEventArr = append(messageEventArr, value.(MessageEvent)) |
|
} |
|
return messageEventArr |
|
} |
|
|
|
func (s *Span) interfaceArrayToAnnotationArray() []Annotation { |
|
annotationArr := make([]Annotation, 0) |
|
for _, value := range s.annotations.queue { |
|
annotationArr = append(annotationArr, value.(Annotation)) |
|
} |
|
return annotationArr |
|
} |
|
|
|
func (s *Span) lruAttributesToAttributeMap() map[string]interface{} { |
|
attributes := make(map[string]interface{}) |
|
for _, key := range s.lruAttributes.simpleLruMap.Keys() { |
|
value, ok := s.lruAttributes.simpleLruMap.Get(key) |
|
if ok { |
|
keyStr := key.(string) |
|
attributes[keyStr] = value |
|
} |
|
} |
|
return attributes |
|
} |
|
|
|
func (s *Span) copyToCappedAttributes(attributes []Attribute) { |
|
for _, a := range attributes { |
|
s.lruAttributes.add(a.key, a.value) |
|
} |
|
} |
|
|
|
func (s *Span) addChild() { |
|
if !s.IsRecordingEvents() { |
|
return |
|
} |
|
s.mu.Lock() |
|
s.data.ChildSpanCount++ |
|
s.mu.Unlock() |
|
} |
|
|
|
// AddAttributes sets attributes in the span. |
|
// |
|
// Existing attributes whose keys appear in the attributes parameter are overwritten. |
|
func (s *Span) AddAttributes(attributes ...Attribute) { |
|
if !s.IsRecordingEvents() { |
|
return |
|
} |
|
s.mu.Lock() |
|
s.copyToCappedAttributes(attributes) |
|
s.mu.Unlock() |
|
} |
|
|
|
// copyAttributes copies a slice of Attributes into a map. |
|
func copyAttributes(m map[string]interface{}, attributes []Attribute) { |
|
for _, a := range attributes { |
|
m[a.key] = a.value |
|
} |
|
} |
|
|
|
func (s *Span) lazyPrintfInternal(attributes []Attribute, format string, a ...interface{}) { |
|
now := time.Now() |
|
msg := fmt.Sprintf(format, a...) |
|
var m map[string]interface{} |
|
s.mu.Lock() |
|
if len(attributes) != 0 { |
|
m = make(map[string]interface{}) |
|
copyAttributes(m, attributes) |
|
} |
|
s.annotations.add(Annotation{ |
|
Time: now, |
|
Message: msg, |
|
Attributes: m, |
|
}) |
|
s.mu.Unlock() |
|
} |
|
|
|
func (s *Span) printStringInternal(attributes []Attribute, str string) { |
|
now := time.Now() |
|
var a map[string]interface{} |
|
s.mu.Lock() |
|
if len(attributes) != 0 { |
|
a = make(map[string]interface{}) |
|
copyAttributes(a, attributes) |
|
} |
|
s.annotations.add(Annotation{ |
|
Time: now, |
|
Message: str, |
|
Attributes: a, |
|
}) |
|
s.mu.Unlock() |
|
} |
|
|
|
// Annotate adds an annotation with attributes. |
|
// Attributes can be nil. |
|
func (s *Span) Annotate(attributes []Attribute, str string) { |
|
if !s.IsRecordingEvents() { |
|
return |
|
} |
|
s.printStringInternal(attributes, str) |
|
} |
|
|
|
// Annotatef adds an annotation with attributes. |
|
func (s *Span) Annotatef(attributes []Attribute, format string, a ...interface{}) { |
|
if !s.IsRecordingEvents() { |
|
return |
|
} |
|
s.lazyPrintfInternal(attributes, format, a...) |
|
} |
|
|
|
// AddMessageSendEvent adds a message send event to the span. |
|
// |
|
// messageID is an identifier for the message, which is recommended to be |
|
// unique in this span and the same between the send event and the receive |
|
// event (this allows to identify a message between the sender and receiver). |
|
// For example, this could be a sequence id. |
|
func (s *Span) AddMessageSendEvent(messageID, uncompressedByteSize, compressedByteSize int64) { |
|
if !s.IsRecordingEvents() { |
|
return |
|
} |
|
now := time.Now() |
|
s.mu.Lock() |
|
s.messageEvents.add(MessageEvent{ |
|
Time: now, |
|
EventType: MessageEventTypeSent, |
|
MessageID: messageID, |
|
UncompressedByteSize: uncompressedByteSize, |
|
CompressedByteSize: compressedByteSize, |
|
}) |
|
s.mu.Unlock() |
|
} |
|
|
|
// AddMessageReceiveEvent adds a message receive event to the span. |
|
// |
|
// messageID is an identifier for the message, which is recommended to be |
|
// unique in this span and the same between the send event and the receive |
|
// event (this allows to identify a message between the sender and receiver). |
|
// For example, this could be a sequence id. |
|
func (s *Span) AddMessageReceiveEvent(messageID, uncompressedByteSize, compressedByteSize int64) { |
|
if !s.IsRecordingEvents() { |
|
return |
|
} |
|
now := time.Now() |
|
s.mu.Lock() |
|
s.messageEvents.add(MessageEvent{ |
|
Time: now, |
|
EventType: MessageEventTypeRecv, |
|
MessageID: messageID, |
|
UncompressedByteSize: uncompressedByteSize, |
|
CompressedByteSize: compressedByteSize, |
|
}) |
|
s.mu.Unlock() |
|
} |
|
|
|
// AddLink adds a link to the span. |
|
func (s *Span) AddLink(l Link) { |
|
if !s.IsRecordingEvents() { |
|
return |
|
} |
|
s.mu.Lock() |
|
s.links.add(l) |
|
s.mu.Unlock() |
|
} |
|
|
|
func (s *Span) String() string { |
|
if s == nil { |
|
return "<nil>" |
|
} |
|
if s.data == nil { |
|
return fmt.Sprintf("span %s", s.spanContext.SpanID) |
|
} |
|
s.mu.Lock() |
|
str := fmt.Sprintf("span %s %q", s.spanContext.SpanID, s.data.Name) |
|
s.mu.Unlock() |
|
return str |
|
} |
|
|
|
var config atomic.Value // access atomically |
|
|
|
func init() { |
|
gen := &defaultIDGenerator{} |
|
// initialize traceID and spanID generators. |
|
var rngSeed int64 |
|
for _, p := range []interface{}{ |
|
&rngSeed, &gen.traceIDAdd, &gen.nextSpanID, &gen.spanIDInc, |
|
} { |
|
binary.Read(crand.Reader, binary.LittleEndian, p) |
|
} |
|
gen.traceIDRand = rand.New(rand.NewSource(rngSeed)) |
|
gen.spanIDInc |= 1 |
|
|
|
config.Store(&Config{ |
|
DefaultSampler: ProbabilitySampler(defaultSamplingProbability), |
|
IDGenerator: gen, |
|
MaxAttributesPerSpan: DefaultMaxAttributesPerSpan, |
|
MaxAnnotationEventsPerSpan: DefaultMaxAnnotationEventsPerSpan, |
|
MaxMessageEventsPerSpan: DefaultMaxMessageEventsPerSpan, |
|
MaxLinksPerSpan: DefaultMaxLinksPerSpan, |
|
}) |
|
} |
|
|
|
type defaultIDGenerator struct { |
|
sync.Mutex |
|
|
|
// Please keep these as the first fields |
|
// so that these 8 byte fields will be aligned on addresses |
|
// divisible by 8, on both 32-bit and 64-bit machines when |
|
// performing atomic increments and accesses. |
|
// See: |
|
// * https://github.com/census-instrumentation/opencensus-go/issues/587 |
|
// * https://github.com/census-instrumentation/opencensus-go/issues/865 |
|
// * https://golang.org/pkg/sync/atomic/#pkg-note-BUG |
|
nextSpanID uint64 |
|
spanIDInc uint64 |
|
|
|
traceIDAdd [2]uint64 |
|
traceIDRand *rand.Rand |
|
} |
|
|
|
// NewSpanID returns a non-zero span ID from a randomly-chosen sequence. |
|
func (gen *defaultIDGenerator) NewSpanID() [8]byte { |
|
var id uint64 |
|
for id == 0 { |
|
id = atomic.AddUint64(&gen.nextSpanID, gen.spanIDInc) |
|
} |
|
var sid [8]byte |
|
binary.LittleEndian.PutUint64(sid[:], id) |
|
return sid |
|
} |
|
|
|
// NewTraceID returns a non-zero trace ID from a randomly-chosen sequence. |
|
// mu should be held while this function is called. |
|
func (gen *defaultIDGenerator) NewTraceID() [16]byte { |
|
var tid [16]byte |
|
// Construct the trace ID from two outputs of traceIDRand, with a constant |
|
// added to each half for additional entropy. |
|
gen.Lock() |
|
binary.LittleEndian.PutUint64(tid[0:8], gen.traceIDRand.Uint64()+gen.traceIDAdd[0]) |
|
binary.LittleEndian.PutUint64(tid[8:16], gen.traceIDRand.Uint64()+gen.traceIDAdd[1]) |
|
gen.Unlock() |
|
return tid |
|
}
|
|
|