@@ -69,12 +69,15 @@ pub struct TransactionContext {
69
69
op : String ,
70
70
trace_id : protocol:: TraceId ,
71
71
parent_span_id : Option < protocol:: SpanId > ,
72
+ span_id : protocol:: SpanId ,
72
73
sampled : Option < bool > ,
73
74
custom : Option < CustomTransactionContext > ,
74
75
}
75
76
76
77
impl 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.
78
81
///
79
82
/// See <https://docs.sentry.io/platforms/native/enriching-events/transaction-name/>
80
83
/// for an explanation of a Transaction's `name`, and
@@ -85,13 +88,33 @@ impl TransactionContext {
85
88
/// can be used for distributed tracing.
86
89
#[ must_use = "this must be used with `start_transaction`" ]
87
90
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
+ }
89
111
}
90
112
91
113
/// Creates a new Transaction Context based on the distributed tracing `headers`.
92
114
///
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.
95
118
#[ must_use = "this must be used with `start_transaction`" ]
96
119
pub fn continue_from_headers < ' a , I : IntoIterator < Item = ( & ' a str , & ' a str ) > > (
97
120
name : & str ,
@@ -102,6 +125,11 @@ impl TransactionContext {
102
125
for ( k, v) in headers. into_iter ( ) {
103
126
if k. eq_ignore_ascii_case ( "sentry-trace" ) {
104
127
trace = parse_sentry_trace ( v) ;
128
+ break ;
129
+ }
130
+
131
+ if k. eq_ignore_ascii_case ( "traceparent" ) {
132
+ trace = parse_w3c_traceparent ( v) ;
105
133
}
106
134
}
107
135
@@ -115,6 +143,7 @@ impl TransactionContext {
115
143
op : op. into ( ) ,
116
144
trace_id,
117
145
parent_span_id,
146
+ span_id : Default :: default ( ) ,
118
147
sampled,
119
148
custom : None ,
120
149
}
@@ -152,6 +181,7 @@ impl TransactionContext {
152
181
op : op. into ( ) ,
153
182
trace_id,
154
183
parent_span_id : Some ( parent_span_id) ,
184
+ span_id : protocol:: SpanId :: default ( ) ,
155
185
sampled,
156
186
custom : None ,
157
187
}
@@ -185,6 +215,11 @@ impl TransactionContext {
185
215
self . trace_id
186
216
}
187
217
218
+ /// Get the Span ID of this Transaction.
219
+ pub fn span_id ( & self ) -> protocol:: SpanId {
220
+ self . span_id
221
+ }
222
+
188
223
/// Get the custom context of this Transaction.
189
224
pub fn custom ( & self ) -> Option < & CustomTransactionContext > {
190
225
self . custom . as_ref ( )
@@ -220,6 +255,80 @@ impl TransactionContext {
220
255
std:: mem:: swap ( & mut self . custom , & mut Some ( custom) ) ;
221
256
existing_value
222
257
}
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
+ }
223
332
}
224
333
225
334
/// A function to be run for each new transaction, to determine the rate at which
@@ -467,6 +576,7 @@ impl Transaction {
467
576
let context = protocol:: TraceContext {
468
577
trace_id : ctx. trace_id ,
469
578
parent_span_id : ctx. parent_span_id ,
579
+ span_id : ctx. span_id ,
470
580
op : Some ( ctx. op ) ,
471
581
..Default :: default ( )
472
582
} ;
@@ -864,6 +974,23 @@ fn parse_sentry_trace(header: &str) -> Option<SentryTrace> {
864
974
Some ( SentryTrace ( trace_id, parent_span_id, parent_sampled) )
865
975
}
866
976
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
+
867
994
impl std:: fmt:: Display for SentryTrace {
868
995
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
869
996
write ! ( f, "{}-{}" , self . 0 , self . 1 ) ?;
@@ -896,6 +1023,26 @@ mod tests {
896
1023
assert_eq ! ( parsed, Some ( trace) ) ;
897
1024
}
898
1025
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
+
899
1046
#[ test]
900
1047
fn disabled_forwards_trace_id ( ) {
901
1048
let headers = [ (
0 commit comments