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