...

Source file src/go.opentelemetry.io/otel/sdk/trace/provider_test.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
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"fmt"
    21  	"math/rand"
    22  	"testing"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  
    27  	ottest "go.opentelemetry.io/otel/sdk/internal/internaltest"
    28  	"go.opentelemetry.io/otel/trace"
    29  )
    30  
    31  type basicSpanProcessor struct {
    32  	flushed             bool
    33  	closed              bool
    34  	injectShutdownError error
    35  }
    36  
    37  func (t *basicSpanProcessor) Shutdown(context.Context) error {
    38  	t.closed = true
    39  	return t.injectShutdownError
    40  }
    41  
    42  func (t *basicSpanProcessor) OnStart(context.Context, ReadWriteSpan) {}
    43  func (t *basicSpanProcessor) OnEnd(ReadOnlySpan)                     {}
    44  func (t *basicSpanProcessor) ForceFlush(context.Context) error {
    45  	t.flushed = true
    46  	return nil
    47  }
    48  
    49  type shutdownSpanProcessor struct {
    50  	shutdown func(context.Context) error
    51  }
    52  
    53  func (t *shutdownSpanProcessor) Shutdown(ctx context.Context) error {
    54  	return t.shutdown(ctx)
    55  }
    56  
    57  func (t *shutdownSpanProcessor) OnStart(context.Context, ReadWriteSpan) {}
    58  func (t *shutdownSpanProcessor) OnEnd(ReadOnlySpan)                     {}
    59  func (t *shutdownSpanProcessor) ForceFlush(context.Context) error {
    60  	return nil
    61  }
    62  
    63  func TestShutdownCallsTracerMethod(t *testing.T) {
    64  	stp := NewTracerProvider()
    65  	sp := &shutdownSpanProcessor{
    66  		shutdown: func(ctx context.Context) error {
    67  			_ = stp.Tracer("abc") // must not deadlock
    68  			return nil
    69  		},
    70  	}
    71  	stp.RegisterSpanProcessor(sp)
    72  	assert.NoError(t, stp.Shutdown(context.Background()))
    73  	assert.True(t, stp.isShutdown.Load())
    74  }
    75  
    76  func TestForceFlushAndShutdownTraceProviderWithoutProcessor(t *testing.T) {
    77  	stp := NewTracerProvider()
    78  	assert.NoError(t, stp.ForceFlush(context.Background()))
    79  	assert.NoError(t, stp.Shutdown(context.Background()))
    80  	assert.True(t, stp.isShutdown.Load())
    81  }
    82  
    83  func TestUnregisterFirst(t *testing.T) {
    84  	stp := NewTracerProvider()
    85  	sp1 := &basicSpanProcessor{}
    86  	sp2 := &basicSpanProcessor{}
    87  	sp3 := &basicSpanProcessor{}
    88  	stp.RegisterSpanProcessor(sp1)
    89  	stp.RegisterSpanProcessor(sp2)
    90  	stp.RegisterSpanProcessor(sp3)
    91  
    92  	stp.UnregisterSpanProcessor(sp1)
    93  
    94  	sps := stp.getSpanProcessors()
    95  	require.Len(t, sps, 2)
    96  	assert.Same(t, sp2, sps[0].sp)
    97  	assert.Same(t, sp3, sps[1].sp)
    98  }
    99  
   100  func TestUnregisterMiddle(t *testing.T) {
   101  	stp := NewTracerProvider()
   102  	sp1 := &basicSpanProcessor{}
   103  	sp2 := &basicSpanProcessor{}
   104  	sp3 := &basicSpanProcessor{}
   105  	stp.RegisterSpanProcessor(sp1)
   106  	stp.RegisterSpanProcessor(sp2)
   107  	stp.RegisterSpanProcessor(sp3)
   108  
   109  	stp.UnregisterSpanProcessor(sp2)
   110  
   111  	sps := stp.getSpanProcessors()
   112  	require.Len(t, sps, 2)
   113  	assert.Same(t, sp1, sps[0].sp)
   114  	assert.Same(t, sp3, sps[1].sp)
   115  }
   116  
   117  func TestUnregisterLast(t *testing.T) {
   118  	stp := NewTracerProvider()
   119  	sp1 := &basicSpanProcessor{}
   120  	sp2 := &basicSpanProcessor{}
   121  	sp3 := &basicSpanProcessor{}
   122  	stp.RegisterSpanProcessor(sp1)
   123  	stp.RegisterSpanProcessor(sp2)
   124  	stp.RegisterSpanProcessor(sp3)
   125  
   126  	stp.UnregisterSpanProcessor(sp3)
   127  
   128  	sps := stp.getSpanProcessors()
   129  	require.Len(t, sps, 2)
   130  	assert.Same(t, sp1, sps[0].sp)
   131  	assert.Same(t, sp2, sps[1].sp)
   132  }
   133  
   134  func TestShutdownTraceProvider(t *testing.T) {
   135  	stp := NewTracerProvider()
   136  	sp := &basicSpanProcessor{}
   137  	stp.RegisterSpanProcessor(sp)
   138  
   139  	assert.NoError(t, stp.ForceFlush(context.Background()))
   140  	assert.True(t, sp.flushed, "error ForceFlush basicSpanProcessor")
   141  	assert.NoError(t, stp.Shutdown(context.Background()))
   142  	assert.True(t, stp.isShutdown.Load())
   143  	assert.True(t, sp.closed, "error Shutdown basicSpanProcessor")
   144  }
   145  
   146  func TestFailedProcessorShutdown(t *testing.T) {
   147  	stp := NewTracerProvider()
   148  	spErr := errors.New("basic span processor shutdown failure")
   149  	sp := &basicSpanProcessor{
   150  		injectShutdownError: spErr,
   151  	}
   152  	stp.RegisterSpanProcessor(sp)
   153  
   154  	err := stp.Shutdown(context.Background())
   155  	assert.Error(t, err)
   156  	assert.Equal(t, err, spErr)
   157  	assert.True(t, stp.isShutdown.Load())
   158  }
   159  
   160  func TestFailedProcessorsShutdown(t *testing.T) {
   161  	stp := NewTracerProvider()
   162  	spErr1 := errors.New("basic span processor shutdown failure1")
   163  	spErr2 := errors.New("basic span processor shutdown failure2")
   164  	sp1 := &basicSpanProcessor{
   165  		injectShutdownError: spErr1,
   166  	}
   167  	sp2 := &basicSpanProcessor{
   168  		injectShutdownError: spErr2,
   169  	}
   170  	stp.RegisterSpanProcessor(sp1)
   171  	stp.RegisterSpanProcessor(sp2)
   172  
   173  	err := stp.Shutdown(context.Background())
   174  	assert.Error(t, err)
   175  	assert.EqualError(t, err, "basic span processor shutdown failure1; basic span processor shutdown failure2")
   176  	assert.True(t, sp1.closed)
   177  	assert.True(t, sp2.closed)
   178  	assert.True(t, stp.isShutdown.Load())
   179  }
   180  
   181  func TestFailedProcessorShutdownInUnregister(t *testing.T) {
   182  	handler.Reset()
   183  	stp := NewTracerProvider()
   184  	spErr := errors.New("basic span processor shutdown failure")
   185  	sp := &basicSpanProcessor{
   186  		injectShutdownError: spErr,
   187  	}
   188  	stp.RegisterSpanProcessor(sp)
   189  	stp.UnregisterSpanProcessor(sp)
   190  
   191  	assert.Contains(t, handler.errs, spErr)
   192  
   193  	err := stp.Shutdown(context.Background())
   194  	assert.NoError(t, err)
   195  	assert.True(t, stp.isShutdown.Load())
   196  }
   197  
   198  func TestSchemaURL(t *testing.T) {
   199  	stp := NewTracerProvider()
   200  	schemaURL := "https://opentelemetry.io/schemas/1.2.0"
   201  	tracerIface := stp.Tracer("tracername", trace.WithSchemaURL(schemaURL))
   202  
   203  	// Verify that the SchemaURL of the constructed Tracer is correctly populated.
   204  	tracerStruct := tracerIface.(*tracer)
   205  	assert.EqualValues(t, schemaURL, tracerStruct.instrumentationScope.SchemaURL)
   206  }
   207  
   208  func TestRegisterAfterShutdownWithoutProcessors(t *testing.T) {
   209  	stp := NewTracerProvider()
   210  	err := stp.Shutdown(context.Background())
   211  	assert.NoError(t, err)
   212  	assert.True(t, stp.isShutdown.Load())
   213  
   214  	sp := &basicSpanProcessor{}
   215  	stp.RegisterSpanProcessor(sp) // no-op
   216  	assert.Empty(t, stp.getSpanProcessors())
   217  }
   218  
   219  func TestRegisterAfterShutdownWithProcessors(t *testing.T) {
   220  	stp := NewTracerProvider()
   221  	sp1 := &basicSpanProcessor{}
   222  
   223  	stp.RegisterSpanProcessor(sp1)
   224  	err := stp.Shutdown(context.Background())
   225  	assert.NoError(t, err)
   226  	assert.True(t, stp.isShutdown.Load())
   227  	assert.Empty(t, stp.getSpanProcessors())
   228  
   229  	sp2 := &basicSpanProcessor{}
   230  	stp.RegisterSpanProcessor(sp2) // no-op
   231  	assert.Empty(t, stp.getSpanProcessors())
   232  }
   233  
   234  func TestTracerProviderSamplerConfigFromEnv(t *testing.T) {
   235  	type testCase struct {
   236  		sampler             string
   237  		samplerArg          string
   238  		argOptional         bool
   239  		description         string
   240  		errorType           error
   241  		invalidArgErrorType interface{}
   242  	}
   243  
   244  	randFloat := rand.Float64()
   245  
   246  	tests := []testCase{
   247  		{
   248  			sampler:             "invalid-sampler",
   249  			argOptional:         true,
   250  			description:         ParentBased(AlwaysSample()).Description(),
   251  			errorType:           errUnsupportedSampler("invalid-sampler"),
   252  			invalidArgErrorType: func() *errUnsupportedSampler { e := errUnsupportedSampler("invalid-sampler"); return &e }(),
   253  		},
   254  		{
   255  			sampler:     "always_on",
   256  			argOptional: true,
   257  			description: AlwaysSample().Description(),
   258  		},
   259  		{
   260  			sampler:     "always_off",
   261  			argOptional: true,
   262  			description: NeverSample().Description(),
   263  		},
   264  		{
   265  			sampler:     "traceidratio",
   266  			samplerArg:  fmt.Sprintf("%g", randFloat),
   267  			description: TraceIDRatioBased(randFloat).Description(),
   268  		},
   269  		{
   270  			sampler:     "traceidratio",
   271  			samplerArg:  fmt.Sprintf("%g", -randFloat),
   272  			description: TraceIDRatioBased(1.0).Description(),
   273  			errorType:   errNegativeTraceIDRatio,
   274  		},
   275  		{
   276  			sampler:     "traceidratio",
   277  			samplerArg:  fmt.Sprintf("%g", 1+randFloat),
   278  			description: TraceIDRatioBased(1.0).Description(),
   279  			errorType:   errGreaterThanOneTraceIDRatio,
   280  		},
   281  		{
   282  			sampler:             "traceidratio",
   283  			argOptional:         true,
   284  			description:         TraceIDRatioBased(1.0).Description(),
   285  			invalidArgErrorType: new(samplerArgParseError),
   286  		},
   287  		{
   288  			sampler:     "parentbased_always_on",
   289  			argOptional: true,
   290  			description: ParentBased(AlwaysSample()).Description(),
   291  		},
   292  		{
   293  			sampler:     "parentbased_always_off",
   294  			argOptional: true,
   295  			description: ParentBased(NeverSample()).Description(),
   296  		},
   297  		{
   298  			sampler:     "parentbased_traceidratio",
   299  			samplerArg:  fmt.Sprintf("%g", randFloat),
   300  			description: ParentBased(TraceIDRatioBased(randFloat)).Description(),
   301  		},
   302  		{
   303  			sampler:     "parentbased_traceidratio",
   304  			samplerArg:  fmt.Sprintf("%g", -randFloat),
   305  			description: ParentBased(TraceIDRatioBased(1.0)).Description(),
   306  			errorType:   errNegativeTraceIDRatio,
   307  		},
   308  		{
   309  			sampler:     "parentbased_traceidratio",
   310  			samplerArg:  fmt.Sprintf("%g", 1+randFloat),
   311  			description: ParentBased(TraceIDRatioBased(1.0)).Description(),
   312  			errorType:   errGreaterThanOneTraceIDRatio,
   313  		},
   314  		{
   315  			sampler:             "parentbased_traceidratio",
   316  			argOptional:         true,
   317  			description:         ParentBased(TraceIDRatioBased(1.0)).Description(),
   318  			invalidArgErrorType: new(samplerArgParseError),
   319  		},
   320  	}
   321  
   322  	handler.Reset()
   323  
   324  	for _, test := range tests {
   325  		t.Run(test.sampler, func(t *testing.T) {
   326  			envVars := map[string]string{
   327  				"OTEL_TRACES_SAMPLER": test.sampler,
   328  			}
   329  
   330  			if test.samplerArg != "" {
   331  				envVars["OTEL_TRACES_SAMPLER_ARG"] = test.samplerArg
   332  			}
   333  			envStore, err := ottest.SetEnvVariables(envVars)
   334  			require.NoError(t, err)
   335  			t.Cleanup(func() {
   336  				handler.Reset()
   337  				require.NoError(t, envStore.Restore())
   338  			})
   339  
   340  			stp := NewTracerProvider(WithSyncer(NewTestExporter()))
   341  			assert.Equal(t, test.description, stp.sampler.Description())
   342  			if test.errorType != nil {
   343  				testStoredError(t, test.errorType)
   344  			} else {
   345  				assert.Empty(t, handler.errs)
   346  			}
   347  
   348  			if test.argOptional {
   349  				t.Run("invalid sampler arg", func(t *testing.T) {
   350  					envStore, err := ottest.SetEnvVariables(map[string]string{
   351  						"OTEL_TRACES_SAMPLER":     test.sampler,
   352  						"OTEL_TRACES_SAMPLER_ARG": "invalid-ignored-string",
   353  					})
   354  					require.NoError(t, err)
   355  					t.Cleanup(func() {
   356  						handler.Reset()
   357  						require.NoError(t, envStore.Restore())
   358  					})
   359  
   360  					stp := NewTracerProvider(WithSyncer(NewTestExporter()))
   361  					t.Cleanup(func() {
   362  						require.NoError(t, stp.Shutdown(context.Background()))
   363  					})
   364  					assert.Equal(t, test.description, stp.sampler.Description())
   365  
   366  					if test.invalidArgErrorType != nil {
   367  						testStoredError(t, test.invalidArgErrorType)
   368  					} else {
   369  						assert.Empty(t, handler.errs)
   370  					}
   371  				})
   372  			}
   373  		})
   374  	}
   375  }
   376  
   377  func testStoredError(t *testing.T, target interface{}) {
   378  	t.Helper()
   379  
   380  	if assert.Len(t, handler.errs, 1) && assert.Error(t, handler.errs[0]) {
   381  		err := handler.errs[0]
   382  
   383  		require.Implements(t, (*error)(nil), target)
   384  		require.NotNil(t, target.(error))
   385  
   386  		defer handler.Reset()
   387  		if errors.Is(err, target.(error)) {
   388  			return
   389  		}
   390  
   391  		assert.ErrorAs(t, err, target)
   392  	}
   393  }
   394  

View as plain text