9
9
//! not duplicate this data to avoid that different [`Tracer`] instances
10
10
//! of the [`TracerProvider`] have different versions of these data.
11
11
use crate :: runtime:: RuntimeChannel ;
12
- use crate :: trace:: { BatchSpanProcessor , SimpleSpanProcessor , Tracer } ;
12
+ use crate :: trace:: {
13
+ BatchSpanProcessor , Config , RandomIdGenerator , Sampler , SimpleSpanProcessor , SpanLimits , Tracer ,
14
+ } ;
13
15
use crate :: { export:: trace:: SpanExporter , trace:: SpanProcessor } ;
14
16
use crate :: { InstrumentationLibrary , Resource } ;
15
- use once_cell:: sync:: OnceCell ;
17
+ use once_cell:: sync:: { Lazy , OnceCell } ;
18
+ use opentelemetry:: trace:: TraceError ;
16
19
use opentelemetry:: { global, trace:: TraceResult } ;
17
20
use std:: borrow:: Cow ;
21
+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
18
22
use std:: sync:: Arc ;
19
23
20
24
/// Default tracer name if empty string is provided.
21
25
const DEFAULT_COMPONENT_NAME : & str = "rust.opentelemetry.io/sdk/tracer" ;
22
26
static PROVIDER_RESOURCE : OnceCell < Resource > = OnceCell :: new ( ) ;
23
27
28
+ // a no nop tracer provider used as placeholder when the provider is shutdown
29
+ static NOOP_TRACER_PROVIDER : Lazy < TracerProvider > = Lazy :: new ( || TracerProvider {
30
+ inner : Arc :: new ( TracerProviderInner {
31
+ processors : Vec :: new ( ) ,
32
+ config : Config {
33
+ // cannot use default here as the default resource is not empty
34
+ sampler : Box :: new ( Sampler :: ParentBased ( Box :: new ( Sampler :: AlwaysOn ) ) ) ,
35
+ id_generator : Box :: < RandomIdGenerator > :: default ( ) ,
36
+ span_limits : SpanLimits :: default ( ) ,
37
+ resource : Cow :: Owned ( Resource :: empty ( ) ) ,
38
+ } ,
39
+ } ) ,
40
+ is_shutdown : Arc :: new ( AtomicBool :: new ( true ) ) ,
41
+ } ) ;
42
+
24
43
/// TracerProvider inner type
25
44
#[ derive( Debug ) ]
26
45
pub ( crate ) struct TracerProviderInner {
@@ -39,9 +58,14 @@ impl Drop for TracerProviderInner {
39
58
}
40
59
41
60
/// Creator and registry of named [`Tracer`] instances.
61
+ ///
62
+ /// `TracerProvider` is lightweight container holding pointers to `SpanProcessor` and other components.
63
+ /// Cloning and dropping them will not stop the span processing. To stop span processing, users
64
+ /// must either call `shutdown` method explicitly, or drop every clone of `TracerProvider`.
42
65
#[ derive( Clone , Debug ) ]
43
66
pub struct TracerProvider {
44
67
inner : Arc < TracerProviderInner > ,
68
+ is_shutdown : Arc < AtomicBool > ,
45
69
}
46
70
47
71
impl Default for TracerProvider {
@@ -52,8 +76,11 @@ impl Default for TracerProvider {
52
76
53
77
impl TracerProvider {
54
78
/// Build a new tracer provider
55
- pub ( crate ) fn new ( inner : Arc < TracerProviderInner > ) -> Self {
56
- TracerProvider { inner }
79
+ pub ( crate ) fn new ( inner : TracerProviderInner ) -> Self {
80
+ TracerProvider {
81
+ inner : Arc :: new ( inner) ,
82
+ is_shutdown : Arc :: new ( AtomicBool :: new ( false ) ) ,
83
+ }
57
84
}
58
85
59
86
/// Create a new [`TracerProvider`] builder.
@@ -71,6 +98,12 @@ impl TracerProvider {
71
98
& self . inner . config
72
99
}
73
100
101
+ /// true if the provider has been shutdown
102
+ /// Don't start span or export spans when provider is shutdown
103
+ pub ( crate ) fn is_shutdown ( & self ) -> bool {
104
+ self . is_shutdown . load ( Ordering :: Relaxed )
105
+ }
106
+
74
107
/// Force flush all remaining spans in span processors and return results.
75
108
///
76
109
/// # Examples
@@ -114,11 +147,41 @@ impl TracerProvider {
114
147
. map ( |processor| processor. force_flush ( ) )
115
148
. collect ( )
116
149
}
150
+
151
+ /// Shuts down the current `TracerProvider`.
152
+ ///
153
+ /// Note that shut down doesn't means the TracerProvider has dropped
154
+ pub fn shutdown ( & self ) -> TraceResult < ( ) > {
155
+ if self
156
+ . is_shutdown
157
+ . compare_exchange ( false , true , Ordering :: SeqCst , Ordering :: SeqCst )
158
+ . is_ok ( )
159
+ {
160
+ // propagate the shutdown signal to processors
161
+ // it's up to the processor to properly block new spans after shutdown
162
+ let mut errs = vec ! [ ] ;
163
+ for processor in & self . inner . processors {
164
+ if let Err ( err) = processor. shutdown ( ) {
165
+ errs. push ( err) ;
166
+ }
167
+ }
168
+
169
+ if errs. is_empty ( ) {
170
+ Ok ( ( ) )
171
+ } else {
172
+ Err ( TraceError :: Other ( format ! ( "{errs:?}" ) . into ( ) ) )
173
+ }
174
+ } else {
175
+ Err ( TraceError :: Other (
176
+ "tracer provider already shut down" . into ( ) ,
177
+ ) )
178
+ }
179
+ }
117
180
}
118
181
119
182
impl opentelemetry:: trace:: TracerProvider for TracerProvider {
120
183
/// This implementation of `TracerProvider` produces `Tracer` instances.
121
- type Tracer = crate :: trace :: Tracer ;
184
+ type Tracer = Tracer ;
122
185
123
186
/// Create a new versioned `Tracer` instance.
124
187
fn versioned_tracer (
@@ -152,7 +215,10 @@ impl opentelemetry::trace::TracerProvider for TracerProvider {
152
215
}
153
216
154
217
fn library_tracer ( & self , library : Arc < InstrumentationLibrary > ) -> Self :: Tracer {
155
- Tracer :: new ( library, Arc :: downgrade ( & self . inner ) )
218
+ if self . is_shutdown . load ( Ordering :: Relaxed ) {
219
+ return Tracer :: new ( library, NOOP_TRACER_PROVIDER . clone ( ) ) ;
220
+ }
221
+ Tracer :: new ( library, self . clone ( ) )
156
222
}
157
223
}
158
224
@@ -226,9 +292,7 @@ impl Builder {
226
292
p. set_resource ( config. resource . as_ref ( ) ) ;
227
293
}
228
294
229
- TracerProvider {
230
- inner : Arc :: new ( TracerProviderInner { processors, config } ) ,
231
- }
295
+ TracerProvider :: new ( TracerProviderInner { processors, config } )
232
296
}
233
297
}
234
298
@@ -241,24 +305,59 @@ mod tests {
241
305
use crate :: trace:: provider:: TracerProviderInner ;
242
306
use crate :: trace:: { Config , Span , SpanProcessor } ;
243
307
use crate :: Resource ;
244
- use opentelemetry:: trace:: { TraceError , TraceResult } ;
308
+ use opentelemetry:: trace:: { TraceError , TraceResult , Tracer , TracerProvider } ;
245
309
use opentelemetry:: { Context , Key , KeyValue , Value } ;
246
310
use std:: borrow:: Cow ;
247
311
use std:: env;
312
+ use std:: sync:: atomic:: { AtomicBool , AtomicU32 , Ordering } ;
248
313
use std:: sync:: Arc ;
249
314
315
+ // fields below is wrapped with Arc so we can assert it
316
+ #[ derive( Default , Debug ) ]
317
+ struct AssertInfo {
318
+ started_span : AtomicU32 ,
319
+ is_shutdown : AtomicBool ,
320
+ }
321
+
322
+ #[ derive( Default , Debug , Clone ) ]
323
+ struct SharedAssertInfo ( Arc < AssertInfo > ) ;
324
+
325
+ impl SharedAssertInfo {
326
+ fn started_span_count ( & self , count : u32 ) -> bool {
327
+ self . 0 . started_span . load ( Ordering :: SeqCst ) == count
328
+ }
329
+ }
330
+
250
331
#[ derive( Debug ) ]
251
332
struct TestSpanProcessor {
252
333
success : bool ,
334
+ assert_info : SharedAssertInfo ,
335
+ }
336
+
337
+ impl TestSpanProcessor {
338
+ fn new ( success : bool ) -> TestSpanProcessor {
339
+ TestSpanProcessor {
340
+ success,
341
+ assert_info : SharedAssertInfo :: default ( ) ,
342
+ }
343
+ }
344
+
345
+ // get handle to assert info
346
+ fn assert_info ( & self ) -> SharedAssertInfo {
347
+ self . assert_info . clone ( )
348
+ }
253
349
}
254
350
255
351
impl SpanProcessor for TestSpanProcessor {
256
352
fn on_start ( & self , _span : & mut Span , _cx : & Context ) {
257
- unimplemented ! ( )
353
+ self . assert_info
354
+ . 0
355
+ . started_span
356
+ . fetch_add ( 1 , Ordering :: SeqCst ) ;
258
357
}
259
358
260
359
fn on_end ( & self , _span : SpanData ) {
261
- unimplemented ! ( )
360
+ // ignore
262
361
}
263
362
264
363
fn force_flush ( & self ) -> TraceResult < ( ) > {
@@ -270,19 +369,29 @@ mod tests {
270
369
}
271
370
272
371
fn shutdown ( & self ) -> TraceResult < ( ) > {
273
- self . force_flush ( )
372
+ if self . assert_info . 0 . is_shutdown . load ( Ordering :: SeqCst ) {
373
+ Ok ( ( ) )
374
+ } else {
375
+ let _ = self . assert_info . 0 . is_shutdown . compare_exchange (
376
+ false ,
377
+ true ,
378
+ Ordering :: SeqCst ,
379
+ Ordering :: SeqCst ,
380
+ ) ;
381
+ self . force_flush ( )
382
+ }
274
383
}
275
384
}
276
385
277
386
#[ test]
278
387
fn test_force_flush ( ) {
279
- let tracer_provider = super :: TracerProvider :: new ( Arc :: from ( TracerProviderInner {
388
+ let tracer_provider = super :: TracerProvider :: new ( TracerProviderInner {
280
389
processors : vec ! [
281
- Box :: from( TestSpanProcessor { success : true } ) ,
282
- Box :: from( TestSpanProcessor { success : false } ) ,
390
+ Box :: from( TestSpanProcessor :: new ( true ) ) ,
391
+ Box :: from( TestSpanProcessor :: new ( false ) ) ,
283
392
] ,
284
393
config : Default :: default ( ) ,
285
- } ) ) ;
394
+ } ) ;
286
395
287
396
let results = tracer_provider. force_flush ( ) ;
288
397
assert_eq ! ( results. len( ) , 2 ) ;
@@ -417,4 +526,42 @@ mod tests {
417
526
418
527
assert_eq ! ( no_service_name. config( ) . resource. len( ) , 0 )
419
528
}
529
+
530
+ #[ test]
531
+ fn test_shutdown_noops ( ) {
532
+ let processor = TestSpanProcessor :: new ( false ) ;
533
+ let assert_handle = processor. assert_info ( ) ;
534
+ let tracer_provider = super :: TracerProvider :: new ( TracerProviderInner {
535
+ processors : vec ! [ Box :: from( processor) ] ,
536
+ config : Default :: default ( ) ,
537
+ } ) ;
538
+
539
+ let test_tracer_1 = tracer_provider. tracer ( "test1" ) ;
540
+ let _ = test_tracer_1. start ( "test" ) ;
541
+
542
+ assert ! ( assert_handle. started_span_count( 1 ) ) ;
543
+
544
+ let _ = test_tracer_1. start ( "test" ) ;
545
+
546
+ assert ! ( assert_handle. started_span_count( 2 ) ) ;
547
+
548
+ let shutdown = |tracer_provider : super :: TracerProvider | {
549
+ let _ = tracer_provider. shutdown ( ) ; // shutdown once
550
+ } ;
551
+
552
+ // assert tracer provider can be shutdown using on a cloned version
553
+ shutdown ( tracer_provider. clone ( ) ) ;
554
+
555
+ // after shutdown we should get noop tracer
556
+ let noop_tracer = tracer_provider. tracer ( "noop" ) ;
557
+ // noop tracer cannot start anything
558
+ let _ = noop_tracer. start ( "test" ) ;
559
+ assert ! ( assert_handle. started_span_count( 2 ) ) ;
560
+ // noop tracer's tracer provider should be shutdown
561
+ assert ! ( noop_tracer. provider( ) . is_shutdown. load( Ordering :: SeqCst ) ) ;
562
+
563
+ // existing tracer becomes noops after shutdown
564
+ let _ = test_tracer_1. start ( "test" ) ;
565
+ assert ! ( assert_handle. started_span_count( 2 ) ) ;
566
+ }
420
567
}
0 commit comments