Skip to content

Commit a425962

Browse files
authored
feat(core): add some DSC fields to transaction envelope headers (#869)
1 parent 611b2cd commit a425962

3 files changed

Lines changed: 79 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
- feat(types): add all the missing supported envelope headers ([#867](https://github.com/getsentry/sentry-rust/pull/867)) by @lcian
1010
- feat(types): add setters for envelope headers ([#868](https://github.com/getsentry/sentry-rust/pull/868)) by @lcian
1111
- It's now possible to set all of the [envelope headers](https://develop.sentry.dev/sdk/data-model/envelopes/#headers) supported by the protocol when constructing envelopes.
12+
- feat(core): add some DSC fields to transaction envelope headers ([#869](https://github.com/getsentry/sentry-rust/pull/869)) by @lcian
13+
- The SDK now sends additional envelope headers with transactions. This should solve some extrapolation issues for span metrics.
1214

1315
### Behavioral changes
1416

sentry-core/src/performance.rs

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -594,26 +594,33 @@ fn transaction_sample_rate(
594594
) -> f32 {
595595
match (traces_sampler, traces_sample_rate) {
596596
(Some(traces_sampler), _) => traces_sampler(ctx),
597-
(None, traces_sample_rate) => ctx
598-
.sampled
599-
.map(|sampled| if sampled { 1.0 } else { 0.0 })
600-
.unwrap_or(traces_sample_rate),
597+
(None, traces_sample_rate) => ctx.sampled.map(f32::from).unwrap_or(traces_sample_rate),
601598
}
602599
}
603600

604601
/// Determine whether the new transaction should be sampled.
605602
#[cfg(feature = "client")]
606603
impl Client {
607-
fn is_transaction_sampled(&self, ctx: &TransactionContext) -> bool {
604+
fn determine_sampling_decision(&self, ctx: &TransactionContext) -> (bool, f32) {
608605
let client_options = self.options();
609-
self.sample_should_send(transaction_sample_rate(
606+
let sample_rate = transaction_sample_rate(
610607
client_options.traces_sampler.as_deref(),
611608
ctx,
612609
client_options.traces_sample_rate,
613-
))
610+
);
611+
let sampled = self.sample_should_send(sample_rate);
612+
(sampled, sample_rate)
614613
}
615614
}
616615

616+
/// Some metadata associated with a transaction.
617+
#[cfg(feature = "client")]
618+
#[derive(Clone, Debug)]
619+
struct TransactionMetadata {
620+
/// The sample rate used when making the sampling decision for the associated transaction.
621+
sample_rate: f32,
622+
}
623+
617624
/// A running Performance Monitoring Transaction.
618625
///
619626
/// The transaction needs to be explicitly finished via [`Transaction::finish`],
@@ -622,6 +629,8 @@ impl Client {
622629
#[derive(Clone, Debug)]
623630
pub struct Transaction {
624631
pub(crate) inner: TransactionArc,
632+
#[cfg(feature = "client")]
633+
metadata: TransactionMetadata,
625634
}
626635

627636
/// Iterable for a transaction's [data attributes](protocol::TraceContext::data).
@@ -660,15 +669,21 @@ impl<'a> TransactionData<'a> {
660669
impl Transaction {
661670
#[cfg(feature = "client")]
662671
fn new(client: Option<Arc<Client>>, ctx: TransactionContext) -> Self {
663-
let (sampled, transaction) = match client.as_ref() {
672+
let ((sampled, sample_rate), transaction) = match client.as_ref() {
664673
Some(client) => (
665-
client.is_transaction_sampled(&ctx),
674+
client.determine_sampling_decision(&ctx),
666675
Some(protocol::Transaction {
667676
name: Some(ctx.name),
668677
..Default::default()
669678
}),
670679
),
671-
None => (ctx.sampled.unwrap_or(false), None),
680+
None => (
681+
(
682+
ctx.sampled.unwrap_or(false),
683+
ctx.sampled.map_or(0.0, f32::from),
684+
),
685+
None,
686+
),
672687
};
673688

674689
let context = protocol::TraceContext {
@@ -686,6 +701,7 @@ impl Transaction {
686701
context,
687702
transaction,
688703
})),
704+
metadata: TransactionMetadata { sample_rate },
689705
}
690706
}
691707

@@ -820,9 +836,19 @@ impl Transaction {
820836
transaction.sdk = Some(std::borrow::Cow::Owned(client.sdk_info.clone()));
821837
transaction.server_name.clone_from(&opts.server_name);
822838

839+
let mut dsc = protocol::DynamicSamplingContext::new()
840+
.with_trace_id(inner.context.trace_id)
841+
.with_sample_rate(self.metadata.sample_rate)
842+
.with_sampled(inner.sampled);
843+
if let Some(public_key) = client.dsn().map(|dsn| dsn.public_key()) {
844+
dsc = dsc.with_public_key(public_key.to_owned());
845+
}
846+
823847
drop(inner);
824848

825-
let mut envelope = protocol::Envelope::new();
849+
let mut envelope = protocol::Envelope::new().with_headers(
850+
protocol::EnvelopeHeaders::new().with_trace(dsc)
851+
);
826852
envelope.add_item(transaction);
827853

828854
client.send_envelope(envelope)

sentry/tests/test_basic.rs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
use std::sync::atomic::{AtomicUsize, Ordering};
44
use std::sync::Arc;
55

6-
use sentry::protocol::{Attachment, Context, EnvelopeItem};
7-
use sentry::types::Uuid;
6+
use sentry::protocol::{
7+
Attachment, Context, DynamicSamplingContext, EnvelopeHeaders, EnvelopeItem,
8+
};
9+
use sentry::types::{Dsn, Uuid};
810

911
#[test]
1012
fn test_basic_capture_message() {
@@ -578,3 +580,39 @@ fn test_basic_capture_log_macro_message_formatted_with_attributes() {
578580
_ => panic!("expected item container"),
579581
}
580582
}
583+
584+
#[test]
585+
fn test_transaction_envelope_dsc_headers() {
586+
let mut trace_id: Option<sentry::protocol::TraceId> = None;
587+
let dsn: Option<Dsn> = "http://foo@example.com/42".parse().ok();
588+
let envelopes = sentry::test::with_captured_envelopes_options(
589+
|| {
590+
let transaction_ctx = sentry::TransactionContext::new("name transaction", "op");
591+
trace_id = Some(transaction_ctx.trace_id());
592+
let transaction = sentry::start_transaction(transaction_ctx);
593+
sentry::configure_scope(|scope| scope.set_span(Some(transaction.clone().into())));
594+
transaction.finish();
595+
},
596+
sentry::ClientOptions {
597+
dsn: dsn.clone(),
598+
traces_sample_rate: 1.0,
599+
..Default::default()
600+
},
601+
);
602+
603+
assert!(trace_id.is_some());
604+
let trace_id = trace_id.unwrap();
605+
assert_eq!(envelopes.len(), 1);
606+
let envelope = envelopes.into_iter().next().unwrap();
607+
assert!(envelope.uuid().is_some());
608+
let uuid = envelope.uuid().copied().unwrap();
609+
610+
let expected = EnvelopeHeaders::new().with_event_id(uuid).with_trace(
611+
DynamicSamplingContext::new()
612+
.with_trace_id(trace_id)
613+
.with_public_key(dsn.unwrap().public_key().to_owned())
614+
.with_sample_rate(1.0)
615+
.with_sampled(true),
616+
);
617+
assert_eq!(envelope.headers(), &expected);
618+
}

0 commit comments

Comments
 (0)