1+ #[ cfg( feature = "sentry" ) ]
2+ use std:: borrow:: ToOwned ;
13use std:: {
24 collections:: BTreeMap ,
35 sync:: { Arc , Mutex } ,
46} ;
57
68use once_cell:: sync:: OnceCell ;
7- use tracing:: { callsite:: DefaultCallsite , field:: FieldSet , Callsite } ;
9+ use tracing:: { callsite:: DefaultCallsite , debug , error , field:: FieldSet , Callsite } ;
810use tracing_core:: { identify_callsite, metadata:: Kind as MetadataKind } ;
911
1012/// Log an event.
@@ -96,6 +98,8 @@ fn span_or_event_enabled(callsite: &'static DefaultCallsite) -> bool {
9698#[ derive( uniffi:: Object ) ]
9799pub struct Span ( tracing:: Span ) ;
98100
101+ pub ( crate ) const BRIDGE_SPAN_NAME : & str = "<sdk_bridge_span>" ;
102+
99103#[ matrix_sdk_ffi_macros:: export]
100104impl Span {
101105 /// Create a span originating at the given callsite (file, line and column).
@@ -129,18 +133,41 @@ impl Span {
129133 level : LogLevel ,
130134 target : String ,
131135 name : String ,
136+ bridge_trace_id : Option < String > ,
132137 ) -> Arc < Self > {
133138 static CALLSITES : Mutex < BTreeMap < MetadataId , & ' static DefaultCallsite > > =
134139 Mutex :: new ( BTreeMap :: new ( ) ) ;
135140
136141 let loc = MetadataId { file, line, level, target, name : Some ( name) } ;
137- let callsite = get_or_init_metadata ( & CALLSITES , loc, & [ ] , MetadataKind :: SPAN ) ;
142+
143+ // If sentry isn't enabled, ignore bridge_trace_id's contents
144+ let bridge_trace_id = if cfg ! ( feature = "sentry" ) { bridge_trace_id } else { None } ;
145+
146+ let callsite = if cfg ! ( feature = "sentry" ) {
147+ get_or_init_metadata ( & CALLSITES , loc, & [ "sentry" , "sentry.trace" ] , MetadataKind :: SPAN )
148+ } else {
149+ get_or_init_metadata ( & CALLSITES , loc, & [ ] , MetadataKind :: SPAN )
150+ } ;
151+
138152 let metadata = callsite. metadata ( ) ;
139153
140154 let span = if span_or_event_enabled ( callsite) {
141155 // This function is hidden from docs, but we have to use it (see above).
142- let values = metadata. fields ( ) . value_set ( & [ ] ) ;
143- tracing:: Span :: new ( metadata, & values)
156+ let fields = metadata. fields ( ) ;
157+
158+ if let Some ( parent_trace_id) = bridge_trace_id {
159+ debug ! ( "Adding fields | sentry:true, sentry.trace={parent_trace_id}" ) ;
160+ let sentry_field = fields. field ( "sentry" ) . unwrap ( ) ;
161+ let sentry_trace_field = fields. field ( "sentry.trace" ) . unwrap ( ) ;
162+ #[ allow( trivial_casts) ] // The compiler is lying, it can't infer this cast
163+ let values = [
164+ ( & sentry_field, Some ( & true as & dyn tracing:: Value ) ) ,
165+ ( & sentry_trace_field, Some ( & parent_trace_id as & dyn tracing:: Value ) ) ,
166+ ] ;
167+ tracing:: Span :: new ( metadata, & fields. value_set ( & values) )
168+ } else {
169+ tracing:: Span :: new ( metadata, & fields. value_set ( & [ ] ) )
170+ }
144171 } else {
145172 tracing:: Span :: none ( )
146173 } ;
@@ -164,6 +191,27 @@ impl Span {
164191 fn is_none ( & self ) -> bool {
165192 self . 0 . is_none ( )
166193 }
194+
195+ /// Creates a [`Span`] that acts as a bridge between the client spans and
196+ /// the SDK ones, allowing them to be joined in Sentry. This function
197+ /// will only return a valid span if the `sentry` feature is enabled,
198+ /// otherwise it will return a noop span.
199+ #[ uniffi:: constructor]
200+ pub fn new_bridge_span ( target : String , parent_trace_id : Option < String > ) -> Arc < Self > {
201+ if cfg ! ( feature = "sentry" ) {
202+ Self :: new (
203+ "Bridge" . to_owned ( ) ,
204+ None ,
205+ LogLevel :: Info ,
206+ target,
207+ BRIDGE_SPAN_NAME . to_owned ( ) ,
208+ parent_trace_id,
209+ )
210+ } else {
211+ error ! ( "Sentry is not enabled!" ) ;
212+ Arc :: new ( Self ( tracing:: Span :: none ( ) ) )
213+ }
214+ }
167215}
168216
169217#[ derive( PartialEq , Eq , PartialOrd , Ord , Clone , Copy , uniffi:: Enum ) ]
0 commit comments