From 4d38f7f7a0b8d47485e0b0867c87f1f53bd020b5 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 19 May 2025 14:22:23 +0200 Subject: [PATCH 01/15] feat(core): implement Tracing without Performance --- sentry-core/src/scope/real.rs | 25 +++++++++++++++++++++++++ sentry-types/src/protocol/v7.rs | 16 ++++++++++++++-- sentry/tests/test_basic.rs | 8 +++++++- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/sentry-core/src/scope/real.rs b/sentry-core/src/scope/real.rs index 7c4eda471..3a70a389b 100644 --- a/sentry-core/src/scope/real.rs +++ b/sentry-core/src/scope/real.rs @@ -5,6 +5,8 @@ use std::fmt; use std::sync::Mutex; use std::sync::{Arc, PoisonError, RwLock}; +use sentry_types::protocol::v7::{SpanId, TraceContext, TraceId}; + use crate::performance::TransactionOrSpan; use crate::protocol::{Attachment, Breadcrumb, Context, Event, Level, Transaction, User, Value}; #[cfg(feature = "release-health")] @@ -19,6 +21,12 @@ pub struct Stack { pub type EventProcessor = Arc) -> Option> + Send + Sync>; +#[derive(Debug, Default, Clone)] +pub(crate) struct PropagationContext { + pub(crate) trace_id: TraceId, + pub(crate) span_id: SpanId, +} + /// Holds contextual data for the current scope. /// /// The scope is an object that can be cloned efficiently and stores data that @@ -52,6 +60,7 @@ pub struct Scope { pub(crate) session: Arc>>, pub(crate) span: Arc>, pub(crate) attachments: Arc>, + pub(crate) propagation_context: PropagationContext, } impl fmt::Debug for Scope { @@ -74,6 +83,7 @@ impl fmt::Debug for Scope { debug_struct .field("span", &self.span) .field("attachments", &self.attachments.len()) + .field("propagation_context", &self.propagation_context) .finish() } } @@ -289,6 +299,8 @@ impl Scope { if let Some(span) = self.span.as_ref() { span.apply_to_event(&mut event); + } else { + self.apply_propagation_context(&mut event); } if event.transaction.is_none() { @@ -357,4 +369,17 @@ impl Scope { session.update_from_event(event); } } + + pub(crate) fn apply_propagation_context(&self, event: &mut Event<'_>) { + if event.contexts.contains_key("trace") { + return; + } + + let context = TraceContext { + trace_id: self.propagation_context.trace_id, + span_id: self.propagation_context.span_id, + ..Default::default() + }; + event.contexts.insert("trace".into(), context.into()); + } } diff --git a/sentry-types/src/protocol/v7.rs b/sentry-types/src/protocol/v7.rs index bb2008f60..85bc537f8 100644 --- a/sentry-types/src/protocol/v7.rs +++ b/sentry-types/src/protocol/v7.rs @@ -1352,7 +1352,7 @@ pub struct OtelContext { } /// Holds the identifier for a Span -#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Hash)] #[serde(try_from = "String", into = "String")] pub struct SpanId([u8; 8]); @@ -1368,6 +1368,12 @@ impl fmt::Display for SpanId { } } +impl fmt::Debug for SpanId { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}", self) + } +} + impl From for String { fn from(span_id: SpanId) -> Self { span_id.to_string() @@ -1399,7 +1405,7 @@ impl From<[u8; 8]> for SpanId { } /// Holds the identifier for a Trace -#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Hash)] #[serde(try_from = "String", into = "String")] pub struct TraceId([u8; 16]); @@ -1415,6 +1421,12 @@ impl fmt::Display for TraceId { } } +impl fmt::Debug for TraceId { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}", self) + } +} + impl From for String { fn from(trace_id: TraceId) -> Self { trace_id.to_string() diff --git a/sentry/tests/test_basic.rs b/sentry/tests/test_basic.rs index 3813b14ea..c9431747e 100644 --- a/sentry/tests/test_basic.rs +++ b/sentry/tests/test_basic.rs @@ -3,15 +3,17 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; -use sentry::protocol::{Attachment, EnvelopeItem}; +use sentry::protocol::{Attachment, Context, EnvelopeItem}; use sentry::types::Uuid; #[test] fn test_basic_capture_message() { let mut last_event_id = None::; + let mut span = None; let events = sentry::test::with_captured_events(|| { sentry::configure_scope(|scope| { scope.set_tag("worker", "worker1"); + span = scope.get_span(); }); sentry::capture_message("Hello World!", sentry::Level::Warning); last_event_id = sentry::last_event_id(); @@ -26,6 +28,10 @@ fn test_basic_capture_message() { ); assert_eq!(Some(event.event_id), last_event_id); + + let trace_context = event.contexts.get("trace"); + assert!(span.is_none()); + assert!(matches!(trace_context, Some(Context::Trace(_)))); } #[test] From d4c2f65fbb5cb05adb0a08b0eb27204015380f8c Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 19 May 2025 14:39:08 +0200 Subject: [PATCH 02/15] split tests --- sentry/tests/test_basic.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/sentry/tests/test_basic.rs b/sentry/tests/test_basic.rs index c9431747e..54d91cec6 100644 --- a/sentry/tests/test_basic.rs +++ b/sentry/tests/test_basic.rs @@ -9,11 +9,9 @@ use sentry::types::Uuid; #[test] fn test_basic_capture_message() { let mut last_event_id = None::; - let mut span = None; let events = sentry::test::with_captured_events(|| { sentry::configure_scope(|scope| { scope.set_tag("worker", "worker1"); - span = scope.get_span(); }); sentry::capture_message("Hello World!", sentry::Level::Warning); last_event_id = sentry::last_event_id(); @@ -28,6 +26,21 @@ fn test_basic_capture_message() { ); assert_eq!(Some(event.event_id), last_event_id); +} + +#[test] +fn test_event_propagation_context() { + let mut last_event_id = None::; + let mut span = None; + let events = sentry::test::with_captured_events(|| { + sentry::configure_scope(|scope| { + span = scope.get_span(); + }); + sentry::capture_message("Hello World!", sentry::Level::Warning); + last_event_id = sentry::last_event_id(); + }); + assert_eq!(events.len(), 1); + let event = events.into_iter().next().unwrap(); let trace_context = event.contexts.get("trace"); assert!(span.is_none()); From b4b94841e45f61f165dc13b04d6d355b80f8ff11 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 19 May 2025 16:15:19 +0200 Subject: [PATCH 03/15] API adjustments --- sentry-core/src/performance.rs | 14 ++++++++++++-- sentry-core/src/scope/real.rs | 18 +++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/sentry-core/src/performance.rs b/sentry-core/src/performance.rs index f36ed6b93..9dc0073c3 100644 --- a/sentry-core/src/performance.rs +++ b/sentry-core/src/performance.rs @@ -476,6 +476,8 @@ impl TransactionOrSpan { } /// Returns the headers needed for distributed tracing. + /// [`crate::Scope::iter_trace_propagation_headers`] is preferred if the intention is to get + /// the distributed tracing headers for the currently active span/transaction. pub fn iter_headers(&self) -> TraceHeadersIter { match self { TransactionOrSpan::Transaction(transaction) => transaction.iter_headers(), @@ -774,6 +776,8 @@ impl Transaction { } /// Returns the headers needed for distributed tracing. + /// [`crate::Scope::iter_trace_propagation_headers`] is preferred if the intention is to get + /// the distributed tracing headers for the currently active transaction. pub fn iter_headers(&self) -> TraceHeadersIter { let inner = self.inner.lock().unwrap(); let trace = SentryTrace( @@ -1026,6 +1030,8 @@ impl Span { } /// Returns the headers needed for distributed tracing. + /// [`crate::Scope::iter_trace_propagation_headers`] is preferred if the intention is to get + /// the distributed tracing headers for the currently active span. pub fn iter_headers(&self) -> TraceHeadersIter { let span = self.span.lock().unwrap(); let trace = SentryTrace(span.trace_id, span.span_id, Some(self.sampled)); @@ -1130,7 +1136,7 @@ impl Span { /// This currently only yields the `sentry-trace` header, but other headers /// may be added in the future. pub struct TraceHeadersIter { - sentry_trace: Option, + pub(crate) sentry_trace: Option, } impl Iterator for TraceHeadersIter { @@ -1144,7 +1150,11 @@ 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)] -pub struct SentryTrace(protocol::TraceId, protocol::SpanId, Option); +pub struct SentryTrace( + pub(crate) protocol::TraceId, + pub(crate) protocol::SpanId, + pub(crate) Option, // sampled +); impl SentryTrace { /// Creates a new [`SentryTrace`] from the provided parameters diff --git a/sentry-core/src/scope/real.rs b/sentry-core/src/scope/real.rs index 3a70a389b..1b54a7de6 100644 --- a/sentry-core/src/scope/real.rs +++ b/sentry-core/src/scope/real.rs @@ -11,7 +11,7 @@ use crate::performance::TransactionOrSpan; use crate::protocol::{Attachment, Breadcrumb, Context, Event, Level, Transaction, User, Value}; #[cfg(feature = "release-health")] use crate::session::Session; -use crate::Client; +use crate::{Client, SentryTrace, TraceHeadersIter}; #[derive(Debug)] pub struct Stack { @@ -382,4 +382,20 @@ impl Scope { }; event.contexts.insert("trace".into(), context.into()); } + + /// Returns the headers needed for distributed tracing. + pub fn iter_trace_propagation_headers(&self) -> TraceHeadersIter { + if let Some(span) = self.get_span() { + span.iter_headers() + } else { + let data = SentryTrace( + self.propagation_context.trace_id, + self.propagation_context.span_id, + None, + ); + TraceHeadersIter { + sentry_trace: Some(data.to_string()), + } + } + } } From 0f9e36930e97fac263acf1e712c1934aa996be33 Mon Sep 17 00:00:00 2001 From: lcian Date: Mon, 19 May 2025 16:31:17 +0200 Subject: [PATCH 04/15] final adjustments --- sentry-core/src/performance.rs | 2 +- sentry-core/src/scope/real.rs | 6 +++--- sentry/tests/test_basic.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sentry-core/src/performance.rs b/sentry-core/src/performance.rs index 9dc0073c3..9e8c797b8 100644 --- a/sentry-core/src/performance.rs +++ b/sentry-core/src/performance.rs @@ -1149,7 +1149,7 @@ 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)] +#[derive(Debug, PartialEq, Clone, Copy)] pub struct SentryTrace( pub(crate) protocol::TraceId, pub(crate) protocol::SpanId, diff --git a/sentry-core/src/scope/real.rs b/sentry-core/src/scope/real.rs index 1b54a7de6..d26148cd4 100644 --- a/sentry-core/src/scope/real.rs +++ b/sentry-core/src/scope/real.rs @@ -21,10 +21,10 @@ pub struct Stack { pub type EventProcessor = Arc) -> Option> + Send + Sync>; -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, Copy)] pub(crate) struct PropagationContext { - pub(crate) trace_id: TraceId, - pub(crate) span_id: SpanId, + trace_id: TraceId, + span_id: SpanId, } /// Holds contextual data for the current scope. diff --git a/sentry/tests/test_basic.rs b/sentry/tests/test_basic.rs index 54d91cec6..dac87e373 100644 --- a/sentry/tests/test_basic.rs +++ b/sentry/tests/test_basic.rs @@ -29,7 +29,7 @@ fn test_basic_capture_message() { } #[test] -fn test_event_propagation_context() { +fn test_event_trace_context_from_propagation_context() { let mut last_event_id = None::; let mut span = None; let events = sentry::test::with_captured_events(|| { From 618cf88da6d5b52d497befc4f19704e0d02f3e4b Mon Sep 17 00:00:00 2001 From: Lorenzo Cian Date: Thu, 22 May 2025 13:31:12 +0200 Subject: [PATCH 05/15] Update sentry-core/src/performance.rs Co-authored-by: Daniel Szoke <7881302+szokeasaurusrex@users.noreply.github.com> --- sentry-core/src/performance.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sentry-core/src/performance.rs b/sentry-core/src/performance.rs index 9e8c797b8..a8acf9d5a 100644 --- a/sentry-core/src/performance.rs +++ b/sentry-core/src/performance.rs @@ -1030,8 +1030,8 @@ impl Span { } /// Returns the headers needed for distributed tracing. - /// [`crate::Scope::iter_trace_propagation_headers`] is preferred if the intention is to get - /// the distributed tracing headers for the currently active span. + /// Use [`crate::Scope::iter_trace_propagation_headers`] to obtain the active + /// span's/transaction's distributed tracing headers. pub fn iter_headers(&self) -> TraceHeadersIter { let span = self.span.lock().unwrap(); let trace = SentryTrace(span.trace_id, span.span_id, Some(self.sampled)); From f5755670b48a2201b3afdd8f67c69f815f9153d5 Mon Sep 17 00:00:00 2001 From: Lorenzo Cian Date: Thu, 22 May 2025 13:31:19 +0200 Subject: [PATCH 06/15] Update sentry-core/src/performance.rs Co-authored-by: Daniel Szoke <7881302+szokeasaurusrex@users.noreply.github.com> --- sentry-core/src/performance.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sentry-core/src/performance.rs b/sentry-core/src/performance.rs index a8acf9d5a..ace30fd25 100644 --- a/sentry-core/src/performance.rs +++ b/sentry-core/src/performance.rs @@ -476,8 +476,8 @@ impl TransactionOrSpan { } /// Returns the headers needed for distributed tracing. - /// [`crate::Scope::iter_trace_propagation_headers`] is preferred if the intention is to get - /// the distributed tracing headers for the currently active span/transaction. + /// Use [`crate::Scope::iter_trace_propagation_headers`] to obtain the active + /// span's/transaction's distributed tracing headers. pub fn iter_headers(&self) -> TraceHeadersIter { match self { TransactionOrSpan::Transaction(transaction) => transaction.iter_headers(), From e255bfe60ff27512ba76aaa2b990c9bb3f585774 Mon Sep 17 00:00:00 2001 From: Lorenzo Cian Date: Thu, 22 May 2025 13:31:27 +0200 Subject: [PATCH 07/15] Update sentry-core/src/performance.rs Co-authored-by: Daniel Szoke <7881302+szokeasaurusrex@users.noreply.github.com> --- sentry-core/src/performance.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sentry-core/src/performance.rs b/sentry-core/src/performance.rs index ace30fd25..a135ab901 100644 --- a/sentry-core/src/performance.rs +++ b/sentry-core/src/performance.rs @@ -776,8 +776,8 @@ impl Transaction { } /// Returns the headers needed for distributed tracing. - /// [`crate::Scope::iter_trace_propagation_headers`] is preferred if the intention is to get - /// the distributed tracing headers for the currently active transaction. + /// Use [`crate::Scope::iter_trace_propagation_headers`] to obtain the active + /// span's/transaction's distributed tracing headers. pub fn iter_headers(&self) -> TraceHeadersIter { let inner = self.inner.lock().unwrap(); let trace = SentryTrace( From 633c697e9985bd50aa880947adacb82fa2548a2f Mon Sep 17 00:00:00 2001 From: Lorenzo Cian Date: Thu, 22 May 2025 13:47:26 +0200 Subject: [PATCH 08/15] Update sentry-types/src/protocol/v7.rs Co-authored-by: Daniel Szoke <7881302+szokeasaurusrex@users.noreply.github.com> --- sentry-types/src/protocol/v7.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry-types/src/protocol/v7.rs b/sentry-types/src/protocol/v7.rs index 85bc537f8..abd910fc1 100644 --- a/sentry-types/src/protocol/v7.rs +++ b/sentry-types/src/protocol/v7.rs @@ -1370,7 +1370,7 @@ impl fmt::Display for SpanId { impl fmt::Debug for SpanId { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{}", self) + write!(fmt, "SpanId({})", self) } } From 6f32d186a8f2ce3c1d8404235fae8fbb6f438b78 Mon Sep 17 00:00:00 2001 From: Lorenzo Cian Date: Thu, 22 May 2025 13:47:34 +0200 Subject: [PATCH 09/15] Update sentry-types/src/protocol/v7.rs Co-authored-by: Daniel Szoke <7881302+szokeasaurusrex@users.noreply.github.com> --- sentry-types/src/protocol/v7.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry-types/src/protocol/v7.rs b/sentry-types/src/protocol/v7.rs index abd910fc1..12de86392 100644 --- a/sentry-types/src/protocol/v7.rs +++ b/sentry-types/src/protocol/v7.rs @@ -1423,7 +1423,7 @@ impl fmt::Display for TraceId { impl fmt::Debug for TraceId { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{}", self) + write!(fmt, "TraceId({})", self) } } From 3f5fd205c9cf37d897250c4a631b9113b792656a Mon Sep 17 00:00:00 2001 From: lcian Date: Thu, 22 May 2025 13:54:43 +0200 Subject: [PATCH 10/15] cargo fmt --- sentry-core/src/performance.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sentry-core/src/performance.rs b/sentry-core/src/performance.rs index a135ab901..231b2f481 100644 --- a/sentry-core/src/performance.rs +++ b/sentry-core/src/performance.rs @@ -476,7 +476,7 @@ impl TransactionOrSpan { } /// Returns the headers needed for distributed tracing. - /// Use [`crate::Scope::iter_trace_propagation_headers`] to obtain the active + /// Use [`crate::Scope::iter_trace_propagation_headers`] to obtain the active /// span's/transaction's distributed tracing headers. pub fn iter_headers(&self) -> TraceHeadersIter { match self { @@ -776,7 +776,7 @@ impl Transaction { } /// Returns the headers needed for distributed tracing. - /// Use [`crate::Scope::iter_trace_propagation_headers`] to obtain the active + /// Use [`crate::Scope::iter_trace_propagation_headers`] to obtain the active /// span's/transaction's distributed tracing headers. pub fn iter_headers(&self) -> TraceHeadersIter { let inner = self.inner.lock().unwrap(); @@ -1030,7 +1030,7 @@ impl Span { } /// Returns the headers needed for distributed tracing. - /// Use [`crate::Scope::iter_trace_propagation_headers`] to obtain the active + /// Use [`crate::Scope::iter_trace_propagation_headers`] to obtain the active /// span's/transaction's distributed tracing headers. pub fn iter_headers(&self) -> TraceHeadersIter { let span = self.span.lock().unwrap(); From 2ff1a6ca6d53abd12dc4ba9f9c3ac94e08e646b9 Mon Sep 17 00:00:00 2001 From: lcian Date: Thu, 22 May 2025 16:59:29 +0200 Subject: [PATCH 11/15] address feedback --- sentry-core/src/performance.rs | 69 +++++++++++++++++++++------------- sentry-core/src/scope/real.rs | 20 +++------- 2 files changed, 49 insertions(+), 40 deletions(-) diff --git a/sentry-core/src/performance.rs b/sentry-core/src/performance.rs index 231b2f481..56d340f9d 100644 --- a/sentry-core/src/performance.rs +++ b/sentry-core/src/performance.rs @@ -199,14 +199,12 @@ impl TransactionContext { sentry_trace: &SentryTrace, span_id: Option, ) -> Self { - let (trace_id, parent_span_id, sampled) = - (sentry_trace.0, Some(sentry_trace.1), sentry_trace.2); Self { name: name.into(), op: op.into(), - trace_id, - parent_span_id, - sampled, + trace_id: sentry_trace.trace_id, + parent_span_id: Some(sentry_trace.span_id), + sampled: sentry_trace.sampled, span_id: span_id.unwrap_or_default(), custom: None, } @@ -780,11 +778,11 @@ impl Transaction { /// span's/transaction's distributed tracing headers. pub fn iter_headers(&self) -> TraceHeadersIter { let inner = self.inner.lock().unwrap(); - let trace = SentryTrace( - inner.context.trace_id, - inner.context.span_id, - Some(inner.sampled), - ); + let trace = SentryTrace { + trace_id: inner.context.trace_id, + span_id: inner.context.span_id, + sampled: Some(inner.sampled), + }; TraceHeadersIter { sentry_trace: Some(trace.to_string()), } @@ -1034,7 +1032,7 @@ impl Span { /// span's/transaction's distributed tracing headers. pub fn iter_headers(&self) -> TraceHeadersIter { let span = self.span.lock().unwrap(); - let trace = SentryTrace(span.trace_id, span.span_id, Some(self.sampled)); + let trace = SentryTrace::new(span.trace_id, span.span_id, Some(self.sampled)); TraceHeadersIter { sentry_trace: Some(trace.to_string()), } @@ -1131,12 +1129,24 @@ impl Span { } } +/// Represents a key-value pair such as an HTTP header. +pub type TraceHeader = (&'static str, String); + /// An Iterator over HTTP header names and values needed for distributed tracing. /// /// This currently only yields the `sentry-trace` header, but other headers /// may be added in the future. pub struct TraceHeadersIter { - pub(crate) sentry_trace: Option, + sentry_trace: Option, +} + +impl TraceHeadersIter { + #[cfg_attr(not(feature = "client"), allow(dead_code))] + pub(crate) fn new(sentry_trace: String) -> Self { + Self { + sentry_trace: Some(sentry_trace), + } + } } impl Iterator for TraceHeadersIter { @@ -1149,12 +1159,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)] -pub struct SentryTrace( - pub(crate) protocol::TraceId, - pub(crate) protocol::SpanId, - pub(crate) Option, // sampled -); +#[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 @@ -1163,7 +1173,11 @@ impl SentryTrace { span_id: protocol::SpanId, sampled: Option, ) -> Self { - SentryTrace(trace_id, span_id, sampled) + SentryTrace { + trace_id, + span_id, + sampled, + } } } @@ -1179,7 +1193,7 @@ fn parse_sentry_trace(header: &str) -> Option { _ => None, }); - Some(SentryTrace(trace_id, parent_span_id, parent_sampled)) + Some(SentryTrace::new(trace_id, parent_span_id, parent_sampled)) } /// Extracts distributed tracing metadata from headers (or, generally, key-value pairs), @@ -1199,8 +1213,8 @@ pub fn parse_headers<'a, I: IntoIterator>( impl std::fmt::Display for SentryTrace { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}-{}", self.0, self.1)?; - if let Some(sampled) = self.2 { + write!(f, "{}-{}", self.trace_id, self.span_id)?; + if let Some(sampled) = self.sampled { write!(f, "-{}", if sampled { '1' } else { '0' })?; } Ok(()) @@ -1221,10 +1235,10 @@ mod tests { let trace = parse_sentry_trace("09e04486820349518ac7b5d2adbf6ba5-9cf635fa5b870b3a-0"); assert_eq!( trace, - Some(SentryTrace(trace_id, parent_trace_id, Some(false))) + Some(SentryTrace::new(trace_id, parent_trace_id, Some(false))) ); - let trace = SentryTrace(Default::default(), Default::default(), None); + let trace = SentryTrace::new(Default::default(), Default::default(), None); let parsed = parse_sentry_trace(&trace.to_string()); assert_eq!(parsed, Some(trace)); } @@ -1243,8 +1257,11 @@ mod tests { let header = span.iter_headers().next().unwrap().1; let parsed = parse_sentry_trace(&header).unwrap(); - assert_eq!(&parsed.0.to_string(), "09e04486820349518ac7b5d2adbf6ba5"); - assert_eq!(parsed.2, Some(true)); + assert_eq!( + &parsed.trace_id.to_string(), + "09e04486820349518ac7b5d2adbf6ba5" + ); + assert_eq!(parsed.sampled, Some(true)); } #[test] diff --git a/sentry-core/src/scope/real.rs b/sentry-core/src/scope/real.rs index d26148cd4..47b1e9da0 100644 --- a/sentry-core/src/scope/real.rs +++ b/sentry-core/src/scope/real.rs @@ -5,13 +5,13 @@ use std::fmt; use std::sync::Mutex; use std::sync::{Arc, PoisonError, RwLock}; -use sentry_types::protocol::v7::{SpanId, TraceContext, TraceId}; +use sentry_types::protocol::v7::TraceContext; use crate::performance::TransactionOrSpan; use crate::protocol::{Attachment, Breadcrumb, Context, Event, Level, Transaction, User, Value}; #[cfg(feature = "release-health")] use crate::session::Session; -use crate::{Client, SentryTrace, TraceHeadersIter}; +use crate::{Client, SentryTrace, TraceHeader, TraceHeadersIter}; #[derive(Debug)] pub struct Stack { @@ -21,12 +21,6 @@ pub struct Stack { pub type EventProcessor = Arc) -> Option> + Send + Sync>; -#[derive(Debug, Default, Clone, Copy)] -pub(crate) struct PropagationContext { - trace_id: TraceId, - span_id: SpanId, -} - /// Holds contextual data for the current scope. /// /// The scope is an object that can be cloned efficiently and stores data that @@ -60,7 +54,7 @@ pub struct Scope { pub(crate) session: Arc>>, pub(crate) span: Arc>, pub(crate) attachments: Arc>, - pub(crate) propagation_context: PropagationContext, + pub(crate) propagation_context: SentryTrace, } impl fmt::Debug for Scope { @@ -384,18 +378,16 @@ impl Scope { } /// Returns the headers needed for distributed tracing. - pub fn iter_trace_propagation_headers(&self) -> TraceHeadersIter { + pub fn iter_trace_propagation_headers(&self) -> impl Iterator { if let Some(span) = self.get_span() { span.iter_headers() } else { - let data = SentryTrace( + let data = SentryTrace::new( self.propagation_context.trace_id, self.propagation_context.span_id, None, ); - TraceHeadersIter { - sentry_trace: Some(data.to_string()), - } + TraceHeadersIter::new(data.to_string()) } } } From 8cf93b8d2978f7473cb54445170112a415203271 Mon Sep 17 00:00:00 2001 From: lcian Date: Thu, 22 May 2025 17:05:13 +0200 Subject: [PATCH 12/15] more improvements --- sentry-core/src/performance.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sentry-core/src/performance.rs b/sentry-core/src/performance.rs index 56d340f9d..ebe99146a 100644 --- a/sentry-core/src/performance.rs +++ b/sentry-core/src/performance.rs @@ -778,11 +778,11 @@ impl Transaction { /// span's/transaction's distributed tracing headers. pub fn iter_headers(&self) -> TraceHeadersIter { let inner = self.inner.lock().unwrap(); - let trace = SentryTrace { - trace_id: inner.context.trace_id, - span_id: inner.context.span_id, - sampled: Some(inner.sampled), - }; + let trace = SentryTrace::new( + inner.context.trace_id, + inner.context.span_id, + Some(inner.sampled), + ); TraceHeadersIter { sentry_trace: Some(trace.to_string()), } From 96af5a91d2c23c7ef2fe0433b892b6525e7b4208 Mon Sep 17 00:00:00 2001 From: lcian Date: Tue, 27 May 2025 14:47:37 +0200 Subject: [PATCH 13/15] address feedback --- sentry-core/src/performance.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sentry-core/src/performance.rs b/sentry-core/src/performance.rs index ebe99146a..39ee2363e 100644 --- a/sentry-core/src/performance.rs +++ b/sentry-core/src/performance.rs @@ -475,7 +475,7 @@ impl TransactionOrSpan { /// Returns the headers needed for distributed tracing. /// Use [`crate::Scope::iter_trace_propagation_headers`] to obtain the active - /// span's/transaction's distributed tracing headers. + /// trace's distributed tracing headers. pub fn iter_headers(&self) -> TraceHeadersIter { match self { TransactionOrSpan::Transaction(transaction) => transaction.iter_headers(), @@ -775,7 +775,7 @@ impl Transaction { /// Returns the headers needed for distributed tracing. /// Use [`crate::Scope::iter_trace_propagation_headers`] to obtain the active - /// span's/transaction's distributed tracing headers. + /// trace's distributed tracing headers. pub fn iter_headers(&self) -> TraceHeadersIter { let inner = self.inner.lock().unwrap(); let trace = SentryTrace::new( @@ -1029,7 +1029,7 @@ impl Span { /// Returns the headers needed for distributed tracing. /// Use [`crate::Scope::iter_trace_propagation_headers`] to obtain the active - /// span's/transaction's distributed tracing headers. + /// trace's distributed tracing headers. pub fn iter_headers(&self) -> TraceHeadersIter { let span = self.span.lock().unwrap(); let trace = SentryTrace::new(span.trace_id, span.span_id, Some(self.sampled)); @@ -1141,7 +1141,7 @@ pub struct TraceHeadersIter { } impl TraceHeadersIter { - #[cfg_attr(not(feature = "client"), allow(dead_code))] + #[cfg(feature = "client")] pub(crate) fn new(sentry_trace: String) -> Self { Self { sentry_trace: Some(sentry_trace), From 787b64a94da63c674ce68b8a143709c578a6ec7b Mon Sep 17 00:00:00 2001 From: lcian Date: Tue, 27 May 2025 15:33:21 +0200 Subject: [PATCH 14/15] changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 687a9227c..c8c963636 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Unreleased + +### Behavioral changes + +- feat(core): implement Tracing without Performance (#811) by @lcian + - The SDK now implements Tracing without Performance, which makes it so that each `Scope` is associated with an object holding some tracing information. + - This information is used as a fallback when capturing an event with tracing disabled or otherwise no ongoing span, to still allow related events to be linked by a trace. + ## 0.38.1 ### Fixes From 10d27f99ab1491bd63d71ddd2825ebbde2584a7e Mon Sep 17 00:00:00 2001 From: Lorenzo Cian Date: Tue, 27 May 2025 15:39:24 +0200 Subject: [PATCH 15/15] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7de92055f..ba719c8e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ - feat(core): implement Tracing without Performance (#811) by @lcian - The SDK now implements Tracing without Performance, which makes it so that each `Scope` is associated with an object holding some tracing information. - This information is used as a fallback when capturing an event with tracing disabled or otherwise no ongoing span, to still allow related events to be linked by a trace. - - A new API `Scope::iter_trace_propagation_headers` has been provided that will use the fallback tracing information if there is no current `Span`. + - A new API `Scope::iter_trace_propagation_headers` has been provided that will use the fallback tracing information if there is no current `Span` on the `Scope`. ## 0.38.1