Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2d57966
Finish idle transaction using timeout option
SeanFeldman Jun 27, 2023
8f9e7a5
Ensure timer is cancelled when either triggered or transaction ends n…
SeanFeldman Jun 27, 2023
7254d6e
Add verification
SeanFeldman Jun 27, 2023
fc734ed
Remove destructor
SeanFeldman Jun 27, 2023
b625620
Update CHANGELOG.md
SeanFeldman Jun 27, 2023
1a03d78
Approve API changes
SeanFeldman Jun 27, 2023
5ca5599
Fix .NET 3.1 approval
SeanFeldman Jun 27, 2023
29ce332
Move idle timeout handling to the correct constructor
SeanFeldman Jun 27, 2023
8a1d79b
Add support for IdleTimeout to SentryOptions
SeanFeldman Jun 27, 2023
81950f9
Use SentryOptions configured IdleTimeout with TransactionTracer
SeanFeldman Jun 27, 2023
b653afc
Approve API changes for SentryOptions and updated TransactionTracer
SeanFeldman Jun 27, 2023
c36453c
Remove overload constructor in favor of resolving options from the hub
SeanFeldman Jul 5, 2023
908bdc1
Use explicit cast to determine if Hub type is used
SeanFeldman Jul 5, 2023
59101b2
Remove unnecessary member field
SeanFeldman Jul 5, 2023
af05fee
Avoid race condition
SeanFeldman Jul 5, 2023
e1dfb95
Remove IDisposable and dispose timer if the idle timeout timer has be…
SeanFeldman Jul 5, 2023
5262161
Merge remote-tracking branch 'origin/main' into 1074-transacton-idle-…
SeanFeldman Jul 10, 2023
1695110
Update src/Sentry/TransactionTracer.cs
SeanFeldman Jul 10, 2023
4c691b8
Do not finish transaction that is marked as a Sentry OTel transaction
SeanFeldman Jul 11, 2023
3db4a20
Do not complete OTel transaction
SeanFeldman Jul 12, 2023
696f636
Remove duplicated AssertionScope
SeanFeldman Jul 12, 2023
7e88587
Verify transaction was marked as a sentry request
SeanFeldman Jul 14, 2023
6701ad7
Remove redundant test (covered by TransactionTests)
SeanFeldman Jul 14, 2023
31f7826
Allow transaction idle timeout override
SeanFeldman Jul 17, 2023
dba21ec
Defer first execution until idle timeout kicks in
SeanFeldman Jul 17, 2023
f32c63a
Verify idle timeout override works
SeanFeldman Jul 17, 2023
060a8b9
Fix test name and tweak times
SeanFeldman Jul 18, 2023
3242e07
EXPERIMENTAL - increase delay to a few seconds
SeanFeldman Jul 18, 2023
6f49243
Apply suggestions from code review
SeanFeldman Jul 18, 2023
432b594
Update public API
SeanFeldman Jul 18, 2023
bdb7ed7
Take into consideration Sentry options provided idle timeout (acciden…
SeanFeldman Jul 18, 2023
76bf453
Simplification
SeanFeldman Jul 19, 2023
f8c5913
Apply suggestions from code review
SeanFeldman Jul 20, 2023
ea7db6f
Update src/Sentry/TransactionTracer.cs
SeanFeldman Jul 20, 2023
ebd70ff
Fix code review suggestions
SeanFeldman Jul 20, 2023
6d9e1d1
Update CONTRIBUTING.md
jamescrosswell Jul 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Symbolication for Single File Apps ([#2425](https://github.com/getsentry/sentry-dotnet/pull/2425))
- Add binding to `SwiftAsyncStacktraces` on iOS ([#2436](https://github.com/getsentry/sentry-dotnet/pull/2436))
- Support transaction finishing automatically with 'idle timeout' ([#2452](https://github.com/getsentry/sentry-dotnet/pull/2452))

### Fixes

Expand Down
2 changes: 1 addition & 1 deletion src/Sentry/Internal/Hub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ internal ITransaction StartTransaction(
IReadOnlyDictionary<string, object?> customSamplingContext,
DynamicSamplingContext? dynamicSamplingContext)
{
var transaction = new TransactionTracer(this, context);
var transaction = new TransactionTracer(this, context, _options.IdleTimeout);

// If the hub is disabled, we will always sample out. In other words, starting a transaction
// after disposing the hub will result in that transaction not being sent to Sentry.
Expand Down
5 changes: 5 additions & 0 deletions src/Sentry/SentryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,11 @@ public bool JsonPreserveReferences
/// </summary>
internal bool InitNativeSdks { get; set; } = true;

/// <summary>
/// Idling transaction timeout after which transaction will be finished automatically.
/// </summary>
public TimeSpan? IdleTimeout { get; set; }

/// <summary>
/// Creates a new instance of <see cref="SentryOptions"/>
/// </summary>
Expand Down
43 changes: 41 additions & 2 deletions src/Sentry/TransactionTracer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ namespace Sentry;
/// <summary>
/// Transaction tracer.
/// </summary>
public class TransactionTracer : ITransaction, IHasDistribution, IHasTransactionNameSource, IHasMeasurements
public class TransactionTracer : ITransaction, IHasDistribution, IHasTransactionNameSource, IHasMeasurements, IDisposable
{
private readonly IHub _hub;
private readonly TimeSpan? _idleTimeout;
private readonly Timer? _idleTimer;
private readonly SentryStopwatch _stopwatch = SentryStopwatch.StartNew();

/// <inheritdoc />
Expand Down Expand Up @@ -198,9 +200,15 @@ public TransactionTracer(IHub hub, string name, string operation, TransactionNam
/// <summary>
/// Initializes an instance of <see cref="TransactionTracer"/>.
/// </summary>
public TransactionTracer(IHub hub, ITransactionContext context)
public TransactionTracer(IHub hub, ITransactionContext context) : this (hub, context, null) {}

/// <summary>
/// Initializes an instance of <see cref="TransactionTracer"/>.
/// </summary>
public TransactionTracer(IHub hub, ITransactionContext context, TimeSpan? idleTimeout)
{
_hub = hub;
_idleTimeout = idleTimeout;
Name = context.Name;
NameSource = context is IHasTransactionNameSource c ? c.NameSource : TransactionNameSource.Custom;
Operation = context.Operation;
Expand All @@ -210,6 +218,19 @@ public TransactionTracer(IHub hub, ITransactionContext context)
Description = context.Description;
Status = context.Status;
IsSampled = context.IsSampled;

if (idleTimeout != null)
{
_idleTimer = new Timer(state =>
{
if (state is not TransactionTracer transactionTracer)
{
return;
}

transactionTracer.Finish(Status ?? SpanStatus.Ok);
}, this, TimeSpan.Zero, idleTimeout.Value);
}
}

/// <inheritdoc />
Expand Down Expand Up @@ -260,6 +281,8 @@ public ISpan StartChild(string operation) =>
/// <inheritdoc />
public void Finish()
{
_idleTimer?.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);

TransactionProfiler?.Finish();
Status ??= SpanStatus.Ok;
EndTimestamp = _stopwatch.CurrentDateTimeOffset;
Expand Down Expand Up @@ -307,4 +330,20 @@ public void Finish(Exception exception) =>
TraceId,
SpanId,
IsSampled);

/// <summary>Releases the unmanaged resources.</summary>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_idleTimer?.Dispose();
}
}

/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
6 changes: 5 additions & 1 deletion test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ namespace Sentry
public System.Collections.Generic.IList<Sentry.SubstringOrRegexPattern> FailedRequestTargets { get; set; }
public System.TimeSpan FlushTimeout { get; set; }
public System.Net.IWebProxy? HttpProxy { get; set; }
public System.TimeSpan? IdleTimeout { get; set; }
public System.TimeSpan InitCacheFlushTimeout { get; set; }
public bool IsEnvironmentUser { get; set; }
public bool IsGlobalModeEnabled { get; set; }
Expand Down Expand Up @@ -1060,9 +1061,10 @@ namespace Sentry
public System.Collections.Generic.IReadOnlyDictionary<string, object?> CustomSamplingContext { get; }
public Sentry.ITransactionContext TransactionContext { get; }
}
public class TransactionTracer : Sentry.IEventLike, Sentry.IHasBreadcrumbs, Sentry.IHasExtra, Sentry.IHasTags, Sentry.IHasTransactionNameSource, Sentry.ISpan, Sentry.ISpanContext, Sentry.ISpanData, Sentry.ITransaction, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext
public class TransactionTracer : Sentry.IEventLike, Sentry.IHasBreadcrumbs, Sentry.IHasExtra, Sentry.IHasTags, Sentry.IHasTransactionNameSource, Sentry.ISpan, Sentry.ISpanContext, Sentry.ISpanData, Sentry.ITransaction, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext, System.IDisposable
{
public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context) { }
public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context, System.TimeSpan? idleTimeout) { }
public TransactionTracer(Sentry.IHub hub, string name, string operation) { }
public TransactionTracer(Sentry.IHub hub, string name, string operation, Sentry.TransactionNameSource nameSource) { }
public System.Collections.Generic.IReadOnlyCollection<Sentry.Breadcrumb> Breadcrumbs { get; }
Expand Down Expand Up @@ -1095,6 +1097,8 @@ namespace Sentry
public Sentry.SentryId TraceId { get; }
public Sentry.User User { get; set; }
public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { }
public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
public void Finish() { }
public void Finish(Sentry.SpanStatus status) { }
public void Finish(System.Exception exception) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ namespace Sentry
public System.Collections.Generic.IList<Sentry.SubstringOrRegexPattern> FailedRequestTargets { get; set; }
public System.TimeSpan FlushTimeout { get; set; }
public System.Net.IWebProxy? HttpProxy { get; set; }
public System.TimeSpan? IdleTimeout { get; set; }
public System.TimeSpan InitCacheFlushTimeout { get; set; }
public bool IsEnvironmentUser { get; set; }
public bool IsGlobalModeEnabled { get; set; }
Expand Down Expand Up @@ -1061,9 +1062,10 @@ namespace Sentry
public System.Collections.Generic.IReadOnlyDictionary<string, object?> CustomSamplingContext { get; }
public Sentry.ITransactionContext TransactionContext { get; }
}
public class TransactionTracer : Sentry.IEventLike, Sentry.IHasBreadcrumbs, Sentry.IHasExtra, Sentry.IHasTags, Sentry.IHasTransactionNameSource, Sentry.ISpan, Sentry.ISpanContext, Sentry.ISpanData, Sentry.ITransaction, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext
public class TransactionTracer : Sentry.IEventLike, Sentry.IHasBreadcrumbs, Sentry.IHasExtra, Sentry.IHasTags, Sentry.IHasTransactionNameSource, Sentry.ISpan, Sentry.ISpanContext, Sentry.ISpanData, Sentry.ITransaction, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext, System.IDisposable
{
public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context) { }
public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context, System.TimeSpan? idleTimeout) { }
public TransactionTracer(Sentry.IHub hub, string name, string operation) { }
public TransactionTracer(Sentry.IHub hub, string name, string operation, Sentry.TransactionNameSource nameSource) { }
public System.Collections.Generic.IReadOnlyCollection<Sentry.Breadcrumb> Breadcrumbs { get; }
Expand Down Expand Up @@ -1096,6 +1098,8 @@ namespace Sentry
public Sentry.SentryId TraceId { get; }
public Sentry.User User { get; set; }
public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { }
public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
public void Finish() { }
public void Finish(Sentry.SpanStatus status) { }
public void Finish(System.Exception exception) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ namespace Sentry
public System.Collections.Generic.IList<Sentry.SubstringOrRegexPattern> FailedRequestTargets { get; set; }
public System.TimeSpan FlushTimeout { get; set; }
public System.Net.IWebProxy? HttpProxy { get; set; }
public System.TimeSpan? IdleTimeout { get; set; }
public System.TimeSpan InitCacheFlushTimeout { get; set; }
public bool IsEnvironmentUser { get; set; }
public bool IsGlobalModeEnabled { get; set; }
Expand Down Expand Up @@ -1061,9 +1062,10 @@ namespace Sentry
public System.Collections.Generic.IReadOnlyDictionary<string, object?> CustomSamplingContext { get; }
public Sentry.ITransactionContext TransactionContext { get; }
}
public class TransactionTracer : Sentry.IEventLike, Sentry.IHasBreadcrumbs, Sentry.IHasExtra, Sentry.IHasTags, Sentry.IHasTransactionNameSource, Sentry.ISpan, Sentry.ISpanContext, Sentry.ISpanData, Sentry.ITransaction, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext
public class TransactionTracer : Sentry.IEventLike, Sentry.IHasBreadcrumbs, Sentry.IHasExtra, Sentry.IHasTags, Sentry.IHasTransactionNameSource, Sentry.ISpan, Sentry.ISpanContext, Sentry.ISpanData, Sentry.ITransaction, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext, System.IDisposable
{
public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context) { }
public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context, System.TimeSpan? idleTimeout) { }
public TransactionTracer(Sentry.IHub hub, string name, string operation) { }
public TransactionTracer(Sentry.IHub hub, string name, string operation, Sentry.TransactionNameSource nameSource) { }
public System.Collections.Generic.IReadOnlyCollection<Sentry.Breadcrumb> Breadcrumbs { get; }
Expand Down Expand Up @@ -1096,6 +1098,8 @@ namespace Sentry
public Sentry.SentryId TraceId { get; }
public Sentry.User User { get; set; }
public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { }
public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
public void Finish() { }
public void Finish(Sentry.SpanStatus status) { }
public void Finish(System.Exception exception) { }
Expand Down
6 changes: 5 additions & 1 deletion test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,7 @@ namespace Sentry
public System.Collections.Generic.IList<Sentry.SubstringOrRegexPattern> FailedRequestTargets { get; set; }
public System.TimeSpan FlushTimeout { get; set; }
public System.Net.IWebProxy? HttpProxy { get; set; }
public System.TimeSpan? IdleTimeout { get; set; }
public System.TimeSpan InitCacheFlushTimeout { get; set; }
public bool IsEnvironmentUser { get; set; }
public bool IsGlobalModeEnabled { get; set; }
Expand Down Expand Up @@ -1059,9 +1060,10 @@ namespace Sentry
public System.Collections.Generic.IReadOnlyDictionary<string, object?> CustomSamplingContext { get; }
public Sentry.ITransactionContext TransactionContext { get; }
}
public class TransactionTracer : Sentry.IEventLike, Sentry.IHasBreadcrumbs, Sentry.IHasExtra, Sentry.IHasTags, Sentry.IHasTransactionNameSource, Sentry.ISpan, Sentry.ISpanContext, Sentry.ISpanData, Sentry.ITransaction, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext
public class TransactionTracer : Sentry.IEventLike, Sentry.IHasBreadcrumbs, Sentry.IHasExtra, Sentry.IHasTags, Sentry.IHasTransactionNameSource, Sentry.ISpan, Sentry.ISpanContext, Sentry.ISpanData, Sentry.ITransaction, Sentry.ITransactionContext, Sentry.ITransactionData, Sentry.Protocol.ITraceContext, System.IDisposable
{
public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context) { }
public TransactionTracer(Sentry.IHub hub, Sentry.ITransactionContext context, System.TimeSpan? idleTimeout) { }
public TransactionTracer(Sentry.IHub hub, string name, string operation) { }
public TransactionTracer(Sentry.IHub hub, string name, string operation, Sentry.TransactionNameSource nameSource) { }
public System.Collections.Generic.IReadOnlyCollection<Sentry.Breadcrumb> Breadcrumbs { get; }
Expand Down Expand Up @@ -1094,6 +1096,8 @@ namespace Sentry
public Sentry.SentryId TraceId { get; }
public Sentry.User User { get; set; }
public void AddBreadcrumb(Sentry.Breadcrumb breadcrumb) { }
public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
public void Finish() { }
public void Finish(Sentry.SpanStatus status) { }
public void Finish(System.Exception exception) { }
Expand Down
32 changes: 30 additions & 2 deletions test/Sentry.Tests/HubTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1132,7 +1132,7 @@ public void CaptureTransaction_HubEnabled(bool enabled)
[Fact]
public void CaptureTransaction_Client_Gets_Hint()
{
// Arrange
// Arrange
var hub = _fixture.GetSut();

// Act
Expand All @@ -1146,7 +1146,7 @@ public void CaptureTransaction_Client_Gets_Hint()
[Fact]
public void CaptureTransaction_Client_Gets_ScopeAttachments()
{
// Arrange
// Arrange
var hub = _fixture.GetSut();
List<Attachment> attachments = new List<Attachment> {
AttachmentHelper.FakeAttachment("foo"),
Expand Down Expand Up @@ -1336,4 +1336,32 @@ public async Task WithScopeAsyncT_Works()
}

private static Scope GetCurrentScope(Hub hub) => hub.ScopeManager.GetCurrent().Key;

[Fact]
public async Task IdleTimeout_auto_finishes_transaction()
{
// Arrange
_fixture.Options.IdleTimeout = TimeSpan.FromMilliseconds(2);
var hub = _fixture.GetSut();
var context = new TransactionContext(
SpanId.Create(),
SpanId.Create(),
SentryId.Create(),
"name",
"operation",
"description",
SpanStatus.Ok,
null,
true,
TransactionNameSource.Component
);

// Act
var transaction = hub.StartTransaction(context);

await Task.Delay(TimeSpan.FromMilliseconds(5));

// Assert
transaction.IsFinished.Should().BeTrue();
}
}
25 changes: 25 additions & 0 deletions test/Sentry.Tests/Protocol/TransactionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -504,4 +504,29 @@ public void ISpan_GetTransaction_FromSpan()
// Assert
Assert.Same(transaction, result);
}

[Fact]
public async Task Idle_transaction_should_finish_with_idle_timeout_specified()
{
// Arrange
var context = new TransactionContext(
SpanId.Create(),
SpanId.Create(),
SentryId.Create(),
"my name",
"my operation",
"description",
SpanStatus.Ok,
null,
true,
TransactionNameSource.Component
);
var transaction = new TransactionTracer(DisabledHub.Instance, context, TimeSpan.FromMilliseconds(2));

// Act
await Task.Delay(5);

// Assert
transaction.IsFinished.Should().BeTrue();
}
}
7 changes: 7 additions & 0 deletions test/Sentry.Tests/SentryOptionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,11 @@ public void FailedRequestTargets_ByDefault_MatchesAnyUrl()
var sut = new SentryOptions();
Assert.Contains(".*", sut.FailedRequestTargets);
}

[Fact]
public void IdleTimeout_ByDefault_IsNull()
{
var sut = new SentryOptions();
sut.IdleTimeout.IsNull();
}
}