1
2
3
4
5
6
7
8
9
10
11
12
13
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")
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
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)
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)
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