123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598 |
- // 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
- }
|