Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1a76177
CaptureFeedback now returns a SentryId
jamescrosswell Oct 9, 2025
98bf2f6
Changelog
jamescrosswell Oct 9, 2025
f58417b
Update ApiApprovalTests.Run.Net4_8.verified.txt
jamescrosswell Oct 9, 2025
04e9a56
Fixed client disposal
jamescrosswell Oct 9, 2025
0cda3df
Merge remote-tracking branch 'origin/capture-feedback-return' into ca…
jamescrosswell Oct 9, 2025
3d5e218
Added CaptureFeedbackResult
jamescrosswell Oct 15, 2025
22da5b6
Merge branch 'version6' into capture-feedback-return
jamescrosswell Oct 15, 2025
7824b1c
Updated docs/changelog
jamescrosswell Oct 15, 2025
5041de0
Format code
getsentry-bot Oct 15, 2025
4f2c694
Update ApiApprovalTests.Run.Net4_8.verified.txt
jamescrosswell Oct 15, 2025
9db3a36
Merge branch 'capture-feedback-return' of https://github.com/getsentr…
jamescrosswell Oct 15, 2025
af76402
Fixed typo
jamescrosswell Oct 16, 2025
f412257
Review feedback
jamescrosswell Oct 16, 2025
764193d
Merge branch 'version6' into capture-feedback-return
jamescrosswell Oct 16, 2025
15ac236
Removed implicit operators
jamescrosswell Oct 16, 2025
55e168d
Merge branch 'version6' into capture-feedback-return
jamescrosswell Oct 16, 2025
ea54e98
Update ApiApprovalTests.Run.Net4_8.verified.txt
jamescrosswell Oct 16, 2025
c738bc2
Merge branch 'version6' into capture-feedback-return
jamescrosswell Oct 16, 2025
3070d9d
Changed to out param
jamescrosswell Oct 20, 2025
9b5ce52
changelog
jamescrosswell Oct 20, 2025
35c9685
Update ApiApprovalTests.Run.Net4_8.verified.txt
jamescrosswell Oct 21, 2025
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 @@ -7,6 +7,7 @@
- `BreadcrumbLevel.Critical` has been renamed to `BreadcrumbLevel.Fatal` for consistency with the other Sentry SDKs ([#4605](https://github.com/getsentry/sentry-dotnet/pull/4605))
- 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))
- Remove unnecessary files from SentryCocoaFramework before packing ([#4602](https://github.com/getsentry/sentry-dotnet/pull/4602))
- CaptureFeedback now returns a `CaptureFeedbackResult` that can be used to determine whether the call succeeded or failed ([#4613](https://github.com/getsentry/sentry-dotnet/pull/4613))
- ScopeExtensions.Populate is now internal ([#4611](https://github.com/getsentry/sentry-dotnet/pull/4611))

### Fixes
Expand Down
95 changes: 95 additions & 0 deletions src/Sentry/CaptureFeedbackResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
namespace Sentry;

/// <summary>
/// The result type of the <see cref="ISentryClient.CaptureFeedback"/> method
/// </summary>
public struct CaptureFeedbackResult
Comment thread
jamescrosswell marked this conversation as resolved.
Outdated
Comment thread
jamescrosswell marked this conversation as resolved.
Outdated
{
/// <summary>
/// Creates a successful feedback capture result with the specified event Id.
/// </summary>
/// <param name="eventId"></param>
public CaptureFeedbackResult(SentryId eventId)
{
if (eventId == SentryId.Empty)
{
throw new ArgumentException("EventId cannot be empty", nameof(eventId));
}

EventId = eventId;
}

/// <summary>
/// Creates a failed feedback capture result with the specified error reason.
/// </summary>
/// <param name="errorReason"></param>
public CaptureFeedbackResult(CaptureFeedbackErrorReason errorReason)
{
EventId = SentryId.Empty;
ErrorReason = errorReason;
}

/// <summary>
/// The Id of the captured feedback, if successful. <see cref="SentryId.Empty"/> if feedback capture fails.
/// </summary>
public SentryId EventId;
Comment thread
jamescrosswell marked this conversation as resolved.
Outdated

/// <inheritdoc cref="CaptureFeedbackErrorReason"/>
public CaptureFeedbackErrorReason? ErrorReason;
Comment thread
jamescrosswell marked this conversation as resolved.
Outdated

/// <summary>
/// Implicitly converts a <see cref="CaptureFeedbackErrorReason"/> to a <see cref="CaptureFeedbackResult"/>
/// </summary>
/// <param name="errorReason"></param>
/// <returns></returns>
public static implicit operator CaptureFeedbackResult(CaptureFeedbackErrorReason errorReason) => new(errorReason);

/// <summary>
/// Implicitly converts a non-empty <see cref="SentryId"/> to a <see cref="CaptureFeedbackResult"/>
/// </summary>
/// <returns></returns>
public static implicit operator CaptureFeedbackResult(SentryId eventId) => new(eventId);

/// <summary>
/// Returns true if feedback capture was successful, false otherwise.
/// </summary>
public bool Succeededed => ErrorReason == null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: API Typo: Succeededed Should Be Succeeded

The Succeededed property in CaptureFeedbackResult has a typo; it should be Succeeded. This public API spelling error is grammatically incorrect and reflected in API approval tests, impacting consumers.

Fix in Cursor Fix in Web

}

/// <summary>
/// Used to specify the reason why feedback capture failed, in the event of a failure
/// </summary>
public enum CaptureFeedbackErrorReason
{
/// <summary>
/// <para>
/// An unknown error occurred (enable debug mode and check the logs for details).
/// </para>
/// <para>
/// Possible causes:
/// <list type="bullet">
/// <item>
/// <description>An exception from the configureScope callback</description>
/// </item>
/// <item>
/// <description>A transmission error when sending the envelope</description>
/// </item>
/// <item>
/// <description>An attempt to send feedback while the application is shutting down</description>
/// </item>
/// <item>
/// <description>Something more mysterious...</description>
/// </item>
/// </list>
/// </para>
/// </summary>
UnknownError,
/// <summary>
/// Sentry is disabled (very likely an empty DSN was provided when initialising the SDK).
/// </summary>
DisabledHub,
/// <summary>
/// The <see cref="SentryFeedback.Message"/> message is empty.
/// </summary>
EmptyMessage,
}
7 changes: 5 additions & 2 deletions src/Sentry/Extensibility/DisabledHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,15 +176,18 @@ public bool CaptureEnvelope(Envelope envelope)
/// <summary>
/// No-Op.
/// </summary>
public void CaptureFeedback(SentryFeedback feedback, Action<Scope> configureScope, SentryHint? hint = null)
public CaptureFeedbackResult CaptureFeedback(SentryFeedback feedback, Action<Scope> configureScope,
SentryHint? hint = null)
{
return CaptureFeedbackErrorReason.DisabledHub;
}

/// <summary>
/// No-Op.
/// </summary>
public void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null)
public CaptureFeedbackResult CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null)
{
return CaptureFeedbackErrorReason.DisabledHub;
}

/// <summary>
Expand Down
5 changes: 3 additions & 2 deletions src/Sentry/Extensibility/HubAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -260,15 +260,16 @@ public SentryId CaptureEvent(SentryEvent evt, Scope? scope, SentryHint? hint = n
/// </summary>
[DebuggerStepThrough]
[EditorBrowsable(EditorBrowsableState.Never)]
public void CaptureFeedback(SentryFeedback feedback, Action<Scope> configureScope, SentryHint? hint = null)
public CaptureFeedbackResult CaptureFeedback(SentryFeedback feedback, Action<Scope> configureScope,
SentryHint? hint = null)
=> SentrySdk.CaptureFeedback(feedback, configureScope, hint);

/// <summary>
/// Forwards the call to <see cref="SentrySdk"/>.
/// </summary>
[DebuggerStepThrough]
[EditorBrowsable(EditorBrowsableState.Never)]
public void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null)
public CaptureFeedbackResult CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null)
=> SentrySdk.CaptureFeedback(feedback, scope, hint);

/// <summary>
Expand Down
11 changes: 9 additions & 2 deletions src/Sentry/IHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ public TransactionContext ContinueTrace(
/// </summary>
/// <param name="feedback">The feedback to send to Sentry.</param>
/// <param name="configureScope">Callback method to configure the scope.</param>
/// <param name="hint">An optional hint providing high level context for the source of the event, including attachments</param>
public void CaptureFeedback(SentryFeedback feedback, Action<Scope> configureScope, SentryHint? hint = null);
/// <param name="hint">
/// An optional hint providing high-level context for the source of the event, including attachments
/// </param>
/// <returns>
/// A <see cref="CaptureFeedbackResult"/> that will contain the Id of the new event (if successful) or an error code
/// indicating the reason for failure (if feedback capture fails).
/// </returns>
public CaptureFeedbackResult CaptureFeedback(SentryFeedback feedback, Action<Scope> configureScope,
SentryHint? hint = null);
}
6 changes: 5 additions & 1 deletion src/Sentry/ISentryClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ public interface ISentryClient
/// <param name="feedback">The feedback to send to Sentry.</param>
/// <param name="scope">An optional scope to be applied to the event.</param>
/// <param name="hint">An optional hint providing high level context for the source of the event</param>
public void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null);
/// <returns>
/// A <see cref="CaptureFeedbackResult"/> that will contain the Id of the new event (if successful) or an error code
/// indicating the reason for failure (if feedback capture fails).
/// </returns>
public CaptureFeedbackResult CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null);

/// <summary>
/// Captures a user feedback.
Expand Down
15 changes: 9 additions & 6 deletions src/Sentry/Internal/Hub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -589,31 +589,33 @@ private SentryId CaptureEvent(SentryEvent evt, SentryHint? hint, Scope scope)
}
}

public void CaptureFeedback(SentryFeedback feedback, Action<Scope> configureScope, SentryHint? hint = null)
public CaptureFeedbackResult CaptureFeedback(SentryFeedback feedback, Action<Scope> configureScope,
SentryHint? hint = null)
{
if (!IsEnabled)
{
return;
return CaptureFeedbackErrorReason.DisabledHub;
}

try
{
var clonedScope = CurrentScope.Clone();
configureScope(clonedScope);

CaptureFeedback(feedback, clonedScope, hint);
return CaptureFeedback(feedback, clonedScope, hint);
}
catch (Exception e)
{
_options.LogError(e, "Failure to capture feedback");
return CaptureFeedbackErrorReason.UnknownError;
}
}

public void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null)
public CaptureFeedbackResult CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null)
{
if (!IsEnabled)
{
return;
return CaptureFeedbackErrorReason.DisabledHub;
}

try
Expand All @@ -625,11 +627,12 @@ public void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, Sentry
}

scope ??= CurrentScope;
CurrentClient.CaptureFeedback(feedback, scope, hint);
return CurrentClient.CaptureFeedback(feedback, scope, hint);
}
catch (Exception e)
{
_options.LogError(e, "Failure to capture feedback");
return CaptureFeedbackErrorReason.UnknownError;
}
}

Expand Down
18 changes: 15 additions & 3 deletions src/Sentry/SentryClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class SentryClient : ISentryClient, IDisposable
private readonly ISessionManager _sessionManager;
private readonly RandomValuesFactory _randomValuesFactory;
private readonly Enricher _enricher;
private volatile int _isDisposed = 0;
Comment thread
jamescrosswell marked this conversation as resolved.
Outdated

internal IBackgroundWorker Worker { get; }

Expand Down Expand Up @@ -85,12 +86,12 @@ public SentryId CaptureEvent(SentryEvent? @event, Scope? scope = null, SentryHin
}

/// <inheritdoc />
public void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null)
public CaptureFeedbackResult CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null)
{
if (string.IsNullOrEmpty(feedback.Message))
{
_options.LogWarning("Feedback dropped due to empty message.");
return;
return CaptureFeedbackErrorReason.EmptyMessage;
}

scope ??= new Scope(_options);
Expand All @@ -116,7 +117,7 @@ public void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, Sentry

var attachments = hint.Attachments.ToList();
var envelope = Envelope.FromFeedback(evt, _options.DiagnosticLogger, attachments, scope.SessionUpdate);
CaptureEnvelope(envelope);
return CaptureEnvelope(envelope) ? evt.EventId : CaptureFeedbackErrorReason.UnknownError;
}

/// <inheritdoc />
Expand Down Expand Up @@ -438,6 +439,12 @@ private SentryId DoSendEvent(SentryEvent @event, SentryHint? hint, Scope? scope)
/// <inheritdoc cref="ISentryClient.CaptureEnvelope"/>
public bool CaptureEnvelope(Envelope envelope)
{
if (_isDisposed == 1)
{
_options.LogWarning("Enqueue envelope failed. Client is disposed.");
return false;
}

if (Worker.EnqueueEnvelope(envelope))
{
_options.LogInfo("Envelope queued up: '{0}'", envelope.TryGetEventId(_options.DiagnosticLogger));
Expand All @@ -456,6 +463,11 @@ public bool CaptureEnvelope(Envelope envelope)
/// </summary>
public void Dispose()
{
if (Interlocked.Exchange(ref _isDisposed, 1) == 1)
{
return;
}

Comment thread
bitsandfoxes marked this conversation as resolved.
Outdated
_options.LogDebug("Flushing SentryClient.");

try
Expand Down
4 changes: 2 additions & 2 deletions src/Sentry/SentrySdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -546,14 +546,14 @@ public static SentryId CaptureMessage(string message, Action<Scope> configureSco
/// Captures feedback from the user.
/// </summary>
[DebuggerStepThrough]
public static void CaptureFeedback(SentryFeedback feedback, Action<Scope> configureScope, SentryHint? hint = null)
public static CaptureFeedbackResult CaptureFeedback(SentryFeedback feedback, Action<Scope> configureScope, SentryHint? hint = null)
=> CurrentHub.CaptureFeedback(feedback, configureScope, hint);

/// <summary>
/// Captures feedback from the user.
/// </summary>
[DebuggerStepThrough]
public static void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null)
public static CaptureFeedbackResult CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null)
=> CurrentHub.CaptureFeedback(feedback, scope, hint);

/// <summary>
Expand Down
Loading
Loading