Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 `SentryId` and a `CaptureFeedbackResult` out parameter that indicate whether feedback was captured successfully and what the reason for failure was otherwise ([#4613](https://github.com/getsentry/sentry-dotnet/pull/4613))
- Removed obsolete APIs ([#4619](https://github.com/getsentry/sentry-dotnet/pull/4619))
- Removed the unusual constructor from `Sentry.Maui.BreadcrumbEvent` that had been marked as obsolete. That constructor expected a `IEnumerable<(string Key, string Value)>[]` argument (i.e. an array of IEnumerable of tuples). If you were using this constructor, you should instead use the alternate constructor that expects just an IEnumerable of tuples: `IEnumerable<(string Key, string Value)>`.
- Removed `SentrySdk.CaptureUserFeedback` and all associated members. Use the newer `SentrySdk.CaptureFeedback` instead.
Expand Down
43 changes: 43 additions & 0 deletions src/Sentry/CaptureFeedbackErrorReason.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace Sentry;

/// <summary>
/// Result code for <see cref="ISentryClient.CaptureFeedback"/> requests
/// </summary>
public enum CaptureFeedbackResult
{
/// <summary>
/// Feedback captured successfully.
/// </summary>
Success,
/// <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>An 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>
/// Capture failed because Sentry is disabled (very likely an empty DSN was provided when initialising the SDK).
/// </summary>
DisabledHub,
/// <summary>
/// Capture failed because the <see cref="SentryFeedback.Message"/> message is empty.
/// </summary>
EmptyMessage,
}
10 changes: 8 additions & 2 deletions src/Sentry/Extensibility/DisabledHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,15 +176,21 @@ public bool CaptureEnvelope(Envelope envelope)
/// <summary>
/// No-Op.
/// </summary>
public void CaptureFeedback(SentryFeedback feedback, Action<Scope> configureScope, SentryHint? hint = null)
public SentryId CaptureFeedback(SentryFeedback feedback, out CaptureFeedbackResult result,
Action<Scope> configureScope, SentryHint? hint = null)
{
result = CaptureFeedbackResult.DisabledHub;
return SentryId.Empty;
}

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

/// <summary>
Expand Down
10 changes: 6 additions & 4 deletions src/Sentry/Extensibility/HubAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -260,16 +260,18 @@ 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)
=> SentrySdk.CaptureFeedback(feedback, configureScope, hint);
public SentryId CaptureFeedback(SentryFeedback feedback, out CaptureFeedbackResult result,
Action<Scope> configureScope, SentryHint? hint = null)
=> SentrySdk.CaptureFeedback(feedback, out result, 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)
=> SentrySdk.CaptureFeedback(feedback, scope, hint);
public SentryId CaptureFeedback(SentryFeedback feedback, out CaptureFeedbackResult result, Scope? scope = null,
SentryHint? hint = null)
=> SentrySdk.CaptureFeedback(feedback, out result, scope, hint);

/// <summary>
/// Forwards the call to <see cref="SentrySdk"/>.
Expand Down
16 changes: 16 additions & 0 deletions src/Sentry/HubExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,22 @@ internal static SentryId CaptureExceptionInternal(this IHub hub, Exception ex) =
public static SentryId CaptureException(this IHub hub, Exception ex, Action<Scope> configureScope) =>
hub.CaptureEvent(new SentryEvent(ex), configureScope);

/// <summary>
/// Captures feedback from the user.
/// </summary>
/// <param name="hub">The Sentry hub.</param>
/// <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>
/// <returns>
/// A <see cref="SentryId"/> that will contain the Id of the new event (if successful) or
/// <see cref="SentryId.Empty"/> otherwise
/// </returns>
public static SentryId CaptureFeedback(this IHub hub, SentryFeedback feedback, Action<Scope> configureScope, SentryHint? hint = null)
=> hub.CaptureFeedback(feedback, out _, configureScope, hint);

/// <summary>
/// Captures a message with a configurable scope callback.
/// </summary>
Expand Down
12 changes: 10 additions & 2 deletions src/Sentry/IHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,15 @@ public TransactionContext ContinueTrace(
/// Captures feedback from the user.
/// </summary>
/// <param name="feedback">The feedback to send to Sentry.</param>
/// <param name="result">A <see cref="CaptureFeedbackResult"/> indicating either success or a specific error</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="SentryId"/> that will contain the Id of the new event (if successful) or
/// <see cref="SentryId.Empty"/> otherwise
/// </returns>
public SentryId CaptureFeedback(SentryFeedback feedback, out CaptureFeedbackResult result, Action<Scope> configureScope,
SentryHint? hint = null);
}
14 changes: 11 additions & 3 deletions src/Sentry/ISentryClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,17 @@ public interface ISentryClient
/// Captures feedback from the user.
/// </summary>
/// <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);
/// <param name="result">A <see cref="CaptureFeedbackResult"/> indicating either success or a specific error</param>
/// <param name="scope">An optional scope to be applied to the feedback event.</param>
/// <param name="hint">
/// An optional hint providing high-level context for the source of the event, including attachments
/// </param>
/// <returns>
/// A <see cref="SentryId"/> that will contain the Id of the new event (if successful) or
/// <see cref="SentryId.Empty"/> otherwise
/// </returns>
public SentryId CaptureFeedback(SentryFeedback feedback, out CaptureFeedbackResult result, Scope? scope = null,
SentryHint? hint = null);

/// <summary>
/// Captures a transaction.
Expand Down
23 changes: 16 additions & 7 deletions src/Sentry/Internal/Hub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -590,31 +590,37 @@ private SentryId CaptureEvent(SentryEvent evt, SentryHint? hint, Scope scope)
}
}

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

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

CaptureFeedback(feedback, clonedScope, hint);
return CaptureFeedback(feedback, out result, clonedScope, hint);
}
catch (Exception e)
{
_options.LogError(e, "Failure to capture feedback");
result = CaptureFeedbackResult.UnknownError;
return SentryId.Empty;
}
}

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

try
Expand All @@ -626,11 +632,13 @@ public void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, Sentry
}

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

Expand Down Expand Up @@ -842,7 +850,8 @@ public void Dispose()
}
//Don't dispose of ScopeManager since we want dangling transactions to still be able to access tags.

_backpressureMonitor?.Dispose();
// Don't dispose of _backpressureMonitor since we want the client to continue to process envelopes without
// throwing an ObjectDisposedException.

#if __IOS__
// TODO
Expand Down
14 changes: 11 additions & 3 deletions src/Sentry/SentryClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,14 @@ public SentryId CaptureEvent(SentryEvent? @event, Scope? scope = null, SentryHin
}

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

scope ??= new Scope(_options);
Expand All @@ -116,7 +118,13 @@ 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);
if (CaptureEnvelope(envelope))
{
result = CaptureFeedbackResult.Success;
return evt.EventId;
}
result = CaptureFeedbackResult.UnknownError;
return SentryId.Empty;
}

/// <inheritdoc />
Expand Down
16 changes: 15 additions & 1 deletion src/Sentry/SentryClientExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,26 @@ public static SentryId CaptureMessage(this ISentryClient client, string message,
/// <summary>
/// Captures feedback from the user.
/// </summary>
public static void CaptureFeedback(this ISentryClient client, string message, string? contactEmail = null,
public static SentryId CaptureFeedback(this ISentryClient client, string message, string? contactEmail = null,
string? name = null, string? replayId = null, string? url = null, SentryId? associatedEventId = null,
Scope? scope = null, SentryHint? hint = null)
=> client.CaptureFeedback(new SentryFeedback(message, contactEmail, name, replayId, url, associatedEventId),
scope, hint);

/// <summary>
/// Captures feedback from the user.
/// </summary>
/// <param name="client">The Sentry client.</param>
/// <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>
/// <returns>
/// A <see cref="SentryId"/> that will contain the Id of the new event (if successful) or
/// <see cref="SentryId.Empty"/> otherwise
/// </returns>
public static SentryId CaptureFeedback(this ISentryClient client, SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null)
=> client.CaptureFeedback(feedback, out _, scope, hint);

/// <summary>
/// Flushes the queue of captured events until the timeout set in <see cref="SentryOptions.FlushTimeout"/>
/// is reached.
Expand Down
26 changes: 17 additions & 9 deletions src/Sentry/SentrySdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -542,25 +542,33 @@ public static SentryId CaptureMessage(string message, SentryLevel level = Sentry
public static SentryId CaptureMessage(string message, Action<Scope> configureScope, SentryLevel level = SentryLevel.Info)
=> CurrentHub.CaptureMessage(message, configureScope, level);

/// <summary>
/// Captures feedback from the user.
/// </summary>
/// <inheritdoc cref="M:Sentry.IHub.CaptureFeedback(Sentry.SentryFeedback,System.Action{Sentry.Scope},Sentry.SentryHint)"/>
[DebuggerStepThrough]
public static void CaptureFeedback(SentryFeedback feedback, Action<Scope> configureScope, SentryHint? hint = null)
public static SentryId CaptureFeedback(SentryFeedback feedback, Action<Scope> configureScope, SentryHint? hint = null)
=> CurrentHub.CaptureFeedback(feedback, configureScope, hint);

/// <summary>
/// Captures feedback from the user.
/// </summary>
/// <inheritdoc cref="M:Sentry.IHub.CaptureFeedback(Sentry.SentryFeedback, out Sentry.CaptureFeedbackResult,System.Action{Sentry.Scope},Sentry.SentryHint)"/>
[DebuggerStepThrough]
public static SentryId CaptureFeedback(SentryFeedback feedback, out CaptureFeedbackResult result,
Action<Scope> configureScope, SentryHint? hint = null)
=> CurrentHub.CaptureFeedback(feedback, out result, configureScope, hint);

/// <inheritdoc cref="M:Sentry.ISentryClient.CaptureFeedback"/>
[DebuggerStepThrough]
public static void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null)
public static SentryId CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null)
=> CurrentHub.CaptureFeedback(feedback, scope, hint);

/// <inheritdoc cref="M:Sentry.ISentryClient.CaptureFeedback"/>
[DebuggerStepThrough]
public static SentryId CaptureFeedback(SentryFeedback feedback, out CaptureFeedbackResult result,
Scope? scope = null, SentryHint? hint = null)
=> CurrentHub.CaptureFeedback(feedback, out result, scope, hint);

/// <summary>
/// Captures feedback from the user.
/// </summary>
[DebuggerStepThrough]
public static void CaptureFeedback(string message, string? contactEmail = null, string? name = null,
public static SentryId CaptureFeedback(string message, string? contactEmail = null, string? name = null,
string? replayId = null, string? url = null, SentryId? associatedEventId = null, Scope? scope = null,
SentryHint? hint = null)
=> CurrentHub.CaptureFeedback(new SentryFeedback(message, contactEmail, name, replayId, url, associatedEventId),
Expand Down
Loading
Loading