Skip to content

Commit 4ca67ff

Browse files
committed
feat(sentry-core): add support for w3c traceparent
1 parent 96b35c7 commit 4ca67ff

File tree

1 file changed

+67
-4
lines changed

1 file changed

+67
-4
lines changed

sentry-core/src/performance.rs

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ pub struct TransactionContext {
7373
}
7474

7575
impl TransactionContext {
76-
/// Creates a new Transaction Context with the given `name` and `op`.
76+
/// Creates a new Transaction Context with the given `name` and `op`. A random
77+
/// `trace_id` is assigned. Use [`TransactionContext::new_with_trace_id`] to
78+
/// specify a custom trace ID.
7779
///
7880
/// See <https://docs.sentry.io/platforms/native/enriching-events/transaction-name/>
7981
/// for an explanation of a Transaction's `name`, and
@@ -84,13 +86,32 @@ impl TransactionContext {
8486
/// can be used for distributed tracing.
8587
#[must_use = "this must be used with `start_transaction`"]
8688
pub fn new(name: &str, op: &str) -> Self {
87-
Self::continue_from_headers(name, op, std::iter::empty())
89+
Self::new_with_trace_id(name, op, protocol::TraceId::default())
90+
}
91+
92+
/// Creates a new Transaction Context with the given `name`, `op`, and `trace_id`.
93+
///
94+
/// See <https://docs.sentry.io/platforms/native/enriching-events/transaction-name/>
95+
/// for an explanation of a Transaction's `name`, and
96+
/// <https://develop.sentry.dev/sdk/performance/span-operations/> for conventions
97+
/// around an `operation`'s value.
98+
#[must_use = "this must be used with `start_transaction`"]
99+
pub fn new_with_trace_id(name: &str, op: &str, trace_id: protocol::TraceId) -> Self {
100+
Self {
101+
name: name.into(),
102+
op: op.into(),
103+
trace_id,
104+
parent_span_id: None,
105+
sampled: None,
106+
custom: None,
107+
}
88108
}
89109

90110
/// Creates a new Transaction Context based on the distributed tracing `headers`.
91111
///
92-
/// The `headers` in particular need to include the `sentry-trace` header,
93-
/// which is used to associate the transaction with a distributed trace.
112+
/// The `headers` in particular need to include either the `sentry-trace` or W3C
113+
/// `traceparent` header, which is used to associate the transaction with a distributed
114+
/// trace. If both are present, `sentry-trace` takes precedence.
94115
#[must_use = "this must be used with `start_transaction`"]
95116
pub fn continue_from_headers<'a, I: IntoIterator<Item = (&'a str, &'a str)>>(
96117
name: &str,
@@ -101,6 +122,11 @@ impl TransactionContext {
101122
for (k, v) in headers.into_iter() {
102123
if k.eq_ignore_ascii_case("sentry-trace") {
103124
trace = parse_sentry_trace(v);
125+
break;
126+
}
127+
128+
if k.eq_ignore_ascii_case("traceparent") {
129+
trace = parse_w3c_traceparent(v);
104130
}
105131
}
106132

@@ -808,6 +834,23 @@ fn parse_sentry_trace(header: &str) -> Option<SentryTrace> {
808834
Some(SentryTrace(trace_id, parent_span_id, parent_sampled))
809835
}
810836

837+
/// Parses a W3C traceparent header.
838+
/// Reference: <https://w3c.github.io/trace-context/#traceparent-header-field-values>
839+
fn parse_w3c_traceparent(header: &str) -> Option<SentryTrace> {
840+
let header = header.trim();
841+
let mut parts = header.splitn(4, '-');
842+
843+
let _version = parts.next()?;
844+
let trace_id = parts.next()?.parse().ok()?;
845+
let parent_span_id = parts.next()?.parse().ok()?;
846+
let parent_sampled = parts
847+
.next()
848+
.and_then(|sampled| u8::from_str_radix(sampled, 16).ok())
849+
.map(|flag| flag & 1 != 0);
850+
851+
Some(SentryTrace(trace_id, parent_span_id, parent_sampled))
852+
}
853+
811854
impl std::fmt::Display for SentryTrace {
812855
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
813856
write!(f, "{}-{}", self.0, self.1)?;
@@ -840,6 +883,26 @@ mod tests {
840883
assert_eq!(parsed, Some(trace));
841884
}
842885

886+
#[test]
887+
fn parses_traceparent() {
888+
let trace_id = protocol::TraceId::from_str("4bf92f3577b34da6a3ce929d0e0e4736").unwrap();
889+
let parent_trace_id = protocol::SpanId::from_str("00f067aa0ba902b7").unwrap();
890+
891+
let trace =
892+
parse_w3c_traceparent("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01");
893+
assert_eq!(
894+
trace,
895+
Some(SentryTrace(trace_id, parent_trace_id, Some(true)))
896+
);
897+
898+
let trace =
899+
parse_w3c_traceparent("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00");
900+
assert_eq!(
901+
trace,
902+
Some(SentryTrace(trace_id, parent_trace_id, Some(false)))
903+
);
904+
}
905+
843906
#[test]
844907
fn disabled_forwards_trace_id() {
845908
let headers = [(

0 commit comments

Comments
 (0)