blob: 0e2a2e7c60d97eb3c1f351710319edca347283fa [file] [log] [blame]
Abhay Kumar40252eb2025-10-13 13:25:53 +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 "fmt"
9 "sync"
10 "sync/atomic"
11
12 "go.opentelemetry.io/otel"
13 "go.opentelemetry.io/otel/internal/global"
14 "go.opentelemetry.io/otel/sdk/instrumentation"
15 "go.opentelemetry.io/otel/sdk/resource"
16 "go.opentelemetry.io/otel/trace"
17 "go.opentelemetry.io/otel/trace/embedded"
18 "go.opentelemetry.io/otel/trace/noop"
19)
20
21const (
22 defaultTracerName = "go.opentelemetry.io/otel/sdk/tracer"
23)
24
25// tracerProviderConfig.
26type tracerProviderConfig struct {
27 // processors contains collection of SpanProcessors that are processing pipeline
28 // for spans in the trace signal.
29 // SpanProcessors registered with a TracerProvider and are called at the start
30 // and end of a Span's lifecycle, and are called in the order they are
31 // registered.
32 processors []SpanProcessor
33
34 // sampler is the default sampler used when creating new spans.
35 sampler Sampler
36
37 // idGenerator is used to generate all Span and Trace IDs when needed.
38 idGenerator IDGenerator
39
40 // spanLimits defines the attribute, event, and link limits for spans.
41 spanLimits SpanLimits
42
43 // resource contains attributes representing an entity that produces telemetry.
44 resource *resource.Resource
45}
46
47// MarshalLog is the marshaling function used by the logging system to represent this Provider.
48func (cfg tracerProviderConfig) MarshalLog() interface{} {
49 return struct {
50 SpanProcessors []SpanProcessor
51 SamplerType string
52 IDGeneratorType string
53 SpanLimits SpanLimits
54 Resource *resource.Resource
55 }{
56 SpanProcessors: cfg.processors,
57 SamplerType: fmt.Sprintf("%T", cfg.sampler),
58 IDGeneratorType: fmt.Sprintf("%T", cfg.idGenerator),
59 SpanLimits: cfg.spanLimits,
60 Resource: cfg.resource,
61 }
62}
63
64// TracerProvider is an OpenTelemetry TracerProvider. It provides Tracers to
65// instrumentation so it can trace operational flow through a system.
66type TracerProvider struct {
67 embedded.TracerProvider
68
69 mu sync.Mutex
70 namedTracer map[instrumentation.Scope]*tracer
71 spanProcessors atomic.Pointer[spanProcessorStates]
72
73 isShutdown atomic.Bool
74
75 // These fields are not protected by the lock mu. They are assumed to be
76 // immutable after creation of the TracerProvider.
77 sampler Sampler
78 idGenerator IDGenerator
79 spanLimits SpanLimits
80 resource *resource.Resource
81}
82
83var _ trace.TracerProvider = &TracerProvider{}
84
85// NewTracerProvider returns a new and configured TracerProvider.
86//
87// By default the returned TracerProvider is configured with:
88// - a ParentBased(AlwaysSample) Sampler
89// - a random number IDGenerator
90// - the resource.Default() Resource
91// - the default SpanLimits.
92//
93// The passed opts are used to override these default values and configure the
94// returned TracerProvider appropriately.
95func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider {
96 o := tracerProviderConfig{
97 spanLimits: NewSpanLimits(),
98 }
99 o = applyTracerProviderEnvConfigs(o)
100
101 for _, opt := range opts {
102 o = opt.apply(o)
103 }
104
105 o = ensureValidTracerProviderConfig(o)
106
107 tp := &TracerProvider{
108 namedTracer: make(map[instrumentation.Scope]*tracer),
109 sampler: o.sampler,
110 idGenerator: o.idGenerator,
111 spanLimits: o.spanLimits,
112 resource: o.resource,
113 }
114 global.Info("TracerProvider created", "config", o)
115
116 spss := make(spanProcessorStates, 0, len(o.processors))
117 for _, sp := range o.processors {
118 spss = append(spss, newSpanProcessorState(sp))
119 }
120 tp.spanProcessors.Store(&spss)
121
122 return tp
123}
124
125// Tracer returns a Tracer with the given name and options. If a Tracer for
126// the given name and options does not exist it is created, otherwise the
127// existing Tracer is returned.
128//
129// If name is empty, DefaultTracerName is used instead.
130//
131// This method is safe to be called concurrently.
132func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer {
133 // This check happens before the mutex is acquired to avoid deadlocking if Tracer() is called from within Shutdown().
134 if p.isShutdown.Load() {
135 return noop.NewTracerProvider().Tracer(name, opts...)
136 }
137 c := trace.NewTracerConfig(opts...)
138 if name == "" {
139 name = defaultTracerName
140 }
141 is := instrumentation.Scope{
142 Name: name,
143 Version: c.InstrumentationVersion(),
144 SchemaURL: c.SchemaURL(),
145 Attributes: c.InstrumentationAttributes(),
146 }
147
148 t, ok := func() (trace.Tracer, bool) {
149 p.mu.Lock()
150 defer p.mu.Unlock()
151 // Must check the flag after acquiring the mutex to avoid returning a valid tracer if Shutdown() ran
152 // after the first check above but before we acquired the mutex.
153 if p.isShutdown.Load() {
154 return noop.NewTracerProvider().Tracer(name, opts...), true
155 }
156 t, ok := p.namedTracer[is]
157 if !ok {
158 t = &tracer{
159 provider: p,
160 instrumentationScope: is,
161 }
162 p.namedTracer[is] = t
163 }
164 return t, ok
165 }()
166 if !ok {
167 // This code is outside the mutex to not hold the lock while calling third party logging code:
168 // - That code may do slow things like I/O, which would prolong the duration the lock is held,
169 // slowing down all tracing consumers.
170 // - Logging code may be instrumented with tracing and deadlock because it could try
171 // acquiring the same non-reentrant mutex.
172 global.Info(
173 "Tracer created",
174 "name",
175 name,
176 "version",
177 is.Version,
178 "schemaURL",
179 is.SchemaURL,
180 "attributes",
181 is.Attributes,
182 )
183 }
184 return t
185}
186
187// RegisterSpanProcessor adds the given SpanProcessor to the list of SpanProcessors.
188func (p *TracerProvider) RegisterSpanProcessor(sp SpanProcessor) {
189 // This check prevents calls during a shutdown.
190 if p.isShutdown.Load() {
191 return
192 }
193 p.mu.Lock()
194 defer p.mu.Unlock()
195 // This check prevents calls after a shutdown.
196 if p.isShutdown.Load() {
197 return
198 }
199
200 current := p.getSpanProcessors()
201 newSPS := make(spanProcessorStates, 0, len(current)+1)
202 newSPS = append(newSPS, current...)
203 newSPS = append(newSPS, newSpanProcessorState(sp))
204 p.spanProcessors.Store(&newSPS)
205}
206
207// UnregisterSpanProcessor removes the given SpanProcessor from the list of SpanProcessors.
208func (p *TracerProvider) UnregisterSpanProcessor(sp SpanProcessor) {
209 // This check prevents calls during a shutdown.
210 if p.isShutdown.Load() {
211 return
212 }
213 p.mu.Lock()
214 defer p.mu.Unlock()
215 // This check prevents calls after a shutdown.
216 if p.isShutdown.Load() {
217 return
218 }
219 old := p.getSpanProcessors()
220 if len(old) == 0 {
221 return
222 }
223 spss := make(spanProcessorStates, len(old))
224 copy(spss, old)
225
226 // stop the span processor if it is started and remove it from the list
227 var stopOnce *spanProcessorState
228 var idx int
229 for i, sps := range spss {
230 if sps.sp == sp {
231 stopOnce = sps
232 idx = i
233 }
234 }
235 if stopOnce != nil {
236 stopOnce.state.Do(func() {
237 if err := sp.Shutdown(context.Background()); err != nil {
238 otel.Handle(err)
239 }
240 })
241 }
242 if len(spss) > 1 {
243 copy(spss[idx:], spss[idx+1:])
244 }
245 spss[len(spss)-1] = nil
246 spss = spss[:len(spss)-1]
247
248 p.spanProcessors.Store(&spss)
249}
250
251// ForceFlush immediately exports all spans that have not yet been exported for
252// all the registered span processors.
253func (p *TracerProvider) ForceFlush(ctx context.Context) error {
254 spss := p.getSpanProcessors()
255 if len(spss) == 0 {
256 return nil
257 }
258
259 for _, sps := range spss {
260 select {
261 case <-ctx.Done():
262 return ctx.Err()
263 default:
264 }
265
266 if err := sps.sp.ForceFlush(ctx); err != nil {
267 return err
268 }
269 }
270 return nil
271}
272
273// Shutdown shuts down TracerProvider. All registered span processors are shut down
274// in the order they were registered and any held computational resources are released.
275// After Shutdown is called, all methods are no-ops.
276func (p *TracerProvider) Shutdown(ctx context.Context) error {
277 // This check prevents deadlocks in case of recursive shutdown.
278 if p.isShutdown.Load() {
279 return nil
280 }
281 p.mu.Lock()
282 defer p.mu.Unlock()
283 // This check prevents calls after a shutdown has already been done concurrently.
284 if !p.isShutdown.CompareAndSwap(false, true) { // did toggle?
285 return nil
286 }
287
288 var retErr error
289 for _, sps := range p.getSpanProcessors() {
290 select {
291 case <-ctx.Done():
292 return ctx.Err()
293 default:
294 }
295
296 var err error
297 sps.state.Do(func() {
298 err = sps.sp.Shutdown(ctx)
299 })
300 if err != nil {
301 if retErr == nil {
302 retErr = err
303 } else {
304 // Poor man's list of errors
305 retErr = fmt.Errorf("%w; %w", retErr, err)
306 }
307 }
308 }
309 p.spanProcessors.Store(&spanProcessorStates{})
310 return retErr
311}
312
313func (p *TracerProvider) getSpanProcessors() spanProcessorStates {
314 return *(p.spanProcessors.Load())
315}
316
317// TracerProviderOption configures a TracerProvider.
318type TracerProviderOption interface {
319 apply(tracerProviderConfig) tracerProviderConfig
320}
321
322type traceProviderOptionFunc func(tracerProviderConfig) tracerProviderConfig
323
324func (fn traceProviderOptionFunc) apply(cfg tracerProviderConfig) tracerProviderConfig {
325 return fn(cfg)
326}
327
328// WithSyncer registers the exporter with the TracerProvider using a
329// SimpleSpanProcessor.
330//
331// This is not recommended for production use. The synchronous nature of the
332// SimpleSpanProcessor that will wrap the exporter make it good for testing,
333// debugging, or showing examples of other feature, but it will be slow and
334// have a high computation resource usage overhead. The WithBatcher option is
335// recommended for production use instead.
336func WithSyncer(e SpanExporter) TracerProviderOption {
337 return WithSpanProcessor(NewSimpleSpanProcessor(e))
338}
339
340// WithBatcher registers the exporter with the TracerProvider using a
341// BatchSpanProcessor configured with the passed opts.
342func WithBatcher(e SpanExporter, opts ...BatchSpanProcessorOption) TracerProviderOption {
343 return WithSpanProcessor(NewBatchSpanProcessor(e, opts...))
344}
345
346// WithSpanProcessor registers the SpanProcessor with a TracerProvider.
347func WithSpanProcessor(sp SpanProcessor) TracerProviderOption {
348 return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
349 cfg.processors = append(cfg.processors, sp)
350 return cfg
351 })
352}
353
354// WithResource returns a TracerProviderOption that will configure the
355// Resource r as a TracerProvider's Resource. The configured Resource is
356// referenced by all the Tracers the TracerProvider creates. It represents the
357// entity producing telemetry.
358//
359// If this option is not used, the TracerProvider will use the
360// resource.Default() Resource by default.
361func WithResource(r *resource.Resource) TracerProviderOption {
362 return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
363 var err error
364 cfg.resource, err = resource.Merge(resource.Environment(), r)
365 if err != nil {
366 otel.Handle(err)
367 }
368 return cfg
369 })
370}
371
372// WithIDGenerator returns a TracerProviderOption that will configure the
373// IDGenerator g as a TracerProvider's IDGenerator. The configured IDGenerator
374// is used by the Tracers the TracerProvider creates to generate new Span and
375// Trace IDs.
376//
377// If this option is not used, the TracerProvider will use a random number
378// IDGenerator by default.
379func WithIDGenerator(g IDGenerator) TracerProviderOption {
380 return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
381 if g != nil {
382 cfg.idGenerator = g
383 }
384 return cfg
385 })
386}
387
388// WithSampler returns a TracerProviderOption that will configure the Sampler
389// s as a TracerProvider's Sampler. The configured Sampler is used by the
390// Tracers the TracerProvider creates to make their sampling decisions for the
391// Spans they create.
392//
393// This option overrides the Sampler configured through the OTEL_TRACES_SAMPLER
394// and OTEL_TRACES_SAMPLER_ARG environment variables. If this option is not used
395// and the sampler is not configured through environment variables or the environment
396// contains invalid/unsupported configuration, the TracerProvider will use a
397// ParentBased(AlwaysSample) Sampler by default.
398func WithSampler(s Sampler) TracerProviderOption {
399 return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
400 if s != nil {
401 cfg.sampler = s
402 }
403 return cfg
404 })
405}
406
407// WithSpanLimits returns a TracerProviderOption that configures a
408// TracerProvider to use the SpanLimits sl. These SpanLimits bound any Span
409// created by a Tracer from the TracerProvider.
410//
411// If any field of sl is zero or negative it will be replaced with the default
412// value for that field.
413//
414// If this or WithRawSpanLimits are not provided, the TracerProvider will use
415// the limits defined by environment variables, or the defaults if unset.
416// Refer to the NewSpanLimits documentation for information about this
417// relationship.
418//
419// Deprecated: Use WithRawSpanLimits instead which allows setting unlimited
420// and zero limits. This option will be kept until the next major version
421// incremented release.
422func WithSpanLimits(sl SpanLimits) TracerProviderOption {
423 if sl.AttributeValueLengthLimit <= 0 {
424 sl.AttributeValueLengthLimit = DefaultAttributeValueLengthLimit
425 }
426 if sl.AttributeCountLimit <= 0 {
427 sl.AttributeCountLimit = DefaultAttributeCountLimit
428 }
429 if sl.EventCountLimit <= 0 {
430 sl.EventCountLimit = DefaultEventCountLimit
431 }
432 if sl.AttributePerEventCountLimit <= 0 {
433 sl.AttributePerEventCountLimit = DefaultAttributePerEventCountLimit
434 }
435 if sl.LinkCountLimit <= 0 {
436 sl.LinkCountLimit = DefaultLinkCountLimit
437 }
438 if sl.AttributePerLinkCountLimit <= 0 {
439 sl.AttributePerLinkCountLimit = DefaultAttributePerLinkCountLimit
440 }
441 return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
442 cfg.spanLimits = sl
443 return cfg
444 })
445}
446
447// WithRawSpanLimits returns a TracerProviderOption that configures a
448// TracerProvider to use these limits. These limits bound any Span created by
449// a Tracer from the TracerProvider.
450//
451// The limits will be used as-is. Zero or negative values will not be changed
452// to the default value like WithSpanLimits does. Setting a limit to zero will
453// effectively disable the related resource it limits and setting to a
454// negative value will mean that resource is unlimited. Consequentially, this
455// means that the zero-value SpanLimits will disable all span resources.
456// Because of this, limits should be constructed using NewSpanLimits and
457// updated accordingly.
458//
459// If this or WithSpanLimits are not provided, the TracerProvider will use the
460// limits defined by environment variables, or the defaults if unset. Refer to
461// the NewSpanLimits documentation for information about this relationship.
462func WithRawSpanLimits(limits SpanLimits) TracerProviderOption {
463 return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
464 cfg.spanLimits = limits
465 return cfg
466 })
467}
468
469func applyTracerProviderEnvConfigs(cfg tracerProviderConfig) tracerProviderConfig {
470 for _, opt := range tracerProviderOptionsFromEnv() {
471 cfg = opt.apply(cfg)
472 }
473
474 return cfg
475}
476
477func tracerProviderOptionsFromEnv() []TracerProviderOption {
478 var opts []TracerProviderOption
479
480 sampler, err := samplerFromEnv()
481 if err != nil {
482 otel.Handle(err)
483 }
484
485 if sampler != nil {
486 opts = append(opts, WithSampler(sampler))
487 }
488
489 return opts
490}
491
492// ensureValidTracerProviderConfig ensures that given TracerProviderConfig is valid.
493func ensureValidTracerProviderConfig(cfg tracerProviderConfig) tracerProviderConfig {
494 if cfg.sampler == nil {
495 cfg.sampler = ParentBased(AlwaysSample())
496 }
497 if cfg.idGenerator == nil {
498 cfg.idGenerator = defaultIDGenerator()
499 }
500 if cfg.resource == nil {
501 cfg.resource = resource.Default()
502 }
503 return cfg
504}