| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 1 | // Copyright The OpenTelemetry Authors |
| 2 | // SPDX-License-Identifier: Apache-2.0 |
| 3 | |
| 4 | package trace // import "go.opentelemetry.io/otel/sdk/trace" |
| 5 | |
| 6 | import ( |
| 7 | "context" |
| 8 | "time" |
| 9 | |
| 10 | "go.opentelemetry.io/otel/sdk/instrumentation" |
| 11 | "go.opentelemetry.io/otel/trace" |
| 12 | "go.opentelemetry.io/otel/trace/embedded" |
| 13 | ) |
| 14 | |
| 15 | type tracer struct { |
| 16 | embedded.Tracer |
| 17 | |
| 18 | provider *TracerProvider |
| 19 | instrumentationScope instrumentation.Scope |
| 20 | } |
| 21 | |
| 22 | var _ trace.Tracer = &tracer{} |
| 23 | |
| 24 | // Start starts a Span and returns it along with a context containing it. |
| 25 | // |
| 26 | // The Span is created with the provided name and as a child of any existing |
| 27 | // span context found in the passed context. The created Span will be |
| 28 | // configured appropriately by any SpanOption passed. |
| 29 | func (tr *tracer) Start( |
| 30 | ctx context.Context, |
| 31 | name string, |
| 32 | options ...trace.SpanStartOption, |
| 33 | ) (context.Context, trace.Span) { |
| 34 | config := trace.NewSpanStartConfig(options...) |
| 35 | |
| 36 | if ctx == nil { |
| 37 | // Prevent trace.ContextWithSpan from panicking. |
| 38 | ctx = context.Background() |
| 39 | } |
| 40 | |
| 41 | // For local spans created by this SDK, track child span count. |
| 42 | if p := trace.SpanFromContext(ctx); p != nil { |
| 43 | if sdkSpan, ok := p.(*recordingSpan); ok { |
| 44 | sdkSpan.addChild() |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | s := tr.newSpan(ctx, name, &config) |
| 49 | if rw, ok := s.(ReadWriteSpan); ok && s.IsRecording() { |
| 50 | sps := tr.provider.getSpanProcessors() |
| 51 | for _, sp := range sps { |
| 52 | sp.sp.OnStart(ctx, rw) |
| 53 | } |
| 54 | } |
| 55 | if rtt, ok := s.(runtimeTracer); ok { |
| 56 | ctx = rtt.runtimeTrace(ctx) |
| 57 | } |
| 58 | |
| 59 | return trace.ContextWithSpan(ctx, s), s |
| 60 | } |
| 61 | |
| 62 | type runtimeTracer interface { |
| 63 | // runtimeTrace starts a "runtime/trace".Task for the span and |
| 64 | // returns a context containing the task. |
| 65 | runtimeTrace(ctx context.Context) context.Context |
| 66 | } |
| 67 | |
| 68 | // newSpan returns a new configured span. |
| 69 | func (tr *tracer) newSpan(ctx context.Context, name string, config *trace.SpanConfig) trace.Span { |
| 70 | // If told explicitly to make this a new root use a zero value SpanContext |
| 71 | // as a parent which contains an invalid trace ID and is not remote. |
| 72 | var psc trace.SpanContext |
| 73 | if config.NewRoot() { |
| 74 | ctx = trace.ContextWithSpanContext(ctx, psc) |
| 75 | } else { |
| 76 | psc = trace.SpanContextFromContext(ctx) |
| 77 | } |
| 78 | |
| 79 | // If there is a valid parent trace ID, use it to ensure the continuity of |
| 80 | // the trace. Always generate a new span ID so other components can rely |
| 81 | // on a unique span ID, even if the Span is non-recording. |
| 82 | var tid trace.TraceID |
| 83 | var sid trace.SpanID |
| 84 | if !psc.TraceID().IsValid() { |
| 85 | tid, sid = tr.provider.idGenerator.NewIDs(ctx) |
| 86 | } else { |
| 87 | tid = psc.TraceID() |
| 88 | sid = tr.provider.idGenerator.NewSpanID(ctx, tid) |
| 89 | } |
| 90 | |
| 91 | samplingResult := tr.provider.sampler.ShouldSample(SamplingParameters{ |
| 92 | ParentContext: ctx, |
| 93 | TraceID: tid, |
| 94 | Name: name, |
| 95 | Kind: config.SpanKind(), |
| 96 | Attributes: config.Attributes(), |
| 97 | Links: config.Links(), |
| 98 | }) |
| 99 | |
| 100 | scc := trace.SpanContextConfig{ |
| 101 | TraceID: tid, |
| 102 | SpanID: sid, |
| 103 | TraceState: samplingResult.Tracestate, |
| 104 | } |
| 105 | if isSampled(samplingResult) { |
| 106 | scc.TraceFlags = psc.TraceFlags() | trace.FlagsSampled |
| 107 | } else { |
| 108 | scc.TraceFlags = psc.TraceFlags() &^ trace.FlagsSampled |
| 109 | } |
| 110 | sc := trace.NewSpanContext(scc) |
| 111 | |
| 112 | if !isRecording(samplingResult) { |
| 113 | return tr.newNonRecordingSpan(sc) |
| 114 | } |
| 115 | return tr.newRecordingSpan(psc, sc, name, samplingResult, config) |
| 116 | } |
| 117 | |
| 118 | // newRecordingSpan returns a new configured recordingSpan. |
| 119 | func (tr *tracer) newRecordingSpan( |
| 120 | psc, sc trace.SpanContext, |
| 121 | name string, |
| 122 | sr SamplingResult, |
| 123 | config *trace.SpanConfig, |
| 124 | ) *recordingSpan { |
| 125 | startTime := config.Timestamp() |
| 126 | if startTime.IsZero() { |
| 127 | startTime = time.Now() |
| 128 | } |
| 129 | |
| 130 | s := &recordingSpan{ |
| 131 | // Do not pre-allocate the attributes slice here! Doing so will |
| 132 | // allocate memory that is likely never going to be used, or if used, |
| 133 | // will be over-sized. The default Go compiler has been tested to |
| 134 | // dynamically allocate needed space very well. Benchmarking has shown |
| 135 | // it to be more performant than what we can predetermine here, |
| 136 | // especially for the common use case of few to no added |
| 137 | // attributes. |
| 138 | |
| 139 | parent: psc, |
| 140 | spanContext: sc, |
| 141 | spanKind: trace.ValidateSpanKind(config.SpanKind()), |
| 142 | name: name, |
| 143 | startTime: startTime, |
| 144 | events: newEvictedQueueEvent(tr.provider.spanLimits.EventCountLimit), |
| 145 | links: newEvictedQueueLink(tr.provider.spanLimits.LinkCountLimit), |
| 146 | tracer: tr, |
| 147 | } |
| 148 | |
| 149 | for _, l := range config.Links() { |
| 150 | s.AddLink(l) |
| 151 | } |
| 152 | |
| 153 | s.SetAttributes(sr.Attributes...) |
| 154 | s.SetAttributes(config.Attributes()...) |
| 155 | |
| 156 | return s |
| 157 | } |
| 158 | |
| 159 | // newNonRecordingSpan returns a new configured nonRecordingSpan. |
| 160 | func (tr *tracer) newNonRecordingSpan(sc trace.SpanContext) nonRecordingSpan { |
| 161 | return nonRecordingSpan{tracer: tr, sc: sc} |
| 162 | } |