@@ -46,7 +46,7 @@ pub struct OpenTelemetryLayer<S, T> {
46
46
with_threads : bool ,
47
47
with_level : bool ,
48
48
sem_conv_config : SemConvConfig ,
49
- get_context : WithContext ,
49
+ with_context : WithContext ,
50
50
_registry : marker:: PhantomData < S > ,
51
51
}
52
52
@@ -81,26 +81,58 @@ where
81
81
OpenTelemetryLayer :: default ( )
82
82
}
83
83
84
- // this function "remembers" the types of the subscriber so that we
85
- // can downcast to something aware of them without knowing those
86
- // types at the callsite.
87
- //
88
- // See https://github.com/tokio-rs/tracing/blob/4dad420ee1d4607bad79270c1520673fa6266a3d/tracing-error/src/layer.rs
89
- pub ( crate ) struct WithContext (
84
+ ///
85
+ /// This struct lets us call back into the layer from the [crate::OpenTelemetrySpanExt] methods,
86
+ /// letting us access and mutate the underlying data on the layer side in the context of
87
+ /// tokio-tracing's span operations.
88
+ ///
89
+ /// The functions on this struct "remember" the types of the subscriber so that we
90
+ /// can downcast to something aware of them without knowing those
91
+ /// types at the callsite.
92
+ ///
93
+ /// See https://github.com/tokio-rs/tracing/blob/4dad420ee1d4607bad79270c1520673fa6266a3d/tracing-error/src/layer.rs
94
+ pub ( crate ) struct WithContext {
95
+ ///
96
+ /// Provides access to the OtelData associated with the given span ID.
97
+ ///
90
98
#[ allow( clippy:: type_complexity) ]
91
- fn ( & tracing:: Dispatch , & span:: Id , f : & mut dyn FnMut ( & mut OtelData ) ) ,
92
- ) ;
99
+ pub ( crate ) with_context : fn ( & tracing:: Dispatch , & span:: Id , f : & mut dyn FnMut ( & mut OtelData ) ) ,
100
+
101
+ ///
102
+ /// Ensures the given SpanId has been activated - that is, created in the OTel side of things,
103
+ /// and had its SpanBuilder consumed - and then provides access to the OtelData associated with it.
104
+ ///
105
+ #[ allow( clippy:: type_complexity) ]
106
+ pub ( crate ) with_activated_context :
107
+ fn ( & tracing:: Dispatch , & span:: Id , f : & mut dyn FnMut ( & mut OtelData ) ) ,
108
+ }
93
109
94
110
impl WithContext {
95
- // This function allows a function to be called in the context of the
96
- // "remembered" subscriber.
111
+ ///
112
+ /// Return the OtelData associated with the given spanId.
113
+ ///
97
114
pub ( crate ) fn with_context (
98
115
& self ,
99
116
dispatch : & tracing:: Dispatch ,
100
117
id : & span:: Id ,
101
118
mut f : impl FnMut ( & mut OtelData ) ,
102
119
) {
103
- ( self . 0 ) ( dispatch, id, & mut f)
120
+ ( self . with_context ) ( dispatch, id, & mut f)
121
+ }
122
+
123
+ ///
124
+ /// If the span associated with the given SpanId has not yet been
125
+ /// built, build it, consuming the span ID.
126
+ ///
127
+ /// Optionally performs additional operations on the OtelData after building.
128
+ ///
129
+ pub ( crate ) fn with_activated_context (
130
+ & self ,
131
+ dispatch : & tracing:: Dispatch ,
132
+ id : & span:: Id ,
133
+ mut f : impl FnMut ( & mut OtelData ) ,
134
+ ) {
135
+ ( self . with_activated_context ) ( dispatch, id, & mut f)
104
136
}
105
137
}
106
138
@@ -608,8 +640,10 @@ where
608
640
error_events_to_exceptions : true ,
609
641
error_events_to_status : true ,
610
642
} ,
611
-
612
- get_context : WithContext ( Self :: get_context) ,
643
+ with_context : WithContext {
644
+ with_context : Self :: get_context,
645
+ with_activated_context : Self :: get_activated_context,
646
+ } ,
613
647
_registry : marker:: PhantomData ,
614
648
}
615
649
}
@@ -659,7 +693,10 @@ where
659
693
with_threads : self . with_threads ,
660
694
with_level : self . with_level ,
661
695
sem_conv_config : self . sem_conv_config ,
662
- get_context : WithContext ( OpenTelemetryLayer :: < S , Tracer > :: get_context) ,
696
+ with_context : WithContext {
697
+ with_context : OpenTelemetryLayer :: < S , Tracer > :: get_context,
698
+ with_activated_context : OpenTelemetryLayer :: < S , Tracer > :: get_activated_context,
699
+ } ,
663
700
_registry : self . _registry ,
664
701
// cannot use ``..self` here due to different generics
665
702
}
@@ -858,6 +895,18 @@ where
858
895
}
859
896
}
860
897
898
+ /// Provides access to the OpenTelemetry data (`OtelData`) stored in a tracing span.
899
+ ///
900
+ /// This function retrieves the span from the subscriber's registry using the provided span ID,
901
+ /// and then applies the callback function `f` to the span's `OtelData` if present.
902
+ ///
903
+ /// # Parameters
904
+ /// * `dispatch` - A reference to the tracing dispatch, used to access the subscriber
905
+ /// * `id` - The ID of the span to look up
906
+ /// * `f` - A callback function that receives a mutable reference to the span's `OtelData`
907
+ /// This callback is used to manipulate or extract information from the OpenTelemetry context
908
+ /// associated with the tracing span
909
+ ///
861
910
fn get_context ( dispatch : & tracing:: Dispatch , id : & span:: Id , f : & mut dyn FnMut ( & mut OtelData ) ) {
862
911
let subscriber = dispatch
863
912
. downcast_ref :: < S > ( )
@@ -872,6 +921,30 @@ where
872
921
}
873
922
}
874
923
924
+ fn get_activated_context (
925
+ dispatch : & tracing:: Dispatch ,
926
+ id : & span:: Id ,
927
+ f : & mut dyn FnMut ( & mut OtelData ) ,
928
+ ) {
929
+ let subscriber = dispatch
930
+ . downcast_ref :: < S > ( )
931
+ . expect ( "subscriber should downcast to expected type; this is a bug!" ) ;
932
+ let span = subscriber
933
+ . span ( id)
934
+ . expect ( "registry should have a span for the current ID" ) ;
935
+
936
+ let layer = dispatch
937
+ . downcast_ref :: < OpenTelemetryLayer < S , T > > ( )
938
+ . expect ( "layer should downcast to expected type; this is a bug!" ) ;
939
+
940
+ let mut extensions = span. extensions_mut ( ) ;
941
+ if let Some ( otel_data) = extensions. get_mut :: < OtelData > ( ) {
942
+ // Activate the context
943
+ layer. start_cx ( otel_data) ;
944
+ f ( otel_data) ;
945
+ }
946
+ }
947
+
875
948
fn extra_span_attrs ( & self ) -> usize {
876
949
let mut extra_attrs = 0 ;
877
950
if self . location {
@@ -886,6 +959,10 @@ where
886
959
extra_attrs
887
960
}
888
961
962
+ ///
963
+ /// Builds the OTel span associated with given OTel context, consuming the SpanBuilder within
964
+ /// the context in the process.
965
+ ///
889
966
fn start_cx ( & self , otel_data : & mut OtelData ) {
890
967
if let Some ( builder) = otel_data. builder . take ( ) {
891
968
let span = builder. start_with_context ( & self . tracer , & otel_data. parent_cx ) ;
@@ -1225,6 +1302,7 @@ where
1225
1302
/// [`Span`]: opentelemetry::trace::Span
1226
1303
fn on_close ( & self , id : span:: Id , ctx : Context < ' _ , S > ) {
1227
1304
let span = ctx. span ( & id) . expect ( "Span not found, this is a bug" ) ;
1305
+ // Now get mutable extensions for removal
1228
1306
let ( otel_data, timings) = {
1229
1307
let mut extensions = span. extensions_mut ( ) ;
1230
1308
let timings = if self . tracked_inactivity {
@@ -1254,10 +1332,10 @@ where
1254
1332
let busy_ns = Key :: new ( "busy_ns" ) ;
1255
1333
let idle_ns = Key :: new ( "idle_ns" ) ;
1256
1334
1257
- let mut attributes = Vec :: with_capacity ( 2 ) ;
1258
- attributes . push ( KeyValue :: new ( busy_ns, timings. busy ) ) ;
1259
- attributes . push ( KeyValue :: new ( idle_ns, timings. idle ) ) ;
1260
- span . set_attributes ( attributes ) ;
1335
+ span . set_attributes ( vec ! [
1336
+ KeyValue :: new( busy_ns, timings. busy) ,
1337
+ KeyValue :: new( idle_ns, timings. idle) ,
1338
+ ] ) ;
1261
1339
}
1262
1340
1263
1341
if let Some ( end_time) = end_time {
@@ -1274,7 +1352,7 @@ where
1274
1352
match id {
1275
1353
id if id == TypeId :: of :: < Self > ( ) => Some ( self as * const _ as * const ( ) ) ,
1276
1354
id if id == TypeId :: of :: < WithContext > ( ) => {
1277
- Some ( & self . get_context as * const _ as * const ( ) )
1355
+ Some ( & self . with_context as * const _ as * const ( ) )
1278
1356
}
1279
1357
_ => None ,
1280
1358
}
@@ -1310,10 +1388,13 @@ fn thread_id_integer(id: thread::ThreadId) -> u64 {
1310
1388
1311
1389
#[ cfg( test) ]
1312
1390
mod tests {
1391
+ use crate :: OpenTelemetrySpanExt ;
1392
+
1313
1393
use super :: * ;
1314
1394
use opentelemetry:: trace:: { SpanContext , TraceFlags , TracerProvider } ;
1315
1395
use opentelemetry_sdk:: trace:: SpanExporter ;
1316
1396
use std:: { collections:: HashMap , error:: Error , fmt:: Display , time:: SystemTime } ;
1397
+ use tracing:: trace_span;
1317
1398
use tracing_subscriber:: prelude:: * ;
1318
1399
1319
1400
#[ derive( Debug , Clone ) ]
@@ -1918,4 +1999,102 @@ mod tests {
1918
1999
// The second tokio child span should have the otel span as parent
1919
2000
assert_eq ! ( child2. parent_span_id, otel. span_context. span_id( ) ) ;
1920
2001
}
2002
+
2003
+ #[ test]
2004
+ fn parent_context ( ) {
2005
+ let mut tracer = TestTracer :: default ( ) ;
2006
+ let subscriber = tracing_subscriber:: registry ( ) . with ( layer ( ) . with_tracer ( tracer. clone ( ) ) ) ;
2007
+
2008
+ tracing:: subscriber:: with_default ( subscriber, || {
2009
+ let root = trace_span ! ( "root" ) ;
2010
+
2011
+ let child1 = trace_span ! ( "child-1" ) ;
2012
+ let root_context = root. context ( ) ; // SpanData (None)
2013
+ child1. set_parent ( root_context) ; // Clone context, but SpanData(None)
2014
+
2015
+ let _enter_root = root. enter ( ) ;
2016
+ drop ( _enter_root) ;
2017
+
2018
+ let child2 = trace_span ! ( "child-2" ) ;
2019
+ child2. set_parent ( root. context ( ) ) ;
2020
+ } ) ;
2021
+
2022
+ // Let's check the spans
2023
+ let spans = tracer. spans ( ) ;
2024
+ let parent = spans. iter ( ) . find ( |span| span. name == "root" ) . unwrap ( ) ;
2025
+ let child1 = spans. iter ( ) . find ( |span| span. name == "child-1" ) . unwrap ( ) ;
2026
+ let child2 = spans. iter ( ) . find ( |span| span. name == "child-2" ) . unwrap ( ) ;
2027
+ assert_eq ! ( parent. parent_span_id, otel:: SpanId :: INVALID ) ;
2028
+ assert_eq ! ( child1. parent_span_id, parent. span_context. span_id( ) ) ;
2029
+ assert_eq ! ( child2. parent_span_id, parent. span_context. span_id( ) ) ;
2030
+ }
2031
+
2032
+ #[ test]
2033
+ fn record_after ( ) {
2034
+ let mut tracer = TestTracer :: default ( ) ;
2035
+ let subscriber = tracing_subscriber:: registry ( ) . with ( layer ( ) . with_tracer ( tracer. clone ( ) ) ) ;
2036
+
2037
+ tracing:: subscriber:: with_default ( subscriber, || {
2038
+ let root = trace_span ! ( "root" , before = "before" , after = "before" ) ;
2039
+
2040
+ // Record a value before the span is entered
2041
+ root. record ( "before" , "after" ) ;
2042
+
2043
+ // Enter and exit the span
2044
+ let _enter_root = root. enter ( ) ;
2045
+ drop ( _enter_root) ;
2046
+
2047
+ // Record a value after the span is exited
2048
+ root. record ( "after" , "after" ) ;
2049
+ } ) ;
2050
+
2051
+ // Let's check the spans. Both values should've been
2052
+ // updated to 'after'.
2053
+ let spans = tracer. spans ( ) ;
2054
+ let parent = spans. iter ( ) . find ( |span| span. name == "root" ) . unwrap ( ) ;
2055
+ assert_eq ! ( parent. parent_span_id, otel:: SpanId :: INVALID ) ;
2056
+ assert ! ( parent
2057
+ . attributes
2058
+ . iter( )
2059
+ . filter( |kv| kv. key. as_str( ) == "before" )
2060
+ . any( |kv| kv. value. as_str( ) == "after" ) ) ;
2061
+
2062
+ assert ! ( parent
2063
+ . attributes
2064
+ . iter( )
2065
+ . filter( |kv| kv. key. as_str( ) == "after" )
2066
+ . any( |kv| kv. value. as_str( ) == "after" ) ) ;
2067
+ }
2068
+
2069
+ #[ test]
2070
+ fn parent_context_2 ( ) {
2071
+ let mut tracer = TestTracer :: default ( ) ;
2072
+ let subscriber = tracing_subscriber:: registry ( ) . with ( layer ( ) . with_tracer ( tracer. clone ( ) ) ) ;
2073
+
2074
+ tracing:: subscriber:: with_default ( subscriber, || {
2075
+ let root = trace_span ! ( "root" ) ;
2076
+ _ = root. enter ( ) ;
2077
+
2078
+ let child1 = trace_span ! ( "child-1" ) ;
2079
+ child1. set_parent ( root. context ( ) ) ;
2080
+
2081
+ trace_span ! ( parent: & child1, "child-2" ) ;
2082
+ child1. set_parent ( root. context ( ) ) ; // <-- this is what causes the issue
2083
+
2084
+ trace_span ! ( parent: & child1, "child-3" ) ;
2085
+ } ) ;
2086
+
2087
+ // Let's check the spans
2088
+ let spans = tracer. spans ( ) ;
2089
+ let root = spans. iter ( ) . find ( |span| span. name == "root" ) . unwrap ( ) ;
2090
+ let child1 = spans. iter ( ) . find ( |span| span. name == "child-1" ) . unwrap ( ) ;
2091
+ let child2 = spans. iter ( ) . find ( |span| span. name == "child-2" ) . unwrap ( ) ;
2092
+ let child3 = spans. iter ( ) . find ( |span| span. name == "child-3" ) . unwrap ( ) ;
2093
+ assert_eq ! ( root. parent_span_id, otel:: SpanId :: INVALID ) ;
2094
+ assert_eq ! ( child1. parent_span_id, root. span_context. span_id( ) ) ;
2095
+ assert_eq ! ( child2. parent_span_id, child1. span_context. span_id( ) ) ;
2096
+
2097
+ // This is surprising, the parent should be `child1`, but is 'root'.
2098
+ assert_eq ! ( child3. parent_span_id, child1. span_context. span_id( ) ) ;
2099
+ }
1921
2100
}
0 commit comments