1 // Copyright The OpenTelemetry Authors
2 // SPDX-License-Identifier: Apache-2.0
3 4 package global // import "go.opentelemetry.io/otel/internal/global"
5 6 /*
7 This file contains the forwarding implementation of the TracerProvider used as
8 the default global instance. Prior to initialization of an SDK, Tracers
9 returned by the global TracerProvider will provide no-op functionality. This
10 means that all Span created prior to initialization are no-op Spans.
11 12 Once an SDK has been initialized, all provided no-op Tracers are swapped for
13 Tracers provided by the SDK defined TracerProvider. However, any Span started
14 prior to this initialization does not change its behavior. Meaning, the Span
15 remains a no-op Span.
16 17 The implementation to track and swap Tracers locks all new Tracer creation
18 until the swap is complete. This assumes that this operation is not
19 performance-critical. If that assumption is incorrect, be sure to configure an
20 SDK prior to any Tracer creation.
21 */
22 23 import (
24 "context"
25 "sync"
26 "sync/atomic"
27 28 "go.opentelemetry.io/auto/sdk"
29 30 "go.opentelemetry.io/otel/attribute"
31 "go.opentelemetry.io/otel/codes"
32 "go.opentelemetry.io/otel/trace"
33 "go.opentelemetry.io/otel/trace/embedded"
34 )
35 36 // tracerProvider is a placeholder for a configured SDK TracerProvider.
37 //
38 // All TracerProvider functionality is forwarded to a delegate once
39 // configured.
40 type tracerProvider struct {
41 embedded.TracerProvider
42 43 mtx sync.Mutex
44 tracers map[il]*tracer
45 delegate trace.TracerProvider
46 }
47 48 // Compile-time guarantee that tracerProvider implements the TracerProvider
49 // interface.
50 var _ trace.TracerProvider = &tracerProvider{}
51 52 // setDelegate configures p to delegate all TracerProvider functionality to
53 // provider.
54 //
55 // All Tracers provided prior to this function call are switched out to be
56 // Tracers provided by provider.
57 //
58 // It is guaranteed by the caller that this happens only once.
59 func (p *tracerProvider) setDelegate(provider trace.TracerProvider) {
60 p.mtx.Lock()
61 defer p.mtx.Unlock()
62 63 p.delegate = provider
64 65 if len(p.tracers) == 0 {
66 return
67 }
68 69 for _, t := range p.tracers {
70 t.setDelegate(provider)
71 }
72 73 p.tracers = nil
74 }
75 76 // Tracer implements TracerProvider.
77 func (p *tracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer {
78 p.mtx.Lock()
79 defer p.mtx.Unlock()
80 81 if p.delegate != nil {
82 return p.delegate.Tracer(name, opts...)
83 }
84 85 // At this moment it is guaranteed that no sdk is installed, save the tracer in the tracers map.
86 87 c := trace.NewTracerConfig(opts...)
88 key := il{
89 name: name,
90 version: c.InstrumentationVersion(),
91 schema: c.SchemaURL(),
92 attrs: c.InstrumentationAttributes(),
93 }
94 95 if p.tracers == nil {
96 p.tracers = make(map[il]*tracer)
97 }
98 99 if val, ok := p.tracers[key]; ok {
100 return val
101 }
102 103 t := &tracer{name: name, opts: opts, provider: p}
104 p.tracers[key] = t
105 return t
106 }
107 108 type il struct {
109 name string
110 version string
111 schema string
112 attrs attribute.Set
113 }
114 115 // tracer is a placeholder for a trace.Tracer.
116 //
117 // All Tracer functionality is forwarded to a delegate once configured.
118 // Otherwise, all functionality is forwarded to a NoopTracer.
119 type tracer struct {
120 embedded.Tracer
121 122 name string
123 opts []trace.TracerOption
124 provider *tracerProvider
125 126 delegate atomic.Value
127 }
128 129 // Compile-time guarantee that tracer implements the trace.Tracer interface.
130 var _ trace.Tracer = &tracer{}
131 132 // setDelegate configures t to delegate all Tracer functionality to Tracers
133 // created by provider.
134 //
135 // All subsequent calls to the Tracer methods will be passed to the delegate.
136 //
137 // It is guaranteed by the caller that this happens only once.
138 func (t *tracer) setDelegate(provider trace.TracerProvider) {
139 t.delegate.Store(provider.Tracer(t.name, t.opts...))
140 }
141 142 // Start implements trace.Tracer by forwarding the call to t.delegate if
143 // set, otherwise it forwards the call to a NoopTracer.
144 func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
145 delegate := t.delegate.Load()
146 if delegate != nil {
147 return delegate.(trace.Tracer).Start(ctx, name, opts...)
148 }
149 150 return t.newSpan(ctx, autoInstEnabled, name, opts)
151 }
152 153 // autoInstEnabled determines if the auto-instrumentation SDK span is returned
154 // from the tracer when not backed by a delegate and auto-instrumentation has
155 // attached to this process.
156 //
157 // The auto-instrumentation is expected to overwrite this value to true when it
158 // attaches. By default, this will point to false and mean a tracer will return
159 // a nonRecordingSpan by default.
160 var autoInstEnabled = new(bool)
161 162 // newSpan is called by tracer.Start so auto-instrumentation can attach an eBPF
163 // uprobe to this code.
164 //
165 // "noinline" pragma prevents the method from ever being inlined.
166 //
167 //go:noinline
168 func (t *tracer) newSpan(
169 ctx context.Context,
170 autoSpan *bool,
171 name string,
172 opts []trace.SpanStartOption,
173 ) (context.Context, trace.Span) {
174 // autoInstEnabled is passed to newSpan via the autoSpan parameter. This is
175 // so the auto-instrumentation can define a uprobe for (*t).newSpan and be
176 // provided with the address of the bool autoInstEnabled points to. It
177 // needs to be a parameter so that pointer can be reliably determined, it
178 // should not be read from the global.
179 180 if *autoSpan {
181 tracer := sdk.TracerProvider().Tracer(t.name, t.opts...)
182 return tracer.Start(ctx, name, opts...)
183 }
184 185 s := nonRecordingSpan{sc: trace.SpanContextFromContext(ctx), tracer: t}
186 ctx = trace.ContextWithSpan(ctx, s)
187 return ctx, s
188 }
189 190 // nonRecordingSpan is a minimal implementation of a Span that wraps a
191 // SpanContext. It performs no operations other than to return the wrapped
192 // SpanContext.
193 type nonRecordingSpan struct {
194 embedded.Span
195 196 sc trace.SpanContext
197 tracer *tracer
198 }
199 200 var _ trace.Span = nonRecordingSpan{}
201 202 // SpanContext returns the wrapped SpanContext.
203 func (s nonRecordingSpan) SpanContext() trace.SpanContext { return s.sc }
204 205 // IsRecording always returns false.
206 func (nonRecordingSpan) IsRecording() bool { return false }
207 208 // SetStatus does nothing.
209 func (nonRecordingSpan) SetStatus(codes.Code, string) {}
210 211 // SetError does nothing.
212 func (nonRecordingSpan) SetError(bool) {}
213 214 // SetAttributes does nothing.
215 func (nonRecordingSpan) SetAttributes(...attribute.KeyValue) {}
216 217 // End does nothing.
218 func (nonRecordingSpan) End(...trace.SpanEndOption) {}
219 220 // RecordError does nothing.
221 func (nonRecordingSpan) RecordError(error, ...trace.EventOption) {}
222 223 // AddEvent does nothing.
224 func (nonRecordingSpan) AddEvent(string, ...trace.EventOption) {}
225 226 // AddLink does nothing.
227 func (nonRecordingSpan) AddLink(trace.Link) {}
228 229 // SetName does nothing.
230 func (nonRecordingSpan) SetName(string) {}
231 232 func (s nonRecordingSpan) TracerProvider() trace.TracerProvider { return s.tracer.provider }
233