diff --git a/CHANGELOG.md b/CHANGELOG.md
index 719ce0d281..f15f8897e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,8 @@
### BREAKING CHANGES
- SentryOptions.IsEnvironmentUser now defaults to false on MAUI. The means the User.Name will no longer be set, by default, to the name of the device ([#4606](https://github.com/getsentry/sentry-dotnet/pull/4606))
+- Spans and Transactions now implement `IDispose` so that they can be used with `using` statements/declarations that will automatically finish the span with a status of OK when it passes out of scope, if it has not already been finished, to be consistent with `Activity` classes when using OpenTelemetry ([#4627](https://github.com/getsentry/sentry-dotnet/pull/4627))
+- SpanTracer and TransactionTracer are still public but these are now `sealed` (see also [#4627](https://github.com/getsentry/sentry-dotnet/pull/4627))
- Remove unnecessary files from SentryCocoaFramework before packing ([#4602](https://github.com/getsentry/sentry-dotnet/pull/4602))
### Fixes
diff --git a/samples/Sentry.Samples.Console.Basic/Program.cs b/samples/Sentry.Samples.Console.Basic/Program.cs
index dfc7723796..1a2605c0ee 100644
--- a/samples/Sentry.Samples.Console.Basic/Program.cs
+++ b/samples/Sentry.Samples.Console.Basic/Program.cs
@@ -63,7 +63,7 @@
// Always try to finish the transaction successfully.
// Unhandled exceptions will fail the transaction automatically.
-// Optionally, you can try/catch the exception, and call transaction.Finish(exception) on failure.
+// Optionally, you can try/catch the exception and call transaction.Finish(exception) on failure.
transaction.Finish();
async Task FirstFunction()
@@ -79,7 +79,6 @@ async Task FirstFunction()
async Task SecondFunction()
{
var span = transaction.StartChild("function", nameof(SecondFunction));
-
try
{
// Simulate doing some work
@@ -97,26 +96,25 @@ async Task SecondFunction()
SentrySdk.Logger.LogError(static log => log.SetAttribute("method", nameof(SecondFunction)),
"Error with message: {0}", exception.Message);
}
-
- span.Finish();
+ finally
+ {
+ span.Finish();
+ }
}
async Task ThirdFunction()
{
- var span = transaction.StartChild("function", nameof(ThirdFunction));
- try
- {
- // Simulate doing some work
- await Task.Delay(100);
+ // The `using` here ensures the span gets finished when we leave this method... This is unnecessary here,
+ // since the method always throws and the span will be finished automatically when the exception is captured,
+ // but this gives you another way to ensure spans are finished.
+ using var span = transaction.StartChild("function", nameof(ThirdFunction));
- SentrySdk.Logger.LogFatal(static log => log.SetAttribute("suppress", true),
- "Crash imminent!");
+ // Simulate doing some work
+ await Task.Delay(100);
- // This is an example of an unhandled exception. It will be captured automatically.
- throw new InvalidOperationException("Something happened that crashed the app!");
- }
- finally
- {
- span.Finish();
- }
+ SentrySdk.Logger.LogFatal(static log => log.SetAttribute("suppress", true),
+ "Crash imminent!");
+
+ // This is an example of an unhandled exception. It will be captured automatically.
+ throw new InvalidOperationException("Something happened that crashed the app!");
}
diff --git a/src/Sentry/ISpan.cs b/src/Sentry/ISpan.cs
index 5cf7193a9c..8bcd06d609 100644
--- a/src/Sentry/ISpan.cs
+++ b/src/Sentry/ISpan.cs
@@ -5,7 +5,7 @@ namespace Sentry;
///
/// SpanTracer interface
///
-public interface ISpan : ISpanData
+public interface ISpan : ISpanData, IDisposable
{
///
/// Span description.
diff --git a/src/Sentry/Internal/NoOpSpan.cs b/src/Sentry/Internal/NoOpSpan.cs
index d57aa34261..75cefe68b7 100644
--- a/src/Sentry/Internal/NoOpSpan.cs
+++ b/src/Sentry/Internal/NoOpSpan.cs
@@ -85,4 +85,8 @@ public void SetMeasurement(string name, Measurement measurement)
}
public string? Origin { get; set; }
+
+ public void Dispose()
+ {
+ }
}
diff --git a/src/Sentry/SpanTracer.cs b/src/Sentry/SpanTracer.cs
index ed51d48eaa..8304eca540 100644
--- a/src/Sentry/SpanTracer.cs
+++ b/src/Sentry/SpanTracer.cs
@@ -6,10 +6,11 @@ namespace Sentry;
///
/// Transaction span tracer.
///
-public class SpanTracer : IBaseTracer, ISpan
+public sealed class SpanTracer : IBaseTracer, ISpan
{
private readonly IHub _hub;
private readonly SentryStopwatch _stopwatch = SentryStopwatch.StartNew();
+ private string? _origin;
private readonly Instrumenter _instrumenter = Instrumenter.Sentry;
@@ -190,5 +191,13 @@ internal set
_origin = value;
}
}
- private string? _origin;
+
+ ///
+ /// Automatically finishes the span at the end of a using block. This is a convenience method only. Disposing
+ /// is not required.
+ ///
+ public void Dispose()
+ {
+ Finish();
+ }
}
diff --git a/src/Sentry/TransactionTracer.cs b/src/Sentry/TransactionTracer.cs
index bdd829ff81..205585dd26 100644
--- a/src/Sentry/TransactionTracer.cs
+++ b/src/Sentry/TransactionTracer.cs
@@ -7,13 +7,14 @@ namespace Sentry;
///
/// Transaction tracer.
///
-public class TransactionTracer : IBaseTracer, ITransactionTracer
+public sealed class TransactionTracer : IBaseTracer, ITransactionTracer
{
private readonly IHub _hub;
private readonly SentryOptions? _options;
private readonly Timer? _idleTimer;
private long _cancelIdleTimeout;
private readonly SentryStopwatch _stopwatch = SentryStopwatch.StartNew();
+ private int _hasFinished = 0;
private readonly Instrumenter _instrumenter = Instrumenter.Sentry;
@@ -361,6 +362,12 @@ public void Clear()
///
public void Finish()
{
+ // TODO: Replace with InterlockedBoolean once this has been merged into version6
+ if (Interlocked.Exchange(ref _hasFinished, 1) == 1)
+ {
+ return;
+ }
+
_options?.LogDebug("Attempting to finish Transaction '{0}'.", SpanId);
if (Interlocked.Exchange(ref _cancelIdleTimeout, 0) == 1)
{
@@ -431,4 +438,19 @@ private void ReleaseSpans()
#endif
_activeSpanTracker.Clear();
}
+
+ ///
+ ///
+ /// Automatically finishes the transaction with a status of at the end of a
+ /// using block, if it has not already been finished.
+ ///
+ ///
+ /// This is the equivalent of calling when the transaction passes out of scope.
+ ///
+ ///
+ /// This is a convenience method only. Disposing is not required.
+ public void Dispose()
+ {
+ Finish();
+ }
}
diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt
index 406a853181..1f8c5f41ee 100644
--- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt
+++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt
@@ -261,7 +261,7 @@ namespace Sentry
{
Sentry.SentryUser? Create();
}
- public interface ISpan : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext
+ public interface ISpan : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext, System.IDisposable
{
new string? Description { get; set; }
new string Operation { get; set; }
@@ -291,7 +291,7 @@ namespace Sentry
{
string? Platform { get; set; }
}
- public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext
+ public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext, System.IDisposable
{
new bool? IsParentSampled { get; set; }
new string Name { get; set; }
@@ -1160,7 +1160,7 @@ namespace Sentry
OutOfRange = 15,
DataLoss = 16,
}
- public class SpanTracer : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext
+ public sealed class SpanTracer : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext, System.IDisposable
{
public SpanTracer(Sentry.IHub hub, Sentry.TransactionTracer transaction, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string operation) { }
public System.Collections.Generic.IReadOnlyDictionary Data { get; }
@@ -1178,6 +1178,7 @@ namespace Sentry
public Sentry.SpanStatus? Status { get; set; }
public System.Collections.Generic.IReadOnlyDictionary Tags { get; }
public Sentry.SentryId TraceId { get; }
+ public void Dispose() { }
public void Finish() { }
public void Finish(Sentry.SpanStatus status) { }
public void Finish(System.Exception exception) { }
@@ -1239,7 +1240,7 @@ namespace Sentry
public System.Collections.Generic.IReadOnlyDictionary CustomSamplingContext { get; }
public Sentry.ITransactionContext TransactionContext { get; }
}
- public class TransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext
+ public sealed class TransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext, System.IDisposable
{
public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context) { }
public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; }
@@ -1275,6 +1276,7 @@ namespace Sentry
public Sentry.SentryId TraceId { get; }
public Sentry.SentryUser User { get; set; }
public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { }
+ public void Dispose() { }
public void Finish() { }
public void Finish(Sentry.SpanStatus status) { }
public void Finish(System.Exception exception) { }
diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt
index 406a853181..1f8c5f41ee 100644
--- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt
+++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt
@@ -261,7 +261,7 @@ namespace Sentry
{
Sentry.SentryUser? Create();
}
- public interface ISpan : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext
+ public interface ISpan : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext, System.IDisposable
{
new string? Description { get; set; }
new string Operation { get; set; }
@@ -291,7 +291,7 @@ namespace Sentry
{
string? Platform { get; set; }
}
- public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext
+ public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext, System.IDisposable
{
new bool? IsParentSampled { get; set; }
new string Name { get; set; }
@@ -1160,7 +1160,7 @@ namespace Sentry
OutOfRange = 15,
DataLoss = 16,
}
- public class SpanTracer : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext
+ public sealed class SpanTracer : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext, System.IDisposable
{
public SpanTracer(Sentry.IHub hub, Sentry.TransactionTracer transaction, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string operation) { }
public System.Collections.Generic.IReadOnlyDictionary Data { get; }
@@ -1178,6 +1178,7 @@ namespace Sentry
public Sentry.SpanStatus? Status { get; set; }
public System.Collections.Generic.IReadOnlyDictionary Tags { get; }
public Sentry.SentryId TraceId { get; }
+ public void Dispose() { }
public void Finish() { }
public void Finish(Sentry.SpanStatus status) { }
public void Finish(System.Exception exception) { }
@@ -1239,7 +1240,7 @@ namespace Sentry
public System.Collections.Generic.IReadOnlyDictionary CustomSamplingContext { get; }
public Sentry.ITransactionContext TransactionContext { get; }
}
- public class TransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext
+ public sealed class TransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext, System.IDisposable
{
public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context) { }
public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; }
@@ -1275,6 +1276,7 @@ namespace Sentry
public Sentry.SentryId TraceId { get; }
public Sentry.SentryUser User { get; set; }
public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { }
+ public void Dispose() { }
public void Finish() { }
public void Finish(Sentry.SpanStatus status) { }
public void Finish(System.Exception exception) { }
diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt
index 406a853181..1f8c5f41ee 100644
--- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt
+++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt
@@ -261,7 +261,7 @@ namespace Sentry
{
Sentry.SentryUser? Create();
}
- public interface ISpan : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext
+ public interface ISpan : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext, System.IDisposable
{
new string? Description { get; set; }
new string Operation { get; set; }
@@ -291,7 +291,7 @@ namespace Sentry
{
string? Platform { get; set; }
}
- public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext
+ public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext, System.IDisposable
{
new bool? IsParentSampled { get; set; }
new string Name { get; set; }
@@ -1160,7 +1160,7 @@ namespace Sentry
OutOfRange = 15,
DataLoss = 16,
}
- public class SpanTracer : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext
+ public sealed class SpanTracer : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext, System.IDisposable
{
public SpanTracer(Sentry.IHub hub, Sentry.TransactionTracer transaction, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string operation) { }
public System.Collections.Generic.IReadOnlyDictionary Data { get; }
@@ -1178,6 +1178,7 @@ namespace Sentry
public Sentry.SpanStatus? Status { get; set; }
public System.Collections.Generic.IReadOnlyDictionary Tags { get; }
public Sentry.SentryId TraceId { get; }
+ public void Dispose() { }
public void Finish() { }
public void Finish(Sentry.SpanStatus status) { }
public void Finish(System.Exception exception) { }
@@ -1239,7 +1240,7 @@ namespace Sentry
public System.Collections.Generic.IReadOnlyDictionary CustomSamplingContext { get; }
public Sentry.ITransactionContext TransactionContext { get; }
}
- public class TransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext
+ public sealed class TransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext, System.IDisposable
{
public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context) { }
public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; }
@@ -1275,6 +1276,7 @@ namespace Sentry
public Sentry.SentryId TraceId { get; }
public Sentry.SentryUser User { get; set; }
public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { }
+ public void Dispose() { }
public void Finish() { }
public void Finish(Sentry.SpanStatus status) { }
public void Finish(System.Exception exception) { }
diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt
index e2a02fc89a..9eeb00ffd1 100644
--- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt
+++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt
@@ -249,7 +249,7 @@ namespace Sentry
{
Sentry.SentryUser? Create();
}
- public interface ISpan : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext
+ public interface ISpan : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpanData, Sentry.Protocol.ITraceContext, System.IDisposable
{
new string? Description { get; set; }
new string Operation { get; set; }
@@ -279,7 +279,7 @@ namespace Sentry
{
string? Platform { get; set; }
}
- public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext
+ public interface ITransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext, System.IDisposable
{
new bool? IsParentSampled { get; set; }
new string Name { get; set; }
@@ -1136,7 +1136,7 @@ namespace Sentry
OutOfRange = 15,
DataLoss = 16,
}
- public class SpanTracer : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext
+ public sealed class SpanTracer : Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.Protocol.ITraceContext, System.IDisposable
{
public SpanTracer(Sentry.IHub hub, Sentry.TransactionTracer transaction, Sentry.SpanId? parentSpanId, Sentry.SentryId traceId, string operation) { }
public System.Collections.Generic.IReadOnlyDictionary Data { get; }
@@ -1154,6 +1154,7 @@ namespace Sentry
public Sentry.SpanStatus? Status { get; set; }
public System.Collections.Generic.IReadOnlyDictionary Tags { get; }
public Sentry.SentryId TraceId { get; }
+ public void Dispose() { }
public void Finish() { }
public void Finish(Sentry.SpanStatus status) { }
public void Finish(System.Exception exception) { }
@@ -1215,7 +1216,7 @@ namespace Sentry
public System.Collections.Generic.IReadOnlyDictionary CustomSamplingContext { get; }
public Sentry.ITransactionContext TransactionContext { get; }
}
- public class TransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext
+ public sealed class TransactionTracer : Sentry.IEventLike, Sentry.IHasData, Sentry.IHasExtra, Sentry.IHasTags, Sentry.ISpan, Sentry.ISpanData, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.ITransactionTracer, Sentry.Protocol.ITraceContext, System.IDisposable
{
public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context) { }
public System.Collections.Generic.IReadOnlyCollection Breadcrumbs { get; }
@@ -1251,6 +1252,7 @@ namespace Sentry
public Sentry.SentryId TraceId { get; }
public Sentry.SentryUser User { get; set; }
public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { }
+ public void Dispose() { }
public void Finish() { }
public void Finish(Sentry.SpanStatus status) { }
public void Finish(System.Exception exception) { }
diff --git a/test/Sentry.Tests/SpanTracerTests.cs b/test/Sentry.Tests/SpanTracerTests.cs
index 59becf2740..9362ad160d 100644
--- a/test/Sentry.Tests/SpanTracerTests.cs
+++ b/test/Sentry.Tests/SpanTracerTests.cs
@@ -10,7 +10,7 @@ public async Task SetExtra_DataInserted_NoDataLoss()
{
// Arrange
var hub = Substitute.For();
- var transaction = new SpanTracer(hub, null, null, SentryId.Empty, "");
+ var spanTracer = new SpanTracer(hub, null, null, SentryId.Empty, "");
var evt = new ManualResetEvent(false);
var ready = new ManualResetEvent(false);
var counter = 0;
@@ -27,7 +27,7 @@ public async Task SetExtra_DataInserted_NoDataLoss()
for (var i = 0; i < amount; i++)
{
- transaction.SetExtra(Guid.NewGuid().ToString(), Guid.NewGuid());
+ spanTracer.SetExtra(Guid.NewGuid().ToString(), Guid.NewGuid());
}
})).ToList();
ready.WaitOne();
@@ -36,7 +36,38 @@ public async Task SetExtra_DataInserted_NoDataLoss()
// Arrange
// 4 tasks testing X amount should be the same amount as Extras.
- Assert.Equal(4 * amount, transaction.Extra.Count);
+ Assert.Equal(4 * amount, spanTracer.Extra.Count);
}
}
+
+ [Fact]
+ public void Dispose_TransactionIsUnfinished_FinishesTransaction()
+ {
+ // Arrange
+ var hub = Substitute.For();
+ var spanTracer = new SpanTracer(hub, null, null, SentryId.Empty, "");
+
+ // Act
+ spanTracer.Dispose();
+
+ // Assert
+ spanTracer.IsFinished.Should().BeTrue();
+ }
+
+ [Fact]
+ public void Dispose_TransactionIsFinsished_DoesNothing()
+ {
+ // Arrange
+ var hub = Substitute.For();
+ var spanTracer = new SpanTracer(hub, null, null, SentryId.Empty, "");
+ spanTracer.Finish();
+ var finishTimestamp = spanTracer.EndTimestamp;
+
+ // Act
+ spanTracer.Dispose();
+
+ // Assert
+ spanTracer.IsFinished.Should().BeTrue();
+ spanTracer.EndTimestamp.Should().Be(finishTimestamp);
+ }
}
diff --git a/test/Sentry.Tests/TransactionTracerTests.cs b/test/Sentry.Tests/TransactionTracerTests.cs
new file mode 100644
index 0000000000..904bb13369
--- /dev/null
+++ b/test/Sentry.Tests/TransactionTracerTests.cs
@@ -0,0 +1,33 @@
+namespace Sentry.Tests;
+
+public class TransactionTracerTests
+{
+ [Fact]
+ public void Dispose_Unfinished_Finishes()
+ {
+ // Arrange
+ var hub = Substitute.For();
+ var transaction = new TransactionTracer(hub, "op", "name");
+
+ // Act
+ transaction.Dispose();
+
+ // Assert
+ hub.Received(1).CaptureTransaction(Arg.Is(t => t.SpanId == transaction.SpanId));
+ }
+
+ [Fact]
+ public void Dispose_Finished_DoesNothing()
+ {
+ // Arrange
+ var hub = Substitute.For();
+ var transaction = new TransactionTracer(hub, "op", "name");
+ transaction.Finish();
+
+ // Act
+ transaction.Dispose();
+
+ // Assert
+ hub.Received(1).CaptureTransaction(Arg.Is(t => t.SpanId == transaction.SpanId));
+ }
+}