...

Source file src/go.opentelemetry.io/otel/sdk/trace/provider.go

Documentation: go.opentelemetry.io/otel/sdk/trace

     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  	"go.opentelemetry.io/otel/trace/embedded"
    29  	"go.opentelemetry.io/otel/trace/noop"
    30  )
    31  
    32  const (
    33  	defaultTracerName = "go.opentelemetry.io/otel/sdk/tracer"
    34  )
    35  
    36  // tracerProviderConfig.
    37  type tracerProviderConfig struct {
    38  	// processors contains collection of SpanProcessors that are processing pipeline
    39  	// for spans in the trace signal.
    40  	// SpanProcessors registered with a TracerProvider and are called at the start
    41  	// and end of a Span's lifecycle, and are called in the order they are
    42  	// registered.
    43  	processors []SpanProcessor
    44  
    45  	// sampler is the default sampler used when creating new spans.
    46  	sampler Sampler
    47  
    48  	// idGenerator is used to generate all Span and Trace IDs when needed.
    49  	idGenerator IDGenerator
    50  
    51  	// spanLimits defines the attribute, event, and link limits for spans.
    52  	spanLimits SpanLimits
    53  
    54  	// resource contains attributes representing an entity that produces telemetry.
    55  	resource *resource.Resource
    56  }
    57  
    58  // MarshalLog is the marshaling function used by the logging system to represent this exporter.
    59  func (cfg tracerProviderConfig) MarshalLog() interface{} {
    60  	return struct {
    61  		SpanProcessors  []SpanProcessor
    62  		SamplerType     string
    63  		IDGeneratorType string
    64  		SpanLimits      SpanLimits
    65  		Resource        *resource.Resource
    66  	}{
    67  		SpanProcessors:  cfg.processors,
    68  		SamplerType:     fmt.Sprintf("%T", cfg.sampler),
    69  		IDGeneratorType: fmt.Sprintf("%T", cfg.idGenerator),
    70  		SpanLimits:      cfg.spanLimits,
    71  		Resource:        cfg.resource,
    72  	}
    73  }
    74  
    75  // TracerProvider is an OpenTelemetry TracerProvider. It provides Tracers to
    76  // instrumentation so it can trace operational flow through a system.
    77  type TracerProvider struct {
    78  	embedded.TracerProvider
    79  
    80  	mu             sync.Mutex
    81  	namedTracer    map[instrumentation.Scope]*tracer
    82  	spanProcessors atomic.Pointer[spanProcessorStates]
    83  
    84  	isShutdown atomic.Bool
    85  
    86  	// These fields are not protected by the lock mu. They are assumed to be
    87  	// immutable after creation of the TracerProvider.
    88  	sampler     Sampler
    89  	idGenerator IDGenerator
    90  	spanLimits  SpanLimits
    91  	resource    *resource.Resource
    92  }
    93  
    94  var _ trace.TracerProvider = &TracerProvider{}
    95  
    96  // NewTracerProvider returns a new and configured TracerProvider.
    97  //
    98  // By default the returned TracerProvider is configured with:
    99  //   - a ParentBased(AlwaysSample) Sampler
   100  //   - a random number IDGenerator
   101  //   - the resource.Default() Resource
   102  //   - the default SpanLimits.
   103  //
   104  // The passed opts are used to override these default values and configure the
   105  // returned TracerProvider appropriately.
   106  func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider {
   107  	o := tracerProviderConfig{
   108  		spanLimits: NewSpanLimits(),
   109  	}
   110  	o = applyTracerProviderEnvConfigs(o)
   111  
   112  	for _, opt := range opts {
   113  		o = opt.apply(o)
   114  	}
   115  
   116  	o = ensureValidTracerProviderConfig(o)
   117  
   118  	tp := &TracerProvider{
   119  		namedTracer: make(map[instrumentation.Scope]*tracer),
   120  		sampler:     o.sampler,
   121  		idGenerator: o.idGenerator,
   122  		spanLimits:  o.spanLimits,
   123  		resource:    o.resource,
   124  	}
   125  	global.Info("TracerProvider created", "config", o)
   126  
   127  	spss := make(spanProcessorStates, 0, len(o.processors))
   128  	for _, sp := range o.processors {
   129  		spss = append(spss, newSpanProcessorState(sp))
   130  	}
   131  	tp.spanProcessors.Store(&spss)
   132  
   133  	return tp
   134  }
   135  
   136  // Tracer returns a Tracer with the given name and options. If a Tracer for
   137  // the given name and options does not exist it is created, otherwise the
   138  // existing Tracer is returned.
   139  //
   140  // If name is empty, DefaultTracerName is used instead.
   141  //
   142  // This method is safe to be called concurrently.
   143  func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer {
   144  	// This check happens before the mutex is acquired to avoid deadlocking if Tracer() is called from within Shutdown().
   145  	if p.isShutdown.Load() {
   146  		return noop.NewTracerProvider().Tracer(name, opts...)
   147  	}
   148  	c := trace.NewTracerConfig(opts...)
   149  	if name == "" {
   150  		name = defaultTracerName
   151  	}
   152  	is := instrumentation.Scope{
   153  		Name:      name,
   154  		Version:   c.InstrumentationVersion(),
   155  		SchemaURL: c.SchemaURL(),
   156  	}
   157  
   158  	t, ok := func() (trace.Tracer, bool) {
   159  		p.mu.Lock()
   160  		defer p.mu.Unlock()
   161  		// Must check the flag after acquiring the mutex to avoid returning a valid tracer if Shutdown() ran
   162  		// after the first check above but before we acquired the mutex.
   163  		if p.isShutdown.Load() {
   164  			return noop.NewTracerProvider().Tracer(name, opts...), true
   165  		}
   166  		t, ok := p.namedTracer[is]
   167  		if !ok {
   168  			t = &tracer{
   169  				provider:             p,
   170  				instrumentationScope: is,
   171  			}
   172  			p.namedTracer[is] = t
   173  		}
   174  		return t, ok
   175  	}()
   176  	if !ok {
   177  		// This code is outside the mutex to not hold the lock while calling third party logging code:
   178  		// - That code may do slow things like I/O, which would prolong the duration the lock is held,
   179  		//   slowing down all tracing consumers.
   180  		// - Logging code may be instrumented with tracing and deadlock because it could try
   181  		//   acquiring the same non-reentrant mutex.
   182  		global.Info("Tracer created", "name", name, "version", is.Version, "schemaURL", is.SchemaURL)
   183  	}
   184  	return t
   185  }
   186  
   187  // RegisterSpanProcessor adds the given SpanProcessor to the list of SpanProcessors.
   188  func (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.
   208  func (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.
   253  func (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.
   276  func (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("%v; %v", retErr, err)
   306  			}
   307  		}
   308  	}
   309  	p.spanProcessors.Store(&spanProcessorStates{})
   310  	return retErr
   311  }
   312  
   313  func (p *TracerProvider) getSpanProcessors() spanProcessorStates {
   314  	return *(p.spanProcessors.Load())
   315  }
   316  
   317  // TracerProviderOption configures a TracerProvider.
   318  type TracerProviderOption interface {
   319  	apply(tracerProviderConfig) tracerProviderConfig
   320  }
   321  
   322  type traceProviderOptionFunc func(tracerProviderConfig) tracerProviderConfig
   323  
   324  func (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.
   336  func 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.
   342  func WithBatcher(e SpanExporter, opts ...BatchSpanProcessorOption) TracerProviderOption {
   343  	return WithSpanProcessor(NewBatchSpanProcessor(e, opts...))
   344  }
   345  
   346  // WithSpanProcessor registers the SpanProcessor with a TracerProvider.
   347  func 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.
   361  func 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.
   379  func 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.
   398  func 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.
   422  func 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.
   462  func WithRawSpanLimits(limits SpanLimits) TracerProviderOption {
   463  	return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
   464  		cfg.spanLimits = limits
   465  		return cfg
   466  	})
   467  }
   468  
   469  func applyTracerProviderEnvConfigs(cfg tracerProviderConfig) tracerProviderConfig {
   470  	for _, opt := range tracerProviderOptionsFromEnv() {
   471  		cfg = opt.apply(cfg)
   472  	}
   473  
   474  	return cfg
   475  }
   476  
   477  func 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.
   493  func 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  }
   505  

View as plain text