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