in transaction.go [44:173]
func (t *Tracer) StartTransactionOptions(name, transactionType string, opts TransactionOptions) *Transaction {
td, _ := t.transactionDataPool.Get().(*TransactionData)
if td == nil {
td = &TransactionData{
Duration: -1,
Context: Context{
captureBodyMask: CaptureBodyTransactions,
},
spanTimings: make(spanTimingsMap),
droppedSpansStats: make(droppedSpanTimingsMap),
}
var seed int64
if err := binary.Read(cryptorand.Reader, binary.LittleEndian, &seed); err != nil {
seed = time.Now().UnixNano()
}
td.rand = rand.New(rand.NewSource(seed))
}
tx := &Transaction{tracer: t, TransactionData: td}
// Take a snapshot of config that should apply to all spans within the
// transaction.
instrumentationConfig := t.instrumentationConfig()
tx.recording = instrumentationConfig.recording
if !tx.recording || !t.Active() {
return tx
}
tx.maxSpans = instrumentationConfig.maxSpans
tx.compressedSpan.options = instrumentationConfig.compressionOptions
tx.exitSpanMinDuration = instrumentationConfig.exitSpanMinDuration
tx.spanStackTraceMinDuration = instrumentationConfig.spanStackTraceMinDuration
tx.stackTraceLimit = instrumentationConfig.stackTraceLimit
tx.Context.captureHeaders = instrumentationConfig.captureHeaders
tx.propagateLegacyHeader = instrumentationConfig.propagateLegacyHeader
tx.Context.sanitizedFieldNames = instrumentationConfig.sanitizedFieldNames
tx.breakdownMetricsEnabled = t.breakdownMetrics.enabled
continuationStrategy := instrumentationConfig.continuationStrategy
shouldRestartTrace := false
if continuationStrategy == "restart_external" {
if opts.TraceContext.State.haveElastic {
continuationStrategy = "continue"
} else {
continuationStrategy = "restart"
}
}
if continuationStrategy == "restart" {
if !opts.TraceContext.Trace.isZero() && !opts.TraceContext.Span.isZero() {
link := SpanLink{
Trace: opts.TraceContext.Trace,
Span: opts.TraceContext.Span,
}
tx.links = append(tx.links, link)
shouldRestartTrace = true
}
}
var root bool
if opts.TraceContext.Trace.Validate() == nil && !shouldRestartTrace {
tx.traceContext.Trace = opts.TraceContext.Trace
tx.traceContext.Options = opts.TraceContext.Options
if opts.TraceContext.Span.Validate() == nil {
tx.parentID = opts.TraceContext.Span
}
if opts.TransactionID.Validate() == nil {
tx.traceContext.Span = opts.TransactionID
} else {
binary.LittleEndian.PutUint64(tx.traceContext.Span[:], tx.rand.Uint64())
}
if opts.TraceContext.State.Validate() == nil {
tx.traceContext.State = opts.TraceContext.State
}
} else {
// Start a new trace. We reuse the trace ID for the root transaction's ID
// if one is not specified in the options.
root = true
binary.LittleEndian.PutUint64(tx.traceContext.Trace[:8], tx.rand.Uint64())
binary.LittleEndian.PutUint64(tx.traceContext.Trace[8:], tx.rand.Uint64())
if opts.TransactionID.Validate() == nil {
tx.traceContext.Span = opts.TransactionID
} else {
copy(tx.traceContext.Span[:], tx.traceContext.Trace[:])
}
}
if root {
var result SampleResult
if instrumentationConfig.sampler != nil {
result = instrumentationConfig.sampler.Sample(SampleParams{
TraceContext: tx.traceContext,
})
if !result.Sampled {
// Special case: for unsampled transactions we
// report a sample rate of 0, so that we do not
// count them in aggregations in the server.
// This is necessary to avoid overcounting, as
// we will scale the sampled transactions.
result.SampleRate = 0
}
sampleRate := roundSampleRate(result.SampleRate)
tx.traceContext.State = NewTraceState(TraceStateEntry{
Key: elasticTracestateVendorKey,
Value: formatElasticTracestateValue(sampleRate),
})
} else {
result.Sampled = true
}
if result.Sampled {
o := tx.traceContext.Options.WithRecorded(true)
tx.traceContext.Options = o
}
} else {
// TODO(axw) make this behaviour configurable. In some cases
// it may not be a good idea to honour the recorded flag, as
// it may open up the application to DoS by forced sampling.
// Even ignoring bad actors, a service that has many feeder
// applications may end up being sampled at a very high rate.
tx.traceContext.Options = opts.TraceContext.Options
}
tx.Name = name
tx.Type = transactionType
tx.timestamp = opts.Start
if tx.timestamp.IsZero() {
tx.timestamp = time.Now()
}
tx.links = append(tx.links, opts.Links...)
return tx
}