...

Source file src/go.opentelemetry.io/otel/internal/global/trace_test.go

Documentation: go.opentelemetry.io/otel/internal/global

     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 global
    16  
    17  import (
    18  	"context"
    19  	"sync/atomic"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/stretchr/testify/assert"
    24  
    25  	"go.opentelemetry.io/otel/trace"
    26  	"go.opentelemetry.io/otel/trace/embedded"
    27  	"go.opentelemetry.io/otel/trace/noop"
    28  )
    29  
    30  type fnTracerProvider struct {
    31  	embedded.TracerProvider
    32  
    33  	tracer func(string, ...trace.TracerOption) trace.Tracer
    34  }
    35  
    36  func (fn fnTracerProvider) Tracer(instrumentationName string, opts ...trace.TracerOption) trace.Tracer {
    37  	return fn.tracer(instrumentationName, opts...)
    38  }
    39  
    40  type fnTracer struct {
    41  	embedded.Tracer
    42  
    43  	start func(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span)
    44  }
    45  
    46  func (fn fnTracer) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
    47  	return fn.start(ctx, spanName, opts...)
    48  }
    49  
    50  func TestTraceProviderDelegation(t *testing.T) {
    51  	ResetForTest(t)
    52  
    53  	// Map of tracers to expected span names.
    54  	expected := map[string][]string{
    55  		"pre":      {"span2"},
    56  		"post":     {"span3"},
    57  		"fromSpan": {"span4"},
    58  	}
    59  
    60  	ctx := context.Background()
    61  	gtp := TracerProvider()
    62  	tracer1 := gtp.Tracer("pre")
    63  	// This is started before an SDK was registered and should be dropped.
    64  	_, span1 := tracer1.Start(ctx, "span1")
    65  
    66  	SetTracerProvider(fnTracerProvider{
    67  		tracer: func(name string, opts ...trace.TracerOption) trace.Tracer {
    68  			spans, ok := expected[name]
    69  			assert.Truef(t, ok, "invalid tracer: %s", name)
    70  			return fnTracer{
    71  				start: func(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
    72  					if ok {
    73  						if len(spans) == 0 {
    74  							t.Errorf("unexpected span: %s", spanName)
    75  						} else {
    76  							var want string
    77  							want, spans = spans[0], spans[1:]
    78  							assert.Equal(t, want, spanName)
    79  						}
    80  					}
    81  					return noop.NewTracerProvider().Tracer(name).Start(ctx, spanName)
    82  				},
    83  			}
    84  		},
    85  	})
    86  
    87  	// This span was started before initialization, it is expected to be dropped.
    88  	span1.End()
    89  
    90  	// The existing Tracer should have been configured to now use the configured SDK.
    91  	_, span2 := tracer1.Start(ctx, "span2")
    92  	span2.End()
    93  
    94  	// The global TracerProvider should now create Tracers that also use the newly configured SDK.
    95  	tracer2 := gtp.Tracer("post")
    96  	_, span3 := tracer2.Start(ctx, "span3")
    97  	span3.End()
    98  
    99  	// The noop-span should still provide access to a usable TracerProvider.
   100  	_, span4 := span1.TracerProvider().Tracer("fromSpan").Start(ctx, "span4")
   101  	span4.End()
   102  }
   103  
   104  func TestTraceProviderDelegates(t *testing.T) {
   105  	ResetForTest(t)
   106  
   107  	// Retrieve the placeholder TracerProvider.
   108  	gtp := TracerProvider()
   109  
   110  	// Configure it with a spy.
   111  	called := false
   112  	SetTracerProvider(fnTracerProvider{
   113  		tracer: func(name string, opts ...trace.TracerOption) trace.Tracer {
   114  			called = true
   115  			assert.Equal(t, "abc", name)
   116  			return noop.NewTracerProvider().Tracer("")
   117  		},
   118  	})
   119  
   120  	gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz"))
   121  	assert.True(t, called, "expected configured TraceProvider to be called")
   122  }
   123  
   124  func TestTraceProviderDelegatesConcurrentSafe(t *testing.T) {
   125  	ResetForTest(t)
   126  
   127  	// Retrieve the placeholder TracerProvider.
   128  	gtp := TracerProvider()
   129  
   130  	done := make(chan struct{})
   131  	quit := make(chan struct{})
   132  	go func() {
   133  		defer close(done)
   134  		for {
   135  			select {
   136  			case <-time.After(1 * time.Millisecond):
   137  				gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz"))
   138  			case <-quit:
   139  				return
   140  			}
   141  		}
   142  	}()
   143  
   144  	// Wait for the goroutine to make some calls before installing the provider.
   145  	<-time.After(100 * time.Millisecond)
   146  
   147  	// Configure it with a spy.
   148  	called := int32(0)
   149  	SetTracerProvider(fnTracerProvider{
   150  		tracer: func(name string, opts ...trace.TracerOption) trace.Tracer {
   151  			newVal := atomic.AddInt32(&called, 1)
   152  			assert.Equal(t, "abc", name)
   153  			if newVal == 10 {
   154  				// Signal the goroutine to finish.
   155  				close(quit)
   156  			}
   157  			return noop.NewTracerProvider().Tracer("")
   158  		},
   159  	})
   160  
   161  	// Wait for the go routine to finish
   162  	<-done
   163  
   164  	assert.LessOrEqual(t, int32(10), atomic.LoadInt32(&called), "expected configured TraceProvider to be called")
   165  }
   166  
   167  func TestTracerDelegatesConcurrentSafe(t *testing.T) {
   168  	ResetForTest(t)
   169  
   170  	// Retrieve the placeholder TracerProvider.
   171  	gtp := TracerProvider()
   172  	tracer := gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz"))
   173  
   174  	done := make(chan struct{})
   175  	quit := make(chan struct{})
   176  	go func() {
   177  		defer close(done)
   178  		for {
   179  			select {
   180  			case <-time.After(1 * time.Millisecond):
   181  				tracer.Start(context.Background(), "name")
   182  			case <-quit:
   183  				return
   184  			}
   185  		}
   186  	}()
   187  
   188  	// Wait for the goroutine to make some calls before installing the provider.
   189  	<-time.After(100 * time.Millisecond)
   190  
   191  	// Configure it with a spy.
   192  	called := int32(0)
   193  	SetTracerProvider(fnTracerProvider{
   194  		tracer: func(name string, opts ...trace.TracerOption) trace.Tracer {
   195  			assert.Equal(t, "abc", name)
   196  			return fnTracer{
   197  				start: func(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
   198  					newVal := atomic.AddInt32(&called, 1)
   199  					assert.Equal(t, "name", spanName)
   200  					if newVal == 10 {
   201  						// Signal the goroutine to finish.
   202  						close(quit)
   203  					}
   204  					return noop.NewTracerProvider().Tracer("").Start(ctx, spanName)
   205  				},
   206  			}
   207  		},
   208  	})
   209  
   210  	// Wait for the go routine to finish
   211  	<-done
   212  
   213  	assert.LessOrEqual(t, int32(10), atomic.LoadInt32(&called), "expected configured TraceProvider to be called")
   214  }
   215  
   216  func TestTraceProviderDelegatesSameInstance(t *testing.T) {
   217  	ResetForTest(t)
   218  
   219  	// Retrieve the placeholder TracerProvider.
   220  	gtp := TracerProvider()
   221  	tracer := gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz"))
   222  	assert.Same(t, tracer, gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz")))
   223  	assert.Same(t, tracer, gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz")))
   224  
   225  	SetTracerProvider(fnTracerProvider{
   226  		tracer: func(name string, opts ...trace.TracerOption) trace.Tracer {
   227  			return noop.NewTracerProvider().Tracer("")
   228  		},
   229  	})
   230  
   231  	assert.NotSame(t, tracer, gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz")))
   232  }
   233  
   234  func TestSpanContextPropagatedWithNonRecordingSpan(t *testing.T) {
   235  	ResetForTest(t)
   236  
   237  	sc := trace.NewSpanContext(trace.SpanContextConfig{
   238  		TraceID:    [16]byte{0x01},
   239  		SpanID:     [8]byte{0x01},
   240  		TraceFlags: trace.FlagsSampled,
   241  		Remote:     true,
   242  	})
   243  	ctx := trace.ContextWithSpanContext(context.Background(), sc)
   244  	_, span := TracerProvider().Tracer("test").Start(ctx, "test")
   245  
   246  	assert.Equal(t, sc, span.SpanContext())
   247  	assert.False(t, span.IsRecording())
   248  }
   249  

View as plain text