in span.go [88:164]
func (tx *Transaction) StartSpanOptions(name, spanType string, opts SpanOptions) *Span {
if tx == nil {
return newDroppedSpan()
}
if opts.Parent == (TraceContext{}) {
if opts.parent != nil {
opts.Parent = opts.parent.TraceContext()
} else {
opts.Parent = tx.traceContext
}
}
transactionID := tx.traceContext.Span
// Lock the parent first to avoid deadlocks in breakdown metrics calculation.
if opts.parent != nil {
opts.parent.mu.Lock()
defer opts.parent.mu.Unlock()
}
// Prevent tx from being ended while we're starting a span.
tx.mu.RLock()
defer tx.mu.RUnlock()
if tx.ended() {
return tx.tracer.StartSpan(name, spanType, transactionID, opts)
}
// Calculate the span time relative to the transaction timestamp so
// that wall-clock adjustments occurring after the transaction start
// don't affect the span timestamp.
if opts.Start.IsZero() {
opts.Start = tx.timestamp.Add(time.Since(tx.timestamp))
} else {
opts.Start = tx.timestamp.Add(opts.Start.Sub(tx.timestamp))
}
span := tx.tracer.startSpan(name, spanType, transactionID, opts)
span.tx = tx
span.parent = opts.parent
if opts.ExitSpan {
span.exit = true
}
// Guard access to spansCreated, spansDropped, rand, and childrenTimer.
tx.TransactionData.mu.Lock()
defer tx.TransactionData.mu.Unlock()
notRecorded := !span.traceContext.Options.Recorded()
exceedsMaxSpans := tx.maxSpans >= 0 && tx.spansCreated >= tx.maxSpans
// Drop span when it is not recorded.
if span.dropWhen(notRecorded) {
// nothing to do here since it isn't recorded.
} else if span.dropWhen(exceedsMaxSpans) {
tx.spansDropped++
} else {
if opts.SpanID.Validate() == nil {
span.traceContext.Span = opts.SpanID
} else {
binary.LittleEndian.PutUint64(span.traceContext.Span[:], tx.rand.Uint64())
}
span.stackStackTraceMinDuration = tx.spanStackTraceMinDuration
span.stackTraceLimit = tx.stackTraceLimit
span.compressedSpan.options = tx.compressedSpan.options
span.exitSpanMinDuration = tx.exitSpanMinDuration
tx.spansCreated++
}
if tx.breakdownMetricsEnabled {
if span.parent != nil {
if !span.parent.ended() {
span.parent.childrenTimer.childStarted(span.timestamp)
}
} else {
tx.childrenTimer.childStarted(span.timestamp)
}
}
return span
}