From 61516784954ac8b8b668fc245aaa8f0e884a43fc Mon Sep 17 00:00:00 2001 From: Daniel Szoke Date: Fri, 26 Jun 2026 14:37:11 +0200 Subject: [PATCH] ref(core): Split `performance` module Split code related to header parsing into its own module. This change is primarily just moving stuff around. But, one test has been modified to not need access to private APIs that are no longer in the same module after the split. --- sentry-core/src/performance/headers.rs | 88 +++++++++++++++++++ .../{performance.rs => performance/mod.rs} | 87 ++---------------- 2 files changed, 93 insertions(+), 82 deletions(-) create mode 100644 sentry-core/src/performance/headers.rs rename sentry-core/src/{performance.rs => performance/mod.rs} (94%) diff --git a/sentry-core/src/performance/headers.rs b/sentry-core/src/performance/headers.rs new file mode 100644 index 00000000..84666556 --- /dev/null +++ b/sentry-core/src/performance/headers.rs @@ -0,0 +1,88 @@ +//! Module containing utilities for interacting with Sentry tracing headers. + +use crate::protocol; + +/// A container for distributed tracing metadata that can be extracted from e.g. the `sentry-trace` +/// HTTP header. +#[derive(Debug, PartialEq, Clone, Copy, Default)] +pub struct SentryTrace { + pub(crate) trace_id: protocol::TraceId, + pub(crate) span_id: protocol::SpanId, + pub(crate) sampled: Option, +} + +impl SentryTrace { + /// Creates a new [`SentryTrace`] from the provided parameters + pub fn new( + trace_id: protocol::TraceId, + span_id: protocol::SpanId, + sampled: Option, + ) -> Self { + SentryTrace { + trace_id, + span_id, + sampled, + } + } +} + +fn parse_sentry_trace(header: &str) -> Option { + let header = header.trim(); + let mut parts = header.splitn(3, '-'); + + let trace_id = parts.next()?.parse().ok()?; + let parent_span_id = parts.next()?.parse().ok()?; + let parent_sampled = parts.next().and_then(|sampled| match sampled { + "1" => Some(true), + "0" => Some(false), + _ => None, + }); + + Some(SentryTrace::new(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.trace_id, self.span_id)?; + if let Some(sampled) = self.sampled { + write!(f, "-{}", if sampled { '1' } else { '0' })?; + } + Ok(()) + } +} + +/// Extracts distributed tracing metadata from headers (or, generally, key-value pairs), +/// considering the values for `sentry-trace`. +pub fn parse<'a, I: IntoIterator>(headers: I) -> Option { + let mut trace = None; + for (k, v) in headers.into_iter() { + if k.eq_ignore_ascii_case("sentry-trace") { + trace = parse_sentry_trace(v); + break; + } + } + trace +} + +#[cfg(test)] +mod tests { + use std::str::FromStr as _; + + use super::*; + + #[test] + fn parses_sentry_trace() { + let trace_id = protocol::TraceId::from_str("09e04486820349518ac7b5d2adbf6ba5").unwrap(); + let parent_trace_id = protocol::SpanId::from_str("9cf635fa5b870b3a").unwrap(); + + let trace = parse_sentry_trace("09e04486820349518ac7b5d2adbf6ba5-9cf635fa5b870b3a-0"); + assert_eq!( + trace, + Some(SentryTrace::new(trace_id, parent_trace_id, Some(false))) + ); + + let trace = SentryTrace::new(Default::default(), Default::default(), None); + let parsed = parse_sentry_trace(&trace.to_string()); + assert_eq!(parsed, Some(trace)); + } +} diff --git a/sentry-core/src/performance.rs b/sentry-core/src/performance/mod.rs similarity index 94% rename from sentry-core/src/performance.rs rename to sentry-core/src/performance/mod.rs index ae6056e0..a790b32f 100644 --- a/sentry-core/src/performance.rs +++ b/sentry-core/src/performance/mod.rs @@ -15,6 +15,10 @@ use crate::{protocol, Hub}; #[cfg(feature = "client")] use crate::Client; +pub use self::headers::{parse as parse_headers, SentryTrace}; + +mod headers; + #[cfg(feature = "client")] const MAX_SPANS: usize = 1_000; @@ -1250,93 +1254,12 @@ impl Iterator for TraceHeadersIter { } } -/// A container for distributed tracing metadata that can be extracted from e.g. the `sentry-trace` -/// HTTP header. -#[derive(Debug, PartialEq, Clone, Copy, Default)] -pub struct SentryTrace { - pub(crate) trace_id: protocol::TraceId, - pub(crate) span_id: protocol::SpanId, - pub(crate) sampled: Option, -} - -impl SentryTrace { - /// Creates a new [`SentryTrace`] from the provided parameters - pub fn new( - trace_id: protocol::TraceId, - span_id: protocol::SpanId, - sampled: Option, - ) -> Self { - SentryTrace { - trace_id, - span_id, - sampled, - } - } -} - -fn parse_sentry_trace(header: &str) -> Option { - let header = header.trim(); - let mut parts = header.splitn(3, '-'); - - let trace_id = parts.next()?.parse().ok()?; - let parent_span_id = parts.next()?.parse().ok()?; - let parent_sampled = parts.next().and_then(|sampled| match sampled { - "1" => Some(true), - "0" => Some(false), - _ => None, - }); - - Some(SentryTrace::new(trace_id, parent_span_id, parent_sampled)) -} - -/// Extracts distributed tracing metadata from headers (or, generally, key-value pairs), -/// considering the values for `sentry-trace`. -pub fn parse_headers<'a, I: IntoIterator>( - headers: I, -) -> Option { - let mut trace = None; - for (k, v) in headers.into_iter() { - if k.eq_ignore_ascii_case("sentry-trace") { - trace = parse_sentry_trace(v); - break; - } - } - trace -} - -impl std::fmt::Display for SentryTrace { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}-{}", self.trace_id, self.span_id)?; - if let Some(sampled) = self.sampled { - write!(f, "-{}", if sampled { '1' } else { '0' })?; - } - Ok(()) - } -} - #[cfg(test)] mod tests { - use std::str::FromStr; use std::sync::Arc; use super::*; - #[test] - fn parses_sentry_trace() { - let trace_id = protocol::TraceId::from_str("09e04486820349518ac7b5d2adbf6ba5").unwrap(); - let parent_trace_id = protocol::SpanId::from_str("9cf635fa5b870b3a").unwrap(); - - let trace = parse_sentry_trace("09e04486820349518ac7b5d2adbf6ba5-9cf635fa5b870b3a-0"); - assert_eq!( - trace, - Some(SentryTrace::new(trace_id, parent_trace_id, Some(false))) - ); - - let trace = SentryTrace::new(Default::default(), Default::default(), None); - let parsed = parse_sentry_trace(&trace.to_string()); - assert_eq!(parsed, Some(trace)); - } - #[test] fn disabled_forwards_trace_id() { let headers = [( @@ -1349,7 +1272,7 @@ mod tests { let span = trx.start_child("noop", "noop"); let header = span.iter_headers().next().unwrap().1; - let parsed = parse_sentry_trace(&header).unwrap(); + let parsed = parse_headers([("sentry-trace", header.as_str())]).unwrap(); assert_eq!( &parsed.trace_id.to_string(),