trace.go raw

   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