diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bcd7a6601..575a3416c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ ### Features +- Allow setting the active span on the scope ([#2364](https://github.com/getsentry/sentry-dotnet/pull/2364)) + - Note: Obsoletes the `Scope.GetSpan` method in favor of a `Scope.Span` property (which now has a setter as well). + - Add tag filters to `SentryOptions` ([#2367](https://github.com/getsentry/sentry-dotnet/pull/2367)) ### Dependencies diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index 81b21c7cf5..5e8f38c6d4 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -182,11 +182,7 @@ public void BindException(Exception exception, ISpan span) _ = ExceptionToSpanMap.GetValue(exception, _ => span); } - public ISpan? GetSpan() - { - var (currentScope, _) = ScopeManager.GetCurrent(); - return currentScope.GetSpan(); - } + public ISpan? GetSpan() => ScopeManager.GetCurrent().Key.Span; public SentryTraceHeader? GetTraceHeader() => GetSpan()?.GetTraceHeader(); @@ -286,7 +282,7 @@ public void EndSession(SessionEndStatus status = SessionEndStatus.Exited) => } // Otherwise just get the currently active span on the scope (unless it's sampled out) - if (scope.GetSpan() is { IsSampled: not false } span) + if (scope.Span is { IsSampled: not false } span) { return span; } diff --git a/src/Sentry/Scope.cs b/src/Sentry/Scope.cs index f8c053378c..e07e3d0eac 100644 --- a/src/Sentry/Scope.cs +++ b/src/Sentry/Scope.cs @@ -521,10 +521,34 @@ internal void Evaluate() } /// - /// Gets the currently ongoing (not finished) span or null if none available. - /// This relies on the transactions being manually set on the scope via . + /// Obsolete. Use the property instead. /// - public ISpan? GetSpan() => Transaction?.GetLastActiveSpan() ?? Transaction; + [Obsolete("Use the Span property instead. This method will be removed in a future release.")] + public ISpan? GetSpan() => Span; + + private ISpan? _span; + + /// + /// Gets or sets the active span, or null if none available. + /// + /// + /// If a span has been set on this property, it will become the active span until it is finished. + /// Otherwise, the active span is the latest unfinished span on the transaction, presuming a transaction + /// was set on the scope via the property. + /// + public ISpan? Span + { + get + { + if (_span?.IsFinished is false) + { + return _span; + } + + return Transaction?.GetLastActiveSpan() ?? Transaction; + } + set => _span = value; + } internal void ResetTransaction(ITransaction? expectedCurrentTransaction) => Interlocked.CompareExchange(ref _transaction, null, expectedCurrentTransaction); diff --git a/test/Sentry.DiagnosticSource.Tests/Integration/SQLite/SentryDiagnosticListenerTests.cs b/test/Sentry.DiagnosticSource.Tests/Integration/SQLite/SentryDiagnosticListenerTests.cs index 6727b72310..1a51c0ba22 100644 --- a/test/Sentry.DiagnosticSource.Tests/Integration/SQLite/SentryDiagnosticListenerTests.cs +++ b/test/Sentry.DiagnosticSource.Tests/Integration/SQLite/SentryDiagnosticListenerTests.cs @@ -38,11 +38,7 @@ public Fixture() } public ItemsContext NewContext() => new(_database.ContextOptions); - public ISpan GetSpan() - { - var (currentScope, _) = ScopeManager.GetCurrent(); - return currentScope.GetSpan(); - } + public ISpan GetSpan() => ScopeManager.GetCurrent().Key.Span; public ITransaction StartTransaction(IHub hub, ITransactionContext context) { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt index 5c636a570f..068bc94dfa 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt @@ -404,6 +404,7 @@ namespace Sentry public string? Release { get; set; } public Sentry.Request Request { get; set; } public Sentry.SdkVersion Sdk { get; } + public Sentry.ISpan? Span { get; set; } public System.Collections.Generic.IReadOnlyDictionary Tags { get; } public Sentry.ITransaction? Transaction { get; set; } public string? TransactionName { get; set; } @@ -417,6 +418,7 @@ namespace Sentry public void ClearAttachments() { } public void ClearBreadcrumbs() { } public Sentry.Scope Clone() { } + [System.Obsolete("Use the Span property instead. This method will be removed in a future release.")] public Sentry.ISpan? GetSpan() { } public void SetExtra(string key, object? value) { } public void SetTag(string key, string value) { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 24eb68fe28..de19abca21 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -404,6 +404,7 @@ namespace Sentry public string? Release { get; set; } public Sentry.Request Request { get; set; } public Sentry.SdkVersion Sdk { get; } + public Sentry.ISpan? Span { get; set; } public System.Collections.Generic.IReadOnlyDictionary Tags { get; } public Sentry.ITransaction? Transaction { get; set; } public string? TransactionName { get; set; } @@ -417,6 +418,7 @@ namespace Sentry public void ClearAttachments() { } public void ClearBreadcrumbs() { } public Sentry.Scope Clone() { } + [System.Obsolete("Use the Span property instead. This method will be removed in a future release.")] public Sentry.ISpan? GetSpan() { } public void SetExtra(string key, object? value) { } public void SetTag(string key, string value) { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 24eb68fe28..de19abca21 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -404,6 +404,7 @@ namespace Sentry public string? Release { get; set; } public Sentry.Request Request { get; set; } public Sentry.SdkVersion Sdk { get; } + public Sentry.ISpan? Span { get; set; } public System.Collections.Generic.IReadOnlyDictionary Tags { get; } public Sentry.ITransaction? Transaction { get; set; } public string? TransactionName { get; set; } @@ -417,6 +418,7 @@ namespace Sentry public void ClearAttachments() { } public void ClearBreadcrumbs() { } public Sentry.Scope Clone() { } + [System.Obsolete("Use the Span property instead. This method will be removed in a future release.")] public Sentry.ISpan? GetSpan() { } public void SetExtra(string key, object? value) { } public void SetTag(string key, string value) { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 1ca12cb25c..a98635723e 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -403,6 +403,7 @@ namespace Sentry public string? Release { get; set; } public Sentry.Request Request { get; set; } public Sentry.SdkVersion Sdk { get; } + public Sentry.ISpan? Span { get; set; } public System.Collections.Generic.IReadOnlyDictionary Tags { get; } public Sentry.ITransaction? Transaction { get; set; } public string? TransactionName { get; set; } @@ -416,6 +417,7 @@ namespace Sentry public void ClearAttachments() { } public void ClearBreadcrumbs() { } public Sentry.Scope Clone() { } + [System.Obsolete("Use the Span property instead. This method will be removed in a future release.")] public Sentry.ISpan? GetSpan() { } public void SetExtra(string key, object? value) { } public void SetTag(string key, string value) { } diff --git a/test/Sentry.Tests/ScopeTests.cs b/test/Sentry.Tests/ScopeTests.cs index 0d10fed586..fad7564e02 100644 --- a/test/Sentry.Tests/ScopeTests.cs +++ b/test/Sentry.Tests/ScopeTests.cs @@ -164,7 +164,7 @@ public void TransactionName_TransactionStarted_NameReturnsActualTransactionName( } [Fact] - public void GetSpan_NoSpans_ReturnsTransaction() + public void Span_NoSpans_ReturnsTransaction() { // Arrange var scope = new Scope(); @@ -172,14 +172,14 @@ public void GetSpan_NoSpans_ReturnsTransaction() scope.Transaction = transaction; // Act - var span = scope.GetSpan(); + var span = scope.Span; // Assert span.Should().Be(transaction); } [Fact] - public void GetSpan_FinishedSpans_ReturnsTransaction() + public void Span_FinishedSpans_ReturnsTransaction() { // Arrange var scope = new Scope(); @@ -191,14 +191,14 @@ public void GetSpan_FinishedSpans_ReturnsTransaction() scope.Transaction = transaction; // Act - var span = scope.GetSpan(); + var span = scope.Span; // Assert span.Should().Be(transaction); } [Fact] - public void GetSpan_ActiveSpans_ReturnsSpan() + public void Span_ActiveSpans_ReturnsSpan() { // Arrange var scope = new Scope(); @@ -210,12 +210,73 @@ public void GetSpan_ActiveSpans_ReturnsSpan() scope.Transaction = transaction; // Act - var span = scope.GetSpan(); + var span = scope.Span; // Assert span.Should().Be(activeSpan); } + [Fact] + public void Span_SetSpan_ReturnsValue() + { + // Arrange + var scope = new Scope(); + + var transaction = new TransactionTracer(DisabledHub.Instance, "foo", "_"); + var firstSpan = transaction.StartChild("123"); + var secondSpan = firstSpan.StartChild("456"); + + scope.Transaction = transaction; + + // Assert Default + scope.Span.Should().Be(secondSpan); + + // Act + scope.Span = firstSpan; + + // Assert + scope.Span.Should().Be(firstSpan); + } + + [Fact] + public void Span_SetSpanNull_ReturnsLatestOpen() + { + // Arrange + var scope = new Scope(); + + var transaction = new TransactionTracer(DisabledHub.Instance, "foo", "_"); + var firstSpan = transaction.StartChild("123"); + var secondSpan = firstSpan.StartChild("456"); + + scope.Transaction = transaction; + + // Act + scope.Span = null; + + // Assert + scope.Span.Should().Be(secondSpan); + } + + [Fact] + public void Span_SetSpanThenCloseIt_ReturnsLatestOpen() + { + // Arrange + var scope = new Scope(); + + var transaction = new TransactionTracer(DisabledHub.Instance, "foo", "_"); + var firstSpan = transaction.StartChild("123"); + var secondSpan = firstSpan.StartChild("456"); + + scope.Transaction = transaction; + + // Act + scope.Span = firstSpan; + firstSpan.Finish(); + + // Assert + scope.Span.Should().Be(secondSpan); + } + [Fact] public void AddAttachment_AddAttachments() {