44use opentelemetry:: trace:: { TraceContextExt , TracerProvider as _} ;
55use opentelemetry_sdk:: trace:: SdkTracerProvider ;
66use opentelemetry_stdout as stdout;
7- use std:: sync:: { Arc , Mutex , RwLock } ;
7+ use std:: sync:: { Arc , Mutex , OnceLock } ;
88use tracing:: { debug, info, span, warn, Subscriber } ;
99use tracing:: { dispatcher:: WeakDispatch , level_filters:: LevelFilter , Dispatch } ;
10- use tracing_opentelemetry:: { layer , OpenTelemetryContext } ;
10+ use tracing_opentelemetry:: { get_otel_context , layer } ;
1111use tracing_subscriber:: layer:: Context ;
1212use tracing_subscriber:: prelude:: * ;
1313use tracing_subscriber:: registry:: LookupSpan ;
1414use tracing_subscriber:: Layer ;
1515
1616/// A custom layer that demonstrates how to use OpenTelemetryContext
1717/// to extract OpenTelemetry contexts from span extensions.
18- #[ derive( Clone ) ]
18+ #[ derive( Clone , Default ) ]
1919struct SpanAnalysisLayer {
2020 /// Store span analysis results for demonstration
2121 analysis_results : Arc < Mutex < Vec < SpanAnalysis > > > ,
2222 /// Weak reference to the dispatcher for context extraction
23- dispatch : Arc < RwLock < Option < WeakDispatch > > > ,
23+ dispatch : Arc < OnceLock < WeakDispatch > > ,
2424}
2525
2626#[ derive( Debug , Clone ) ]
@@ -32,13 +32,6 @@ struct SpanAnalysis {
3232}
3333
3434impl SpanAnalysisLayer {
35- fn new ( ) -> Self {
36- Self {
37- analysis_results : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
38- dispatch : Arc :: new ( RwLock :: new ( None ) ) ,
39- }
40- }
41-
4235 fn get_analysis_results ( & self ) -> Vec < SpanAnalysis > {
4336 self . analysis_results . lock ( ) . unwrap ( ) . clone ( )
4437 }
@@ -68,46 +61,23 @@ impl SpanAnalysisLayer {
6861 }
6962 }
7063 }
71-
72- fn get_weak_dispatch ( & self , get_default : bool ) -> Option < WeakDispatch > {
73- let read_guard = self . dispatch . read ( ) . unwrap ( ) ;
74- match read_guard. as_ref ( ) {
75- Some ( weak_dispatch) => Some ( weak_dispatch. clone ( ) ) ,
76- // Note: This workaround is needed until https://github.com/tokio-rs/tracing/pull/3379
77- // is merged and released. It should really be handled in on_register_dispatch
78- None => {
79- if !get_default {
80- None
81- } else {
82- drop ( read_guard) ;
83- let mut dispatch = self . dispatch . write ( ) . unwrap ( ) ;
84- let weak_dispatch = Dispatch :: default ( ) . downgrade ( ) ;
85- * dispatch = Some ( weak_dispatch. clone ( ) ) ;
86- Some ( weak_dispatch)
87- }
88- }
89- }
90- }
9164}
9265
9366impl < S > Layer < S > for SpanAnalysisLayer
9467where
9568 S : Subscriber + for < ' span > LookupSpan < ' span > ,
9669{
70+ fn on_register_dispatch ( & self , subscriber : & Dispatch ) {
71+ let _ = self . dispatch . set ( subscriber. downgrade ( ) ) ;
72+ }
73+
9774 fn on_new_span (
9875 & self ,
9976 attrs : & tracing:: span:: Attributes < ' _ > ,
10077 id : & tracing:: span:: Id ,
10178 ctx : Context < ' _ , S > ,
10279 ) {
103- // Get the weak dispatch reference.
104- //
105- // Note: We can't use the Dispatch::default() workaround described above here since this
106- // method is called from inside a dispatcher::get_default block, and such calls can't be
107- // nested so we would get the global dispatcher instead, which can't downcast to the right
108- // types when extracting the OpenTelemetry context. This also means that we will miss
109- // analyzing the first span that is created 🤷🏼♂️
110- let Some ( weak_dispatch) = self . get_weak_dispatch ( false ) else {
80+ let Some ( weak_dispatch) = self . dispatch . get ( ) else {
11181 return ;
11282 } ;
11383
11686 // This is the key functionality: using OpenTelemetryContext
11787 // to extract the OpenTelemetry context from span extensions
11888 let mut extensions = span_ref. extensions_mut ( ) ;
119- if let Some ( otel_context) =
120- OpenTelemetryContext :: context ( & mut extensions, & weak_dispatch. upgrade ( ) )
89+ if let Some ( otel_context) = get_otel_context ( & mut extensions, & weak_dispatch. upgrade ( ) )
12190 {
12291 self . analyze_span_context ( attrs. metadata ( ) . name ( ) , & otel_context) ;
12392 } else {
@@ -130,11 +99,11 @@ where
13099 }
131100
132101 fn on_enter ( & self , id : & tracing:: span:: Id , ctx : Context < ' _ , S > ) {
133- if let Some ( weak_dispatch) = self . get_weak_dispatch ( true ) {
102+ if let Some ( weak_dispatch) = self . dispatch . get ( ) {
134103 if let Some ( span_ref) = ctx. span ( id) {
135104 let mut extensions = span_ref. extensions_mut ( ) ;
136105 if let Some ( otel_context) =
137- OpenTelemetryContext :: context ( & mut extensions, & weak_dispatch. upgrade ( ) )
106+ get_otel_context ( & mut extensions, & weak_dispatch. upgrade ( ) )
138107 {
139108 let span = otel_context. span ( ) ;
140109 let span_context = span. span_context ( ) ;
@@ -149,13 +118,6 @@ where
149118 }
150119 }
151120 }
152-
153- fn on_register_dispatch ( & self , subscriber : & tracing:: Dispatch ) {
154- // Note: This does not work for Layer until https://github.com/tokio-rs/tracing/pull/3379
155- // is merged and released, since `on_register_dispatch` is never called.
156- let mut dispatch = self . dispatch . write ( ) . unwrap ( ) ;
157- * dispatch = Some ( subscriber. clone ( ) . downgrade ( ) ) ;
158- }
159121}
160122
161123fn setup_tracing ( ) -> ( impl Subscriber , SdkTracerProvider , SpanAnalysisLayer ) {
@@ -166,7 +128,7 @@ fn setup_tracing() -> (impl Subscriber, SdkTracerProvider, SpanAnalysisLayer) {
166128 let tracer = provider. tracer ( "span_ref_ext_example" ) ;
167129
168130 // Create our custom analysis layer
169- let analysis_layer = SpanAnalysisLayer :: new ( ) ;
131+ let analysis_layer = SpanAnalysisLayer :: default ( ) ;
170132
171133 // Build the subscriber with multiple layers:
172134 // 1. OpenTelemetry layer for trace export
0 commit comments