...

Source file src/go.opencensus.io/trace/trace.go

Documentation: go.opencensus.io/trace

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

View as plain text