github.com/waldiirawan/apm-agent-go/v2@v2.2.2/span.go (about)

     1  // Licensed to Elasticsearch B.V. under one or more contributor
     2  // license agreements. See the NOTICE file distributed with
     3  // this work for additional information regarding copyright
     4  // ownership. Elasticsearch B.V. licenses this file to you under
     5  // the Apache License, Version 2.0 (the "License"); you may
     6  // not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing,
    12  // software distributed under the License is distributed on an
    13  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14  // KIND, either express or implied.  See the License for the
    15  // specific language governing permissions and limitations
    16  // under the License.
    17  
    18  package apm // import "github.com/waldiirawan/apm-agent-go/v2"
    19  
    20  import (
    21  	cryptorand "crypto/rand"
    22  	"encoding/binary"
    23  	"strings"
    24  	"sync"
    25  	"sync/atomic"
    26  	"time"
    27  
    28  	"github.com/waldiirawan/apm-agent-go/v2/stacktrace"
    29  )
    30  
    31  // droppedSpanDataPool holds *SpanData which are used when the span is created
    32  // for a nil or non-sampled trace context, without a transaction reference.
    33  //
    34  // Spans started with a non-nil transaction, even if it is non-sampled, are
    35  // always created with the transaction's tracer span pool.
    36  var droppedSpanDataPool sync.Pool
    37  
    38  // StartSpan starts and returns a new Span within the transaction,
    39  // with the specified name, type, and optional parent span, and
    40  // with the start time set to the current time.
    41  //
    42  // StartSpan always returns a non-nil Span, with a non-nil SpanData
    43  // field. Its End method must be called when the span completes.
    44  //
    45  // If the span type contains two dots, they are assumed to separate
    46  // the span type, subtype, and action; a single dot separates span
    47  // type and subtype, and the action will not be set.
    48  //
    49  // StartSpan is equivalent to calling StartSpanOptions with
    50  // SpanOptions.Parent set to the trace context of parent if
    51  // parent is non-nil.
    52  func (tx *Transaction) StartSpan(name, spanType string, parent *Span) *Span {
    53  	return tx.StartSpanOptions(name, spanType, SpanOptions{
    54  		parent: parent,
    55  	})
    56  }
    57  
    58  // StartExitSpan starts and returns a new Span within the transaction,
    59  // with the specified name, type, and optional parent span, and
    60  // with the start time set to the current time.
    61  //
    62  // StartExitSpan always returns a non-nil Span, with a non-nil SpanData
    63  // field. Its End method must be called when the span completes.
    64  //
    65  // If the span type contains two dots, they are assumed to separate
    66  // the span type, subtype, and action; a single dot separates span
    67  // type and subtype, and the action will not be set.
    68  //
    69  // StartExitSpan is equivalent to calling StartSpanOptions with
    70  // SpanOptions.Parent set to the trace context of parent if
    71  // parent is non-nil and the span being marked as an exit span.
    72  func (tx *Transaction) StartExitSpan(name, spanType string, parent *Span) *Span {
    73  	return tx.StartSpanOptions(name, spanType, SpanOptions{
    74  		parent:   parent,
    75  		ExitSpan: true,
    76  	})
    77  }
    78  
    79  // StartSpanOptions starts and returns a new Span within the transaction,
    80  // with the specified name, type, and options.
    81  //
    82  // StartSpan always returns a non-nil Span. Its End method must be called
    83  // when the span completes.
    84  //
    85  // If the span type contains two dots, they are assumed to separate the
    86  // span type, subtype, and action; a single dot separates span type and
    87  // subtype, and the action will not be set.
    88  func (tx *Transaction) StartSpanOptions(name, spanType string, opts SpanOptions) *Span {
    89  	if tx == nil {
    90  		return newDroppedSpan()
    91  	}
    92  
    93  	if opts.Parent == (TraceContext{}) {
    94  		if opts.parent != nil {
    95  			opts.Parent = opts.parent.TraceContext()
    96  		} else {
    97  			opts.Parent = tx.traceContext
    98  		}
    99  	}
   100  	transactionID := tx.traceContext.Span
   101  
   102  	// Lock the parent first to avoid deadlocks in breakdown metrics calculation.
   103  	if opts.parent != nil {
   104  		opts.parent.mu.Lock()
   105  		defer opts.parent.mu.Unlock()
   106  	}
   107  
   108  	// Prevent tx from being ended while we're starting a span.
   109  	tx.mu.RLock()
   110  	defer tx.mu.RUnlock()
   111  	if tx.ended() {
   112  		return tx.tracer.StartSpan(name, spanType, transactionID, opts)
   113  	}
   114  
   115  	// Calculate the span time relative to the transaction timestamp so
   116  	// that wall-clock adjustments occurring after the transaction start
   117  	// don't affect the span timestamp.
   118  	if opts.Start.IsZero() {
   119  		opts.Start = tx.timestamp.Add(time.Since(tx.timestamp))
   120  	} else {
   121  		opts.Start = tx.timestamp.Add(opts.Start.Sub(tx.timestamp))
   122  	}
   123  	span := tx.tracer.startSpan(name, spanType, transactionID, opts)
   124  	span.tx = tx
   125  	span.parent = opts.parent
   126  	if opts.ExitSpan {
   127  		span.exit = true
   128  	}
   129  
   130  	// Guard access to spansCreated, spansDropped, rand, and childrenTimer.
   131  	tx.TransactionData.mu.Lock()
   132  	defer tx.TransactionData.mu.Unlock()
   133  
   134  	notRecorded := !span.traceContext.Options.Recorded()
   135  	exceedsMaxSpans := tx.maxSpans >= 0 && tx.spansCreated >= tx.maxSpans
   136  	// Drop span when it is not recorded.
   137  	if span.dropWhen(notRecorded) {
   138  		// nothing to do here since it isn't recorded.
   139  	} else if span.dropWhen(exceedsMaxSpans) {
   140  		tx.spansDropped++
   141  	} else {
   142  		if opts.SpanID.Validate() == nil {
   143  			span.traceContext.Span = opts.SpanID
   144  		} else {
   145  			binary.LittleEndian.PutUint64(span.traceContext.Span[:], tx.rand.Uint64())
   146  		}
   147  		span.stackStackTraceMinDuration = tx.spanStackTraceMinDuration
   148  		span.stackTraceLimit = tx.stackTraceLimit
   149  		span.compressedSpan.options = tx.compressedSpan.options
   150  		span.exitSpanMinDuration = tx.exitSpanMinDuration
   151  		tx.spansCreated++
   152  	}
   153  
   154  	if tx.breakdownMetricsEnabled {
   155  		if span.parent != nil {
   156  			if !span.parent.ended() {
   157  				span.parent.childrenTimer.childStarted(span.timestamp)
   158  			}
   159  		} else {
   160  			tx.childrenTimer.childStarted(span.timestamp)
   161  		}
   162  	}
   163  	return span
   164  }
   165  
   166  // StartSpan returns a new Span with the specified name, type, transaction ID,
   167  // and options. The parent transaction context and transaction IDs must have
   168  // valid, non-zero values, or else the span will be dropped.
   169  //
   170  // In most cases, you should use Transaction.StartSpan or Transaction.StartSpanOptions.
   171  // This method is provided for corner-cases, such as starting a span after the
   172  // containing transaction's End method has been called. Spans created in this
   173  // way will not have the "max spans" configuration applied, nor will they be
   174  // considered in any transaction's span count.
   175  func (t *Tracer) StartSpan(name, spanType string, transactionID SpanID, opts SpanOptions) *Span {
   176  	if opts.Parent.Trace.Validate() != nil ||
   177  		opts.Parent.Span.Validate() != nil ||
   178  		transactionID.Validate() != nil ||
   179  		opts.parent.IsExitSpan() {
   180  		return newDroppedSpan()
   181  	}
   182  	if !opts.Parent.Options.Recorded() {
   183  		return newDroppedSpan()
   184  	}
   185  	var spanID SpanID
   186  	if opts.SpanID.Validate() == nil {
   187  		spanID = opts.SpanID
   188  	} else {
   189  		if _, err := cryptorand.Read(spanID[:]); err != nil {
   190  			return newDroppedSpan()
   191  		}
   192  	}
   193  	if opts.Start.IsZero() {
   194  		opts.Start = time.Now()
   195  	}
   196  	span := t.startSpan(name, spanType, transactionID, opts)
   197  	span.traceContext.Span = spanID
   198  
   199  	instrumentationConfig := t.instrumentationConfig()
   200  	span.stackStackTraceMinDuration = instrumentationConfig.spanStackTraceMinDuration
   201  	span.stackTraceLimit = instrumentationConfig.stackTraceLimit
   202  	span.compressedSpan.options = instrumentationConfig.compressionOptions
   203  	span.exitSpanMinDuration = instrumentationConfig.exitSpanMinDuration
   204  	if opts.ExitSpan {
   205  		span.exit = true
   206  	}
   207  
   208  	return span
   209  }
   210  
   211  // SpanOptions holds options for Transaction.StartSpanOptions and Tracer.StartSpan.
   212  type SpanOptions struct {
   213  	// Parent, if non-zero, holds the trace context of the parent span.
   214  	Parent TraceContext
   215  
   216  	// SpanID holds the ID to assign to the span. If this is zero, a new ID
   217  	// will be generated and used instead.
   218  	SpanID SpanID
   219  
   220  	// Indicates whether a span is an exit span or not. All child spans
   221  	// will be noop spans.
   222  	ExitSpan bool
   223  
   224  	// parent, if non-nil, holds the parent span.
   225  	//
   226  	// This is only used if Parent is zero, and is only available to internal
   227  	// callers of Transaction.StartSpanOptions.
   228  	parent *Span
   229  
   230  	// Start is the start time of the span. If this has the zero value,
   231  	// time.Now() will be used instead.
   232  	//
   233  	// When a span is created using Transaction.StartSpanOptions, the
   234  	// span timestamp is internally calculated relative to the transaction
   235  	// timestamp.
   236  	//
   237  	// When Tracer.StartSpan is used, this timestamp should be pre-calculated
   238  	// as relative from the transaction start time, i.e. by calculating the
   239  	// time elapsed since the transaction started, and adding that to the
   240  	// transaction timestamp. Calculating the timstamp in this way will ensure
   241  	// monotonicity of events within a transaction.
   242  	Start time.Time
   243  
   244  	// Links, if non-nil, holds a list of spans linked to the span.
   245  	Links []SpanLink
   246  }
   247  
   248  func (t *Tracer) startSpan(name, spanType string, transactionID SpanID, opts SpanOptions) *Span {
   249  	sd, _ := t.spanDataPool.Get().(*SpanData)
   250  	if sd == nil {
   251  		sd = &SpanData{Duration: -1}
   252  	}
   253  	span := &Span{tracer: t, SpanData: sd}
   254  	span.Name = name
   255  	span.traceContext = opts.Parent
   256  	span.parentID = opts.Parent.Span
   257  	span.transactionID = transactionID
   258  	span.timestamp = opts.Start
   259  	span.Type = spanType
   260  	span.links = opts.Links
   261  	if dot := strings.IndexRune(spanType, '.'); dot != -1 {
   262  		span.Type = spanType[:dot]
   263  		span.Subtype = spanType[dot+1:]
   264  		if dot := strings.IndexRune(span.Subtype, '.'); dot != -1 {
   265  			span.Subtype, span.Action = span.Subtype[:dot], span.Subtype[dot+1:]
   266  		}
   267  	}
   268  	return span
   269  }
   270  
   271  // newDropped returns a new Span with a non-nil SpanData.
   272  func newDroppedSpan() *Span {
   273  	span, _ := droppedSpanDataPool.Get().(*Span)
   274  	if span == nil {
   275  		span = &Span{SpanData: &SpanData{}}
   276  	}
   277  	return span
   278  }
   279  
   280  // Span describes an operation within a transaction.
   281  type Span struct {
   282  	tracer        *Tracer // nil if span is dropped
   283  	tx            *Transaction
   284  	parent        *Span
   285  	traceContext  TraceContext
   286  	transactionID SpanID
   287  	parentID      SpanID
   288  	exit          bool
   289  
   290  	// ctxPropagated is set to 1 when the traceContext is propagated downstream.
   291  	ctxPropagated uint32
   292  
   293  	mu sync.RWMutex
   294  
   295  	// SpanData holds the span data. This field is set to nil when
   296  	// the span's End method is called.
   297  	*SpanData
   298  
   299  	// finalType and finalSubtype are set to SpanData.Type and SpanData.Subtype
   300  	// respectively when the span is ended. This is necessary for filtering out
   301  	// child spans of exit spans that have non-matching type/subtype.
   302  	finalType    string
   303  	finalSubtype string
   304  }
   305  
   306  // TraceContext returns the span's TraceContext.
   307  func (s *Span) TraceContext() TraceContext {
   308  	if s == nil {
   309  		return TraceContext{}
   310  	}
   311  	atomic.StoreUint32(&s.ctxPropagated, 1)
   312  	return s.traceContext
   313  }
   314  
   315  // SetStacktrace sets the stacktrace for the span,
   316  // skipping the first skip number of frames,
   317  // excluding the SetStacktrace function.
   318  func (s *Span) SetStacktrace(skip int) {
   319  	if s == nil || s.dropped() {
   320  		return
   321  	}
   322  	s.mu.RLock()
   323  	defer s.mu.RUnlock()
   324  	if s.ended() {
   325  		return
   326  	}
   327  	s.SpanData.mu.Lock()
   328  	defer s.SpanData.mu.Unlock()
   329  	s.SpanData.setStacktrace(skip + 1)
   330  }
   331  
   332  // Dropped indicates whether or not the span is dropped, meaning it will not
   333  // be included in any transaction. Spans are dropped by Transaction.StartSpan
   334  // if the transaction is nil, non-sampled, or the transaction's max spans
   335  // limit has been reached.
   336  //
   337  // Dropped may be used to avoid any expensive computation required to set
   338  // the span's context.
   339  func (s *Span) Dropped() bool {
   340  	return s == nil || s.dropped()
   341  }
   342  
   343  func (s *Span) dropped() bool {
   344  	return s.tracer == nil
   345  }
   346  
   347  // dropWhen unsets the tracer when the passed bool cond is `true` and returns
   348  // `true` only when the span is dropped. If the span has already been dropped
   349  // or the condition isn't `true`, it then returns `false`.
   350  //
   351  // Must be called with s.mu.Lock held to be able to write to s.tracer.
   352  func (s *Span) dropWhen(cond bool) bool {
   353  	if s.Dropped() {
   354  		return false
   355  	}
   356  	if cond {
   357  		s.tracer = nil
   358  	}
   359  	return cond
   360  }
   361  
   362  // End marks the s as being complete; s must not be used after this.
   363  //
   364  // If s.Duration has not been set, End will set it to the elapsed time
   365  // since the span's start time.
   366  func (s *Span) End() {
   367  	s.mu.Lock()
   368  	defer s.mu.Unlock()
   369  	if s.ended() {
   370  		return
   371  	}
   372  	if s.Type == "" {
   373  		s.Type = "custom"
   374  	}
   375  	// Store the span type and subtype on the Span struct so we can filter
   376  	// out child spans of exit spans with non-matching type/subtype below.
   377  	s.finalType = s.Type
   378  	s.finalSubtype = s.Subtype
   379  
   380  	if s.parent.IsExitSpan() {
   381  		// Children of exit spans must not have service destination/target
   382  		// context, as otherwise service destination metrics will be double
   383  		// counted.
   384  		s.Context.model.Destination = nil
   385  		s.Context.model.Service = nil
   386  
   387  		var parentType, parentSubtype string
   388  		s.parent.mu.RLock()
   389  		if s.parent.ended() {
   390  			parentType = s.parent.finalType
   391  			parentSubtype = s.parent.finalSubtype
   392  		} else {
   393  			parentType = s.parent.Type
   394  			parentSubtype = s.parent.Subtype
   395  		}
   396  		s.parent.mu.RUnlock()
   397  
   398  		if s.Type != parentType || s.Subtype != parentSubtype {
   399  			s.dropWhen(true)
   400  			s.end()
   401  			return
   402  		}
   403  	}
   404  	if s.exit && !s.Context.setDestinationServiceCalled {
   405  		// The span was created as an exit span, but the user did not
   406  		// manually set the destination.service.resource
   407  		s.setExitSpanDestinationService()
   408  	}
   409  
   410  	s.updateSpanServiceTarget()
   411  
   412  	if s.Duration < 0 {
   413  		s.Duration = time.Since(s.timestamp)
   414  	}
   415  	if s.Outcome == "" {
   416  		s.Outcome = s.Context.outcome()
   417  		if s.Outcome == "" {
   418  			if s.errorCaptured {
   419  				s.Outcome = "failure"
   420  			} else {
   421  				s.Outcome = "success"
   422  			}
   423  		}
   424  	}
   425  	switch {
   426  	case s.stackStackTraceMinDuration < 0:
   427  		// If s.stackFramesMinDuration < 0, we never set stacktrace.
   428  	case s.stackStackTraceMinDuration == 0:
   429  		// Always set stacktrace
   430  		s.setStacktrace(1)
   431  	default:
   432  		if !s.dropped() && len(s.stacktrace) == 0 &&
   433  			s.Duration >= s.stackStackTraceMinDuration {
   434  			s.setStacktrace(1)
   435  		}
   436  	}
   437  	// If this span has a parent span, lock it before proceeding to
   438  	// prevent deadlocking when concurrently ending parent and child.
   439  	if s.parent != nil {
   440  		s.parent.mu.Lock()
   441  		defer s.parent.mu.Unlock()
   442  	}
   443  	if s.tx != nil {
   444  		s.tx.mu.RLock()
   445  		defer s.tx.mu.RUnlock()
   446  		if !s.tx.ended() {
   447  			s.tx.TransactionData.mu.Lock()
   448  			defer s.tx.TransactionData.mu.Unlock()
   449  			s.reportSelfTime()
   450  		}
   451  	}
   452  
   453  	evictedSpan, cached := s.attemptCompress()
   454  	if evictedSpan != nil {
   455  		evictedSpan.end()
   456  	}
   457  	if cached {
   458  		// s has been cached for potential compression, and will be enqueued
   459  		// by a future call to attemptCompress on a sibling span, or when the
   460  		// parent is ended.
   461  		return
   462  	}
   463  	s.end()
   464  }
   465  
   466  // end represents a subset of the public `s.End()` API  and will only attempt
   467  // to drop the span when it's a short exit span or enqueue it in case it's not.
   468  //
   469  // end must only be called with from `s.End()` and `tx.End()` with `s.mu`,
   470  // s.tx.mu.Rlock and s.tx.TransactionData.mu held.
   471  func (s *Span) end() {
   472  	// After an exit span finishes (no more compression attempts), we drop it
   473  	// when s.duration <= `exit_span_min_duration` and increment the tx dropped
   474  	// count.
   475  	s.dropFastExitSpan()
   476  
   477  	if s.dropped() {
   478  		if s.tx != nil {
   479  			if !s.tx.ended() {
   480  				s.aggregateDroppedSpanStats()
   481  			} else {
   482  				s.reset(s.tx.tracer)
   483  			}
   484  		} else {
   485  			droppedSpanDataPool.Put(s.SpanData)
   486  		}
   487  	} else {
   488  		s.enqueue()
   489  	}
   490  
   491  	s.SpanData = nil
   492  }
   493  
   494  // ParentID returns the ID of the span's parent span or transaction.
   495  func (s *Span) ParentID() SpanID {
   496  	if s == nil {
   497  		return SpanID{}
   498  	}
   499  	return s.parentID
   500  }
   501  
   502  // reportSelfTime reports the span's self-time to its transaction, and informs
   503  // the parent that it has ended in order for the parent to later calculate its
   504  // own self-time.
   505  //
   506  // This must only be called from Span.End, with s.mu.Lock held for writing and
   507  // s.Duration set.
   508  func (s *Span) reportSelfTime() {
   509  	endTime := s.timestamp.Add(s.Duration)
   510  
   511  	if s.tx.ended() || !s.tx.breakdownMetricsEnabled {
   512  		return
   513  	}
   514  
   515  	if s.parent != nil {
   516  		if !s.parent.ended() {
   517  			s.parent.childrenTimer.childEnded(endTime)
   518  		}
   519  	} else {
   520  		s.tx.childrenTimer.childEnded(endTime)
   521  	}
   522  	s.tx.spanTimings.add(s.Type, s.Subtype, s.Duration-s.childrenTimer.finalDuration(endTime))
   523  }
   524  
   525  func (s *Span) enqueue() {
   526  	event := tracerEvent{eventType: spanEvent}
   527  	event.span.Span = s
   528  	event.span.SpanData = s.SpanData
   529  	select {
   530  	case s.tracer.events <- event:
   531  	default:
   532  		// Enqueuing a span should never block.
   533  		s.tracer.stats.accumulate(TracerStats{SpansDropped: 1})
   534  		s.reset(s.tracer)
   535  	}
   536  }
   537  
   538  func (s *Span) ended() bool {
   539  	return s.SpanData == nil
   540  }
   541  
   542  func (s *Span) setExitSpanDestinationService() {
   543  	resource := s.Subtype
   544  	if resource == "" {
   545  		resource = s.Type
   546  	}
   547  	s.Context.SetDestinationService(DestinationServiceSpanContext{
   548  		Resource: resource,
   549  	})
   550  }
   551  
   552  func (s *Span) updateSpanServiceTarget() {
   553  	if !s.exit {
   554  		// span.context.service.target.* fields should be omitted for non-exit spans.
   555  		s.Context.model.Service = nil
   556  		return
   557  	}
   558  
   559  	fallbackType := s.Subtype
   560  	if fallbackType == "" {
   561  		fallbackType = s.Type
   562  	}
   563  
   564  	// Service target fields explicitly provided.
   565  	if s.Context.setServiceTargetCalled {
   566  		// if the user calls SetServiceTarget with a non-empty name, but empty type,
   567  		// we'll use the specified name and infer the type
   568  		if s.Context.serviceTarget.Type == "" && s.Context.serviceTarget.Name != "" {
   569  			s.Context.SetServiceTarget(ServiceTargetSpanContext{
   570  				Type: fallbackType,
   571  				Name: s.Context.serviceTarget.Name,
   572  			})
   573  		}
   574  		return
   575  	}
   576  
   577  	var fallbackName string
   578  	if s.Context.database.Type != "" { // database spans
   579  		fallbackName = s.Context.database.Instance
   580  	} else if s.Context.message.Queue != nil { // messaging spans
   581  		fallbackName = s.Context.message.Queue.Name
   582  	} else if s.Context.http.URL != nil { // http spans
   583  		fallbackName = s.Context.http.URL.Host
   584  	}
   585  
   586  	s.Context.SetServiceTarget(ServiceTargetSpanContext{
   587  		Type: fallbackType,
   588  		Name: fallbackName,
   589  	})
   590  }
   591  
   592  // IsExitSpan returns true if the span is an exit span.
   593  func (s *Span) IsExitSpan() bool {
   594  	if s == nil {
   595  		return false
   596  	}
   597  	return s.exit
   598  }
   599  
   600  // aggregateDroppedSpanStats aggregates the current span into the transaction
   601  // dropped spans stats timings.
   602  //
   603  // Must only be called from end() with s.tx.mu and s.tx.TransactionData.mu held.
   604  func (s *Span) aggregateDroppedSpanStats() {
   605  	// An exit span would have the destination service set but in any case, we
   606  	// check the field value before adding an entry to the dropped spans stats.
   607  	service := s.Context.destinationService.Resource
   608  	if s.dropped() && s.IsExitSpan() && service != "" {
   609  		count := 1
   610  		if !s.composite.empty() {
   611  			count = s.composite.count
   612  		}
   613  		s.tx.droppedSpansStats.add(s.Context.serviceTarget.Type, s.Context.serviceTarget.Name, service, s.Outcome, count, s.Duration)
   614  	}
   615  }
   616  
   617  // discardable returns whether or not the span can be dropped.
   618  //
   619  // It should be called with s.mu held.
   620  func (s *Span) discardable() bool {
   621  	return s.isCompressionEligible() && s.Duration < s.exitSpanMinDuration
   622  }
   623  
   624  // dropFastExitSpan drops an exit span that is discardable and increments the
   625  // s.tx.spansDropped. If the transaction is nil or has ended, the span will not
   626  // be dropped.
   627  //
   628  // Must be called with s.tx.TransactionData held.
   629  func (s *Span) dropFastExitSpan() {
   630  	if s.tx == nil || s.tx.ended() {
   631  		return
   632  	}
   633  	if !s.dropWhen(s.discardable()) {
   634  		return
   635  	}
   636  	if !s.tx.ended() {
   637  		s.tx.spansCreated--
   638  		s.tx.spansDropped++
   639  	}
   640  }
   641  
   642  // SpanData holds the details for a span, and is embedded inside Span.
   643  // When a span is ended or discarded, its SpanData field will be set
   644  // to nil.
   645  type SpanData struct {
   646  	exitSpanMinDuration        time.Duration
   647  	stackStackTraceMinDuration time.Duration
   648  	stackTraceLimit            int
   649  	timestamp                  time.Time
   650  	childrenTimer              childrenTimer
   651  	composite                  compositeSpan
   652  	compressedSpan             compressedSpan
   653  
   654  	// Name holds the span name, initialized with the value passed to StartSpan.
   655  	Name string
   656  
   657  	// Type holds the overarching span type, such as "db", and will be initialized
   658  	// with the value passed to StartSpan.
   659  	Type string
   660  
   661  	// Subtype holds the span subtype, such as "mysql". This will initially be empty,
   662  	// and can be set after starting the span.
   663  	Subtype string
   664  
   665  	// Action holds the span action, such as "query". This will initially be empty,
   666  	// and can be set after starting the span.
   667  	Action string
   668  
   669  	// Duration holds the span duration, initialized to -1.
   670  	//
   671  	// If you do not update Duration, calling Span.End will calculate the
   672  	// duration based on the elapsed time since the span's start time.
   673  	Duration time.Duration
   674  
   675  	// Outcome holds the span outcome: success, failure, or unknown (the default).
   676  	// If Outcome is set to something else, it will be replaced with "unknown".
   677  	//
   678  	// Outcome is used for error rate calculations. A value of "success" indicates
   679  	// that a operation succeeded, while "failure" indicates that the operation
   680  	// failed. If Outcome is set to "unknown" (or some other value), then the
   681  	// span will not be included in error rate calculations.
   682  	Outcome string
   683  
   684  	// Context describes the context in which span occurs.
   685  	Context SpanContext
   686  
   687  	links []SpanLink
   688  
   689  	mu            sync.Mutex
   690  	stacktrace    []stacktrace.Frame
   691  	errorCaptured bool
   692  }
   693  
   694  func (s *SpanData) setStacktrace(skip int) {
   695  	s.stacktrace = stacktrace.AppendStacktrace(s.stacktrace[:0], skip+1, s.stackTraceLimit)
   696  }
   697  
   698  func (s *SpanData) reset(tracer *Tracer) {
   699  	*s = SpanData{
   700  		Context:    s.Context,
   701  		Duration:   -1,
   702  		stacktrace: s.stacktrace[:0],
   703  	}
   704  	s.Context.reset()
   705  	tracer.spanDataPool.Put(s)
   706  }