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()
{