@@ -69,12 +69,15 @@ pub struct TransactionContext {
6969 op : String ,
7070 trace_id : protocol:: TraceId ,
7171 parent_span_id : Option < protocol:: SpanId > ,
72+ span_id : protocol:: SpanId ,
7273 sampled : Option < bool > ,
7374 custom : Option < CustomTransactionContext > ,
7475}
7576
7677impl TransactionContext {
77- /// Creates a new Transaction Context with the given `name` and `op`.
78+ /// Creates a new Transaction Context with the given `name` and `op`. A random
79+ /// `trace_id` is assigned. Use [`TransactionContext::new_with_trace_id`] to
80+ /// specify a custom trace ID.
7881 ///
7982 /// See <https://docs.sentry.io/platforms/native/enriching-events/transaction-name/>
8083 /// for an explanation of a Transaction's `name`, and
@@ -85,13 +88,33 @@ impl TransactionContext {
8588 /// can be used for distributed tracing.
8689 #[ must_use = "this must be used with `start_transaction`" ]
8790 pub fn new ( name : & str , op : & str ) -> Self {
88- Self :: continue_from_headers ( name, op, std:: iter:: empty ( ) )
91+ Self :: new_with_trace_id ( name, op, protocol:: TraceId :: default ( ) )
92+ }
93+
94+ /// Creates a new Transaction Context with the given `name`, `op`, and `trace_id`.
95+ ///
96+ /// See <https://docs.sentry.io/platforms/native/enriching-events/transaction-name/>
97+ /// for an explanation of a Transaction's `name`, and
98+ /// <https://develop.sentry.dev/sdk/performance/span-operations/> for conventions
99+ /// around an `operation`'s value.
100+ #[ must_use = "this must be used with `start_transaction`" ]
101+ pub fn new_with_trace_id ( name : & str , op : & str , trace_id : protocol:: TraceId ) -> Self {
102+ Self {
103+ name : name. into ( ) ,
104+ op : op. into ( ) ,
105+ trace_id,
106+ parent_span_id : None ,
107+ span_id : Default :: default ( ) ,
108+ sampled : None ,
109+ custom : None ,
110+ }
89111 }
90112
91113 /// Creates a new Transaction Context based on the distributed tracing `headers`.
92114 ///
93- /// The `headers` in particular need to include the `sentry-trace` header,
94- /// which is used to associate the transaction with a distributed trace.
115+ /// The `headers` in particular need to include either the `sentry-trace` or W3C
116+ /// `traceparent` header, which is used to associate the transaction with a distributed
117+ /// trace. If both are present, `sentry-trace` takes precedence.
95118 #[ must_use = "this must be used with `start_transaction`" ]
96119 pub fn continue_from_headers < ' a , I : IntoIterator < Item = ( & ' a str , & ' a str ) > > (
97120 name : & str ,
@@ -102,6 +125,11 @@ impl TransactionContext {
102125 for ( k, v) in headers. into_iter ( ) {
103126 if k. eq_ignore_ascii_case ( "sentry-trace" ) {
104127 trace = parse_sentry_trace ( v) ;
128+ break ;
129+ }
130+
131+ if k. eq_ignore_ascii_case ( "traceparent" ) {
132+ trace = parse_w3c_traceparent ( v) ;
105133 }
106134 }
107135
@@ -115,6 +143,7 @@ impl TransactionContext {
115143 op : op. into ( ) ,
116144 trace_id,
117145 parent_span_id,
146+ span_id : Default :: default ( ) ,
118147 sampled,
119148 custom : None ,
120149 }
@@ -152,6 +181,7 @@ impl TransactionContext {
152181 op : op. into ( ) ,
153182 trace_id,
154183 parent_span_id : Some ( parent_span_id) ,
184+ span_id : protocol:: SpanId :: default ( ) ,
155185 sampled,
156186 custom : None ,
157187 }
@@ -185,6 +215,11 @@ impl TransactionContext {
185215 self . trace_id
186216 }
187217
218+ /// Get the Span ID of this Transaction.
219+ pub fn span_id ( & self ) -> protocol:: SpanId {
220+ self . span_id
221+ }
222+
188223 /// Get the custom context of this Transaction.
189224 pub fn custom ( & self ) -> Option < & CustomTransactionContext > {
190225 self . custom . as_ref ( )
@@ -220,6 +255,80 @@ impl TransactionContext {
220255 std:: mem:: swap ( & mut self . custom , & mut Some ( custom) ) ;
221256 existing_value
222257 }
258+
259+ /// Creates a transaction context builder initialized with the given `name` and `op`.
260+ ///
261+ /// See <https://docs.sentry.io/platforms/native/enriching-events/transaction-name/>
262+ /// for an explanation of a Transaction's `name`, and
263+ /// <https://develop.sentry.dev/sdk/performance/span-operations/> for conventions
264+ /// around an `operation`'s value.
265+ #[ must_use]
266+ pub fn builder ( name : & str , op : & str ) -> TransactionContextBuilder {
267+ TransactionContextBuilder {
268+ ctx : TransactionContext :: new ( name, op) ,
269+ }
270+ }
271+ }
272+
273+ /// A transaction context builder created by [`TransactionContext::builder`].
274+ pub struct TransactionContextBuilder {
275+ ctx : TransactionContext ,
276+ }
277+
278+ impl TransactionContextBuilder {
279+ /// Defines the name of the transaction.
280+ #[ must_use]
281+ pub fn with_name ( mut self , name : String ) -> Self {
282+ self . ctx . name = name;
283+ self
284+ }
285+
286+ /// Defines the operation of the transaction.
287+ #[ must_use]
288+ pub fn with_op ( mut self , op : String ) -> Self {
289+ self . ctx . op = op;
290+ self
291+ }
292+
293+ /// Defines the trace ID.
294+ #[ must_use]
295+ pub fn with_trace_id ( mut self , trace_id : protocol:: TraceId ) -> Self {
296+ self . ctx . trace_id = trace_id;
297+ self
298+ }
299+
300+ /// Defines a parent span ID for the created transaction.
301+ #[ must_use]
302+ pub fn with_parent_span_id ( mut self , parent_span_id : Option < protocol:: SpanId > ) -> Self {
303+ self . ctx . parent_span_id = parent_span_id;
304+ self
305+ }
306+
307+ /// Defines the span ID to be used when creating the transaction.
308+ #[ must_use]
309+ pub fn with_span_id ( mut self , span_id : protocol:: SpanId ) -> Self {
310+ self . ctx . span_id = span_id;
311+ self
312+ }
313+
314+ /// Defines whether the transaction will be sampled.
315+ #[ must_use]
316+ pub fn with_sampled ( mut self , sampled : Option < bool > ) -> Self {
317+ self . ctx . sampled = sampled;
318+ self
319+ }
320+
321+ /// Adds a custom key and value to the transaction context.
322+ #[ must_use]
323+ pub fn with_custom ( mut self , key : String , value : serde_json:: Value ) -> Self {
324+ self . ctx . custom_insert ( key, value) ;
325+ self
326+ }
327+
328+ /// Finishes building a transaction.
329+ pub fn finish ( self ) -> TransactionContext {
330+ self . ctx
331+ }
223332}
224333
225334/// A function to be run for each new transaction, to determine the rate at which
@@ -467,6 +576,7 @@ impl Transaction {
467576 let context = protocol:: TraceContext {
468577 trace_id : ctx. trace_id ,
469578 parent_span_id : ctx. parent_span_id ,
579+ span_id : ctx. span_id ,
470580 op : Some ( ctx. op ) ,
471581 ..Default :: default ( )
472582 } ;
@@ -864,6 +974,23 @@ fn parse_sentry_trace(header: &str) -> Option<SentryTrace> {
864974 Some ( SentryTrace ( trace_id, parent_span_id, parent_sampled) )
865975}
866976
977+ /// Parses a W3C traceparent header.
978+ /// Reference: <https://w3c.github.io/trace-context/#traceparent-header-field-values>
979+ fn parse_w3c_traceparent ( header : & str ) -> Option < SentryTrace > {
980+ let header = header. trim ( ) ;
981+ let mut parts = header. splitn ( 4 , '-' ) ;
982+
983+ let _version = parts. next ( ) ?;
984+ let trace_id = parts. next ( ) ?. parse ( ) . ok ( ) ?;
985+ let parent_span_id = parts. next ( ) ?. parse ( ) . ok ( ) ?;
986+ let parent_sampled = parts
987+ . next ( )
988+ . and_then ( |sampled| u8:: from_str_radix ( sampled, 16 ) . ok ( ) )
989+ . map ( |flag| flag & 1 != 0 ) ;
990+
991+ Some ( SentryTrace ( trace_id, parent_span_id, parent_sampled) )
992+ }
993+
867994impl std:: fmt:: Display for SentryTrace {
868995 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
869996 write ! ( f, "{}-{}" , self . 0 , self . 1 ) ?;
@@ -896,6 +1023,26 @@ mod tests {
8961023 assert_eq ! ( parsed, Some ( trace) ) ;
8971024 }
8981025
1026+ #[ test]
1027+ fn parses_traceparent ( ) {
1028+ let trace_id = protocol:: TraceId :: from_str ( "4bf92f3577b34da6a3ce929d0e0e4736" ) . unwrap ( ) ;
1029+ let parent_trace_id = protocol:: SpanId :: from_str ( "00f067aa0ba902b7" ) . unwrap ( ) ;
1030+
1031+ let trace =
1032+ parse_w3c_traceparent ( "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" ) ;
1033+ assert_eq ! (
1034+ trace,
1035+ Some ( SentryTrace ( trace_id, parent_trace_id, Some ( true ) ) )
1036+ ) ;
1037+
1038+ let trace =
1039+ parse_w3c_traceparent ( "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00" ) ;
1040+ assert_eq ! (
1041+ trace,
1042+ Some ( SentryTrace ( trace_id, parent_trace_id, Some ( false ) ) )
1043+ ) ;
1044+ }
1045+
8991046 #[ test]
9001047 fn disabled_forwards_trace_id ( ) {
9011048 let headers = [ (
0 commit comments