Skip to content

Commit 33ebddb

Browse files
committed
feat(sentry-core): add support for w3c traceparent
1 parent 605d36d commit 33ebddb

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
@@ -74,7 +74,9 @@ pub struct TransactionContext {
7474
}
7575

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

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

@@ -858,6 +884,23 @@ fn parse_sentry_trace(header: &str) -> Option<SentryTrace> {
858884
Some(SentryTrace(trace_id, parent_span_id, parent_sampled))
859885
}
860886

887+
/// Parses a W3C traceparent header.
888+
/// Reference: <https://w3c.github.io/trace-context/#traceparent-header-field-values>
889+
fn parse_w3c_traceparent(header: &str) -> Option<SentryTrace> {
890+
let header = header.trim();
891+
let mut parts = header.splitn(4, '-');
892+
893+
let _version = parts.next()?;
894+
let trace_id = parts.next()?.parse().ok()?;
895+
let parent_span_id = parts.next()?.parse().ok()?;
896+
let parent_sampled = parts
897+
.next()
898+
.and_then(|sampled| u8::from_str_radix(sampled, 16).ok())
899+
.map(|flag| flag & 1 != 0);
900+
901+
Some(SentryTrace(trace_id, parent_span_id, parent_sampled))
902+
}
903+
861904
impl std::fmt::Display for SentryTrace {
862905
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
863906
write!(f, "{}-{}", self.0, self.1)?;
@@ -890,6 +933,26 @@ mod tests {
890933
assert_eq!(parsed, Some(trace));
891934
}
892935

936+
#[test]
937+
fn parses_traceparent() {
938+
let trace_id = protocol::TraceId::from_str("4bf92f3577b34da6a3ce929d0e0e4736").unwrap();
939+
let parent_trace_id = protocol::SpanId::from_str("00f067aa0ba902b7").unwrap();
940+
941+
let trace =
942+
parse_w3c_traceparent("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01");
943+
assert_eq!(
944+
trace,
945+
Some(SentryTrace(trace_id, parent_trace_id, Some(true)))
946+
);
947+
948+
let trace =
949+
parse_w3c_traceparent("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00");
950+
assert_eq!(
951+
trace,
952+
Some(SentryTrace(trace_id, parent_trace_id, Some(false)))
953+
);
954+
}
955+
893956
#[test]
894957
fn disabled_forwards_trace_id() {
895958
let headers = [(

0 commit comments

Comments
 (0)