diff --git a/CHANGELOG.md b/CHANGELOG.md index e44edffa99..0d31b6ef57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,13 @@ ## Unreleased +### Features + +- Added Sampling Decision to Trace Envelope Header ([#2495](https://github.com/getsentry/sentry-dotnet/pull/2495)) + ### Fixes -- Fixes baggage propagation when an exception is thrown from middleware ([#2487](https://github.com/getsentry/sentry-dotnet/pull/2487)) +- Fixed baggage propagation when an exception is thrown from middleware ([#2487](https://github.com/getsentry/sentry-dotnet/pull/2487)) - Fix Durable Functions preventing orchestrators from completing ([#2491](https://github.com/getsentry/sentry-dotnet/pull/2491)) - Re-enable HubTests.FlushOnDispose_SendsEnvelope ([#2492](https://github.com/getsentry/sentry-dotnet/pull/2492)) diff --git a/src/Sentry/DynamicSamplingContext.cs b/src/Sentry/DynamicSamplingContext.cs index baeab3f916..40ffc2161e 100644 --- a/src/Sentry/DynamicSamplingContext.cs +++ b/src/Sentry/DynamicSamplingContext.cs @@ -22,6 +22,7 @@ internal class DynamicSamplingContext private DynamicSamplingContext( SentryId traceId, string publicKey, + bool? sampled, double sampleRate, string? release = null, string? environment = null, @@ -51,6 +52,11 @@ private DynamicSamplingContext( ["sample_rate"] = sampleRate.ToString(CultureInfo.InvariantCulture) }; + if (sampled.HasValue) + { + items.Add("sampled", sampled.Value ? "true" : "false"); + } + // Set optional values if (!string.IsNullOrWhiteSpace(release)) { @@ -95,6 +101,11 @@ private DynamicSamplingContext( return null; } + if (items.TryGetValue("sampled", out var sampledString) && !bool.TryParse(sampledString, out _)) + { + return null; + } + if (!items.TryGetValue("sample_rate", out var sampleRate) || !double.TryParse(sampleRate, NumberStyles.Float, CultureInfo.InvariantCulture, out var rate) || rate is < 0.0 or > 1.0) @@ -110,6 +121,7 @@ public static DynamicSamplingContext CreateFromTransaction(TransactionTracer tra // These should already be set on the transaction. var publicKey = Dsn.Parse(options.Dsn!).PublicKey; var traceId = transaction.TraceId; + var sampled = transaction.IsSampled; var sampleRate = transaction.SampleRate!.Value; var userSegment = transaction.User.Segment; var transactionName = transaction.NameSource.IsHighQuality() ? transaction.Name : null; @@ -121,6 +133,7 @@ public static DynamicSamplingContext CreateFromTransaction(TransactionTracer tra return new DynamicSamplingContext( traceId, publicKey, + sampled, sampleRate, release, environment, diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index 0fbbac748b..cce39ec678 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -172,7 +172,7 @@ internal ITransaction StartTransaction( } // Use the provided DSC, or create one based on this transaction. - // This must be done AFTER the sampling decision has been made. + // DSC creation must be done AFTER the sampling decision has been made. transaction.DynamicSamplingContext = dynamicSamplingContext ?? transaction.CreateDynamicSamplingContext(_options); diff --git a/test/Sentry.Tests/DynamicSamplingContextTests.cs b/test/Sentry.Tests/DynamicSamplingContextTests.cs index df14242d21..b2d55a093d 100644 --- a/test/Sentry.Tests/DynamicSamplingContextTests.cs +++ b/test/Sentry.Tests/DynamicSamplingContextTests.cs @@ -1,3 +1,5 @@ +using Xunit.Sdk; + namespace Sentry.Tests; public class DynamicSamplingContextTests @@ -142,6 +144,22 @@ public void CreateFromBaggage_SampleRate_TooHigh() Assert.Null(dsc); } + [Fact] + public void CreateFromBaggage_Sampled_MalFormed() + { + var baggage = BaggageHeader.Create(new List> + { + {"sentry-trace_id", "43365712692146d08ee11a729dfbcaca"}, + {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, + {"sentry-sample_rate", "1.0"}, + {"sentry-sampled", "foo"}, + }); + + var dsc = baggage.CreateDynamicSamplingContext(); + + Assert.Null(dsc); + } + [Fact] public void CreateFromBaggage_Valid_Minimum() { @@ -168,6 +186,7 @@ public void CreateFromBaggage_Valid_Complete() { {"sentry-trace_id", "43365712692146d08ee11a729dfbcaca"}, {"sentry-public_key", "d4d82fc1c2c4032a83f3a29aa3a3aff"}, + {"sentry-sampled", "true"}, {"sentry-sample_rate", "1.0"}, {"sentry-release", "test@1.0.0+abc"}, {"sentry-environment", "production"}, @@ -178,9 +197,10 @@ public void CreateFromBaggage_Valid_Complete() var dsc = baggage.CreateDynamicSamplingContext(); Assert.NotNull(dsc); - Assert.Equal(7, dsc.Items.Count); + Assert.Equal(baggage.Members.Count, dsc.Items.Count); Assert.Equal("43365712692146d08ee11a729dfbcaca", Assert.Contains("trace_id", dsc.Items)); Assert.Equal("d4d82fc1c2c4032a83f3a29aa3a3aff", Assert.Contains("public_key", dsc.Items)); + Assert.Equal("true", Assert.Contains("sampled", dsc.Items)); Assert.Equal("1.0", Assert.Contains("sample_rate", dsc.Items)); Assert.Equal("test@1.0.0+abc", Assert.Contains("release", dsc.Items)); Assert.Equal("production", Assert.Contains("environment", dsc.Items)); @@ -210,8 +230,11 @@ public void ToBaggageHeader() Assert.Equal(original.Members, result.Members); } - [Fact] - public void CreateFromTransaction() + [Theory] + [InlineData(false)] + [InlineData(true)] + [InlineData(null)] + public void CreateFromTransaction(bool? isSampled) { var options = new SentryOptions { @@ -230,7 +253,7 @@ public void CreateFromTransaction() { Name = "GET /person/{id}", NameSource = TransactionNameSource.Route, - IsSampled = true, + IsSampled = isSampled, SampleRate = 0.5, User = { @@ -241,9 +264,17 @@ public void CreateFromTransaction() var dsc = transaction.CreateDynamicSamplingContext(options); Assert.NotNull(dsc); - Assert.Equal(7, dsc.Items.Count); + Assert.Equal(isSampled.HasValue ? 8 : 7, dsc.Items.Count); Assert.Equal(traceId.ToString(), Assert.Contains("trace_id", dsc.Items)); Assert.Equal("d4d82fc1c2c4032a83f3a29aa3a3aff", Assert.Contains("public_key", dsc.Items)); + if (transaction.IsSampled is { } sampled) + { + Assert.Equal(sampled ? "true" : "false", Assert.Contains("sampled", dsc.Items)); + } + else + { + Assert.DoesNotContain("sampled", dsc.Items); + } Assert.Equal("0.5", Assert.Contains("sample_rate", dsc.Items)); Assert.Equal("foo@2.4.5", Assert.Contains("release", dsc.Items)); Assert.Equal("staging", Assert.Contains("environment", dsc.Items)); diff --git a/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt b/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt index e142c331c3..5a7390c2d0 100644 --- a/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt +++ b/test/Sentry.Tests/EventProcessorTests.WithTransaction.verified.txt @@ -1,4 +1,4 @@ -[ +[ { Header: { event_id: Guid_1, @@ -10,6 +10,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_2, transaction: my transaction } diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Core3_1.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Core3_1.verified.txt index c572a61273..a67f115e35 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Core3_1.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Core3_1.verified.txt @@ -32,6 +32,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } @@ -144,6 +145,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt index 50892ef63a..b2a88282d9 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet6_0.verified.txt @@ -32,6 +32,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } @@ -144,6 +145,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt index 09055037ef..7e4c8954b0 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.DotNet7_0.verified.txt @@ -32,6 +32,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } @@ -144,6 +145,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Mono4_0.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Mono4_0.verified.txt index f879896db4..b4ed75b70a 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Mono4_0.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Mono4_0.verified.txt @@ -32,6 +32,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } @@ -144,6 +145,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } diff --git a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Net4_8.verified.txt b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Net4_8.verified.txt index 2b2d328805..04524791db 100644 --- a/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Net4_8.verified.txt +++ b/test/Sentry.Tests/HubTests.CaptureEvent_ActiveTransaction_UnhandledExceptionTransactionEndedAsCrashed.Net4_8.verified.txt @@ -32,6 +32,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } @@ -144,6 +145,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_3, transaction: my transaction } diff --git a/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt b/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt index bba787fc3f..1392e2d68c 100644 --- a/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt +++ b/test/Sentry.Tests/TransactionProcessorTests.Discard.verified.txt @@ -1,4 +1,4 @@ -[ +[ { Header: { event_id: Guid_1, @@ -10,6 +10,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_2, transaction: my transaction } diff --git a/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt b/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt index 5b2e472532..894eb09320 100644 --- a/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt +++ b/test/Sentry.Tests/TransactionProcessorTests.Simple.verified.txt @@ -1,4 +1,4 @@ -[ +[ { Header: { event_id: Guid_1, @@ -10,6 +10,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_2, transaction: my transaction } @@ -51,6 +52,7 @@ public_key: d4d82fc1c2c4032a83f3a29aa3a3aff, release: release, sample_rate: 1, + sampled: true, trace_id: Guid_2, transaction: my transaction }