Skip to content

Commit

Permalink
feat(sentry-core): add support for w3c traceparent
Browse files Browse the repository at this point in the history
  • Loading branch information
saiintbrisson committed Nov 28, 2024
1 parent 605d36d commit 33ebddb
Showing 1 changed file with 67 additions and 4 deletions.
71 changes: 67 additions & 4 deletions sentry-core/src/performance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ pub struct TransactionContext {
}

impl TransactionContext {
/// Creates a new Transaction Context with the given `name` and `op`.
/// Creates a new Transaction Context with the given `name` and `op`. A random
/// `trace_id` is assigned. Use [`TransactionContext::new_with_trace_id`] to
/// specify a custom trace ID.
///
/// See <https://docs.sentry.io/platforms/native/enriching-events/transaction-name/>
/// for an explanation of a Transaction's `name`, and
Expand All @@ -85,13 +87,32 @@ impl TransactionContext {
/// can be used for distributed tracing.
#[must_use = "this must be used with `start_transaction`"]
pub fn new(name: &str, op: &str) -> Self {
Self::continue_from_headers(name, op, std::iter::empty())
Self::new_with_trace_id(name, op, protocol::TraceId::default())
}

/// Creates a new Transaction Context with the given `name`, `op`, and `trace_id`.
///
/// See <https://docs.sentry.io/platforms/native/enriching-events/transaction-name/>
/// for an explanation of a Transaction's `name`, and
/// <https://develop.sentry.dev/sdk/performance/span-operations/> for conventions
/// around an `operation`'s value.
#[must_use = "this must be used with `start_transaction`"]
pub fn new_with_trace_id(name: &str, op: &str, trace_id: protocol::TraceId) -> Self {
Self {
name: name.into(),
op: op.into(),
trace_id,
parent_span_id: None,
sampled: None,
custom: None,
}
}

/// Creates a new Transaction Context based on the distributed tracing `headers`.
///
/// The `headers` in particular need to include the `sentry-trace` header,
/// which is used to associate the transaction with a distributed trace.
/// The `headers` in particular need to include either the `sentry-trace` or W3C
/// `traceparent` header, which is used to associate the transaction with a distributed
/// trace. If both are present, `sentry-trace` takes precedence.
#[must_use = "this must be used with `start_transaction`"]
pub fn continue_from_headers<'a, I: IntoIterator<Item = (&'a str, &'a str)>>(
name: &str,
Expand All @@ -102,6 +123,11 @@ impl TransactionContext {
for (k, v) in headers.into_iter() {
if k.eq_ignore_ascii_case("sentry-trace") {
trace = parse_sentry_trace(v);
break;
}

if k.eq_ignore_ascii_case("traceparent") {
trace = parse_w3c_traceparent(v);
}
}

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

/// Parses a W3C traceparent header.
/// Reference: <https://w3c.github.io/trace-context/#traceparent-header-field-values>
fn parse_w3c_traceparent(header: &str) -> Option<SentryTrace> {
let header = header.trim();
let mut parts = header.splitn(4, '-');

let _version = parts.next()?;
let trace_id = parts.next()?.parse().ok()?;
let parent_span_id = parts.next()?.parse().ok()?;
let parent_sampled = parts
.next()
.and_then(|sampled| u8::from_str_radix(sampled, 16).ok())
.map(|flag| flag & 1 != 0);

Some(SentryTrace(trace_id, parent_span_id, parent_sampled))
}

impl std::fmt::Display for SentryTrace {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}-{}", self.0, self.1)?;
Expand Down Expand Up @@ -890,6 +933,26 @@ mod tests {
assert_eq!(parsed, Some(trace));
}

#[test]
fn parses_traceparent() {
let trace_id = protocol::TraceId::from_str("4bf92f3577b34da6a3ce929d0e0e4736").unwrap();
let parent_trace_id = protocol::SpanId::from_str("00f067aa0ba902b7").unwrap();

let trace =
parse_w3c_traceparent("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01");
assert_eq!(
trace,
Some(SentryTrace(trace_id, parent_trace_id, Some(true)))
);

let trace =
parse_w3c_traceparent("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00");
assert_eq!(
trace,
Some(SentryTrace(trace_id, parent_trace_id, Some(false)))
);
}

#[test]
fn disabled_forwards_trace_id() {
let headers = [(
Expand Down

0 comments on commit 33ebddb

Please sign in to comment.