blob: 0b65ae9ab703ecae3cb7f5bbe200a169edbf68e7 [file] [log] [blame]
Abhay Kumara2ae5992025-11-10 14:02:24 +00001// Copyright The OpenTelemetry Authors
2// SPDX-License-Identifier: Apache-2.0
3
4package trace // import "go.opentelemetry.io/otel/sdk/trace"
5
6import (
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
15type tracer struct {
16 embedded.Tracer
17
18 provider *TracerProvider
19 instrumentationScope instrumentation.Scope
20}
21
22var _ 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.
29func (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
62type 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.
69func (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.
119func (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.
160func (tr *tracer) newNonRecordingSpan(sc trace.SpanContext) nonRecordingSpan {
161 return nonRecordingSpan{tracer: tr, sc: sc}
162}