func()

in span.go [366:472]


func (s *Span) End() {
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.ended() {
		return
	}
	if s.Type == "" {
		s.Type = "custom"
	}
	// Store the span type and subtype on the Span struct so we can filter
	// out child spans of exit spans with non-matching type/subtype below.
	s.finalType = s.Type
	s.finalSubtype = s.Subtype

	if s.parent.IsExitSpan() {
		// Children of exit spans must not have service destination/target
		// context, as otherwise service destination metrics will be double
		// counted.
		s.Context.model.Destination = nil
		s.Context.model.Service = nil

		var parentType, parentSubtype string
		s.parent.mu.RLock()
		if s.parent.ended() {
			parentType = s.parent.finalType
			parentSubtype = s.parent.finalSubtype
		} else {
			parentType = s.parent.Type
			parentSubtype = s.parent.Subtype
		}
		s.parent.mu.RUnlock()

		if s.Type != parentType || s.Subtype != parentSubtype {
			s.dropWhen(true)
			if s.tx != nil {
				s.tx.mu.Lock()
				defer s.tx.mu.Unlock()
				if !s.tx.ended() {
					s.tx.TransactionData.mu.Lock()
					defer s.tx.TransactionData.mu.Unlock()
				}
			}
			s.end()
			return
		}
	}
	if s.exit && !s.Context.setDestinationServiceCalled {
		// The span was created as an exit span, but the user did not
		// manually set the destination.service.resource
		s.setExitSpanDestinationService()
	}

	s.updateSpanServiceTarget()

	if s.Duration < 0 {
		s.Duration = time.Since(s.timestamp)
	}
	if s.Outcome == "" {
		s.Outcome = s.Context.outcome()
		if s.Outcome == "" {
			if s.errorCaptured {
				s.Outcome = "failure"
			} else {
				s.Outcome = "success"
			}
		}
	}
	switch {
	case s.stackStackTraceMinDuration < 0:
		// If s.stackFramesMinDuration < 0, we never set stacktrace.
	case s.stackStackTraceMinDuration == 0:
		// Always set stacktrace
		s.setStacktrace(1)
	default:
		if !s.dropped() && len(s.stacktrace) == 0 &&
			s.Duration >= s.stackStackTraceMinDuration {
			s.setStacktrace(1)
		}
	}
	// If this span has a parent span, lock it before proceeding to
	// prevent deadlocking when concurrently ending parent and child.
	if s.parent != nil {
		s.parent.mu.Lock()
		defer s.parent.mu.Unlock()
	}
	if s.tx != nil {
		s.tx.mu.RLock()
		defer s.tx.mu.RUnlock()
		if !s.tx.ended() {
			s.tx.TransactionData.mu.Lock()
			defer s.tx.TransactionData.mu.Unlock()
			s.reportSelfTime()
		}
	}

	evictedSpan, cached := s.attemptCompress()
	if evictedSpan != nil {
		evictedSpan.end()
	}
	if cached {
		// s has been cached for potential compression, and will be enqueued
		// by a future call to attemptCompress on a sibling span, or when the
		// parent is ended.
		return
	}
	s.end()
}