diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cc5ac8e26..4b171ed991 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- The SDK now provides and overload of `ContinueTrace` that accepts headers as `string` ([#2601](https://github.com/getsentry/sentry-dotnet/pull/2601)) - Sentry tracing middleware now gets configured automatically ([#2602](https://github.com/getsentry/sentry-dotnet/pull/2602)) ### Dependencies @@ -11,6 +12,7 @@ - Bump CLI from v2.20.6 to v2.20.7 ([#2604](https://github.com/getsentry/sentry-dotnet/pull/2604)) - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2207) - [diff](https://github.com/getsentry/sentry-cli/compare/2.20.6...2.20.7) + ## 3.39.1 ### Fixes diff --git a/src/Sentry/Extensibility/DisabledHub.cs b/src/Sentry/Extensibility/DisabledHub.cs index 6ea3e66da2..8a5ef6c8d2 100644 --- a/src/Sentry/Extensibility/DisabledHub.cs +++ b/src/Sentry/Extensibility/DisabledHub.cs @@ -80,7 +80,20 @@ public void BindException(Exception exception, ISpan span) public BaggageHeader? GetBaggage() => null; /// - /// Returns null. + /// Returns sampled out transaction context. + /// + public TransactionContext ContinueTrace( + string? traceHeader, + string? baggageHeader, + string? name = null, + string? operation = null) + { + // Transactions from DisabledHub are always sampled out + return new TransactionContext( name ?? string.Empty, operation ?? string.Empty, false); + } + + /// + /// Returns sampled out transaction context. /// public TransactionContext ContinueTrace( SentryTraceHeader? traceHeader, diff --git a/src/Sentry/Extensibility/HubAdapter.cs b/src/Sentry/Extensibility/HubAdapter.cs index 6989cd1ac9..d9e86363f4 100644 --- a/src/Sentry/Extensibility/HubAdapter.cs +++ b/src/Sentry/Extensibility/HubAdapter.cs @@ -113,6 +113,17 @@ public void BindException(Exception exception, ISpan span) => public BaggageHeader? GetBaggage() => SentrySdk.GetBaggage(); + /// + /// Forwards the call to . + /// + [DebuggerStepThrough] + public TransactionContext ContinueTrace( + string? traceHeader, + string? baggageHeader, + string? name = null, + string? operation = null) + => SentrySdk.ContinueTrace(traceHeader, baggageHeader, name, operation); + /// /// Forwards the call to . /// diff --git a/src/Sentry/IHub.cs b/src/Sentry/IHub.cs index 570e60b499..f2c20b1aee 100644 --- a/src/Sentry/IHub.cs +++ b/src/Sentry/IHub.cs @@ -49,6 +49,18 @@ ITransaction StartTransaction( /// BaggageHeader? GetBaggage(); + /// + /// Continues a trace based on HTTP header values provided as strings. + /// + /// + /// If no "sentry-trace" header is provided a random trace ID and span ID is created. + /// + TransactionContext ContinueTrace( + string? traceHeader, + string? baggageHeader, + string? name = null, + string? operation = null); + /// /// Continues a trace based on HTTP header values. /// diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index e49db790fa..4a426f6da2 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -219,6 +219,27 @@ public BaggageHeader GetBaggage() return propagationContext.GetOrCreateDynamicSamplingContext(_options).ToBaggageHeader(); } + public TransactionContext ContinueTrace( + string? traceHeader, + string? baggageHeader, + string? name = null, + string? operation = null) + { + SentryTraceHeader? sentryTraceHeader = null; + if (traceHeader is not null) + { + sentryTraceHeader = SentryTraceHeader.Parse(traceHeader); + } + + BaggageHeader? sentryBaggageHeader = null; + if (baggageHeader is not null) + { + sentryBaggageHeader = BaggageHeader.TryParse(baggageHeader, onlySentry: true); + } + + return ContinueTrace(sentryTraceHeader, sentryBaggageHeader, name, operation); + } + public TransactionContext ContinueTrace( SentryTraceHeader? traceHeader, BaggageHeader? baggageHeader, diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs index 723a220241..c0bb979b23 100644 --- a/src/Sentry/SentrySdk.cs +++ b/src/Sentry/SentrySdk.cs @@ -624,6 +624,20 @@ public static void BindException(Exception exception, ISpan span) public static BaggageHeader? GetBaggage() => CurrentHub.GetBaggage(); + /// + /// Continues a trace based on HTTP header values provided as strings. + /// + /// + /// If no "sentry-trace" header is provided a random trace ID and span ID is created. + /// + [DebuggerStepThrough] + public static TransactionContext ContinueTrace( + string? traceHeader, + string? baggageHeader, + string? name = null, + string? operation = null) + => CurrentHub.ContinueTrace(traceHeader, baggageHeader, name, operation); + /// /// Continues a trace based on HTTP header values. /// diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt index 27e6905bae..b09c5062dd 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt @@ -231,6 +231,7 @@ namespace Sentry void BindException(System.Exception exception, Sentry.ISpan span); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null); + Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null); void EndSession(Sentry.SessionEndStatus status = 0); Sentry.BaggageHeader? GetBaggage(); Sentry.ISpan? GetSpan(); @@ -760,6 +761,7 @@ namespace Sentry public static void ConfigureScope(System.Action configureScope) { } public static System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } public static Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } + public static Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } public static void EndSession(Sentry.SessionEndStatus status = 0) { } public static void Flush() { } public static void Flush(System.TimeSpan timeout) { } @@ -1236,6 +1238,7 @@ namespace Sentry.Extensibility public void ConfigureScope(System.Action configureScope) { } public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } public Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } + public Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } public void Dispose() { } public void EndSession(Sentry.SessionEndStatus status = 0) { } public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } @@ -1277,6 +1280,7 @@ namespace Sentry.Extensibility public void ConfigureScope(System.Action configureScope) { } public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } public Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } + public Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } public void EndSession(Sentry.SessionEndStatus status = 0) { } public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } public Sentry.BaggageHeader? GetBaggage() { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 875f2835fc..e87375aed5 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -231,6 +231,7 @@ namespace Sentry void BindException(System.Exception exception, Sentry.ISpan span); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null); + Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null); void EndSession(Sentry.SessionEndStatus status = 0); Sentry.BaggageHeader? GetBaggage(); Sentry.ISpan? GetSpan(); @@ -761,6 +762,7 @@ namespace Sentry public static void ConfigureScope(System.Action configureScope) { } public static System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } public static Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } + public static Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } public static void EndSession(Sentry.SessionEndStatus status = 0) { } public static void Flush() { } public static void Flush(System.TimeSpan timeout) { } @@ -1237,6 +1239,7 @@ namespace Sentry.Extensibility public void ConfigureScope(System.Action configureScope) { } public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } public Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } + public Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } public void Dispose() { } public void EndSession(Sentry.SessionEndStatus status = 0) { } public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } @@ -1278,6 +1281,7 @@ namespace Sentry.Extensibility public void ConfigureScope(System.Action configureScope) { } public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } public Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } + public Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } public void EndSession(Sentry.SessionEndStatus status = 0) { } public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } public Sentry.BaggageHeader? GetBaggage() { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 875f2835fc..e87375aed5 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -231,6 +231,7 @@ namespace Sentry void BindException(System.Exception exception, Sentry.ISpan span); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null); + Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null); void EndSession(Sentry.SessionEndStatus status = 0); Sentry.BaggageHeader? GetBaggage(); Sentry.ISpan? GetSpan(); @@ -761,6 +762,7 @@ namespace Sentry public static void ConfigureScope(System.Action configureScope) { } public static System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } public static Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } + public static Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } public static void EndSession(Sentry.SessionEndStatus status = 0) { } public static void Flush() { } public static void Flush(System.TimeSpan timeout) { } @@ -1237,6 +1239,7 @@ namespace Sentry.Extensibility public void ConfigureScope(System.Action configureScope) { } public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } public Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } + public Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } public void Dispose() { } public void EndSession(Sentry.SessionEndStatus status = 0) { } public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } @@ -1278,6 +1281,7 @@ namespace Sentry.Extensibility public void ConfigureScope(System.Action configureScope) { } public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } public Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } + public Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } public void EndSession(Sentry.SessionEndStatus status = 0) { } public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } public Sentry.BaggageHeader? GetBaggage() { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index dd4c09e8ed..a3174ce82f 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -230,6 +230,7 @@ namespace Sentry void BindException(System.Exception exception, Sentry.ISpan span); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null); + Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null); void EndSession(Sentry.SessionEndStatus status = 0); Sentry.BaggageHeader? GetBaggage(); Sentry.ISpan? GetSpan(); @@ -759,6 +760,7 @@ namespace Sentry public static void ConfigureScope(System.Action configureScope) { } public static System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } public static Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } + public static Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } public static void EndSession(Sentry.SessionEndStatus status = 0) { } public static void Flush() { } public static void Flush(System.TimeSpan timeout) { } @@ -1235,6 +1237,7 @@ namespace Sentry.Extensibility public void ConfigureScope(System.Action configureScope) { } public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } public Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } + public Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } public void Dispose() { } public void EndSession(Sentry.SessionEndStatus status = 0) { } public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } @@ -1276,6 +1279,7 @@ namespace Sentry.Extensibility public void ConfigureScope(System.Action configureScope) { } public System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope) { } public Sentry.TransactionContext ContinueTrace(Sentry.SentryTraceHeader? traceHeader, Sentry.BaggageHeader? baggageHeader, string? name = null, string? operation = null) { } + public Sentry.TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, string? operation = null) { } public void EndSession(Sentry.SessionEndStatus status = 0) { } public System.Threading.Tasks.Task FlushAsync(System.TimeSpan timeout) { } public Sentry.BaggageHeader? GetBaggage() { } diff --git a/test/Sentry.Tests/HubTests.cs b/test/Sentry.Tests/HubTests.cs index 900c415ed5..493dd003be 100644 --- a/test/Sentry.Tests/HubTests.cs +++ b/test/Sentry.Tests/HubTests.cs @@ -915,13 +915,44 @@ public void ContinueTrace_SetsPropagationContextAndReturnsTransactionContext() var propagationContext = new SentryPropagationContext( SentryId.Parse("43365712692146d08ee11a729dfbcaca"), SpanId.Parse("1000000000000000")); hub.ConfigureScope(scope => scope.PropagationContext = propagationContext); + var traceHeader = new SentryTraceHeader(SentryId.Parse("5bd5f6d346b442dd9177dce9302fd737"), SpanId.Parse("2000000000000000"), null); var baggageHeader = BaggageHeader.Create(new List> { - {"sentry-public_key", "49d0f7386ad645858ae85020e393bef3"} + {"sentry-trace_id", "5bd5f6d346b442dd9177dce9302fd737"}, + {"sentry-public_key", "49d0f7386ad645858ae85020e393bef3"}, + {"sentry-sample_rate", "1.0"} + }); + + hub.ConfigureScope(scope => scope.PropagationContext.TraceId.Should().Be("43365712692146d08ee11a729dfbcaca")); // Sanity check + + // Act + var transactionContext = hub.ContinueTrace(traceHeader, baggageHeader, "test-name"); + + // Assert + hub.ConfigureScope(scope => + { + scope.PropagationContext.TraceId.Should().Be(SentryId.Parse("5bd5f6d346b442dd9177dce9302fd737")); + scope.PropagationContext.ParentSpanId.Should().Be(SpanId.Parse("2000000000000000")); + Assert.NotNull(scope.PropagationContext._dynamicSamplingContext); }); + transactionContext.TraceId.Should().Be(SentryId.Parse("5bd5f6d346b442dd9177dce9302fd737")); + transactionContext.ParentSpanId.Should().Be(SpanId.Parse("2000000000000000")); + } + + [Fact] + public void ContinueTrace_ReceivesHeadersAsStrings_SetsPropagationContextAndReturnsTransactionContext() + { + // Arrange + var hub = _fixture.GetSut(); + var propagationContext = new SentryPropagationContext( + SentryId.Parse("43365712692146d08ee11a729dfbcaca"), SpanId.Parse("1000000000000000")); + hub.ConfigureScope(scope => scope.PropagationContext = propagationContext); + var traceHeader = "5bd5f6d346b442dd9177dce9302fd737-2000000000000000"; + var baggageHeader ="sentry-trace_id=5bd5f6d346b442dd9177dce9302fd737, sentry-public_key=49d0f7386ad645858ae85020e393bef3, sentry-sample_rate=1.0"; + hub.ConfigureScope(scope => scope.PropagationContext.TraceId.Should().Be("43365712692146d08ee11a729dfbcaca")); // Sanity check // Act @@ -932,6 +963,7 @@ public void ContinueTrace_SetsPropagationContextAndReturnsTransactionContext() { scope.PropagationContext.TraceId.Should().Be(SentryId.Parse("5bd5f6d346b442dd9177dce9302fd737")); scope.PropagationContext.ParentSpanId.Should().Be(SpanId.Parse("2000000000000000")); + Assert.NotNull(scope.PropagationContext._dynamicSamplingContext); }); transactionContext.TraceId.Should().Be(SentryId.Parse("5bd5f6d346b442dd9177dce9302fd737"));