diff --git a/CHANGELOG.md b/CHANGELOG.md index 270fb60358..5f1d0ef539 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,11 @@ ### Features - GA release for Sentry Metrics ([#5023](https://github.com/getsentry/sentry-dotnet/pull/5023)) - + +### Fixes + +- Common tags such as `Environment` and `Release` and custom event processors are all now correctly applied to CaptureFeedback events ([#4942](https://github.com/getsentry/sentry-dotnet/pull/4942)) + ## 6.2.0 ### Features diff --git a/src/Sentry/CaptureFeedbackErrorReason.cs b/src/Sentry/CaptureFeedbackErrorReason.cs index 728a7b1b47..0a2e5c177f 100644 --- a/src/Sentry/CaptureFeedbackErrorReason.cs +++ b/src/Sentry/CaptureFeedbackErrorReason.cs @@ -40,4 +40,8 @@ public enum CaptureFeedbackResult /// Capture failed because the message is empty. /// EmptyMessage, + /// + /// The was discarded by an event processor. + /// + DroppedByEventProcessor, } diff --git a/src/Sentry/Internal/DataCategory.cs b/src/Sentry/Internal/DataCategory.cs index 5ad06f8363..1b15e2310e 100644 --- a/src/Sentry/Internal/DataCategory.cs +++ b/src/Sentry/Internal/DataCategory.cs @@ -6,6 +6,7 @@ namespace Sentry.Internal; public static DataCategory Attachment = new("attachment"); public static DataCategory Default = new("default"); public static DataCategory Error = new("error"); + public static DataCategory Feedback = new("feedback"); public static DataCategory Internal = new("internal"); public static DataCategory Security = new("security"); public static DataCategory Session = new("session"); diff --git a/src/Sentry/Internal/SentryEventHelper.cs b/src/Sentry/Internal/SentryEventHelper.cs index eb3fbb7f9f..044ec3babe 100644 --- a/src/Sentry/Internal/SentryEventHelper.cs +++ b/src/Sentry/Internal/SentryEventHelper.cs @@ -4,7 +4,8 @@ namespace Sentry.Internal; internal static class SentryEventHelper { - public static SentryEvent? ProcessEvent(SentryEvent? evt, IEnumerable processors, SentryHint? hint, SentryOptions options) + public static SentryEvent? ProcessEvent(SentryEvent? evt, IEnumerable processors, + SentryHint? hint, SentryOptions options, DataCategory dataCategory) { if (evt == null) { @@ -19,7 +20,7 @@ internal static class SentryEventHelper processedEvent = processor.DoProcessEvent(processedEvent, effectiveHint); if (processedEvent == null) { - options.ClientReportRecorder.RecordDiscardedEvent(DiscardReason.EventProcessor, DataCategory.Error); + options.ClientReportRecorder.RecordDiscardedEvent(DiscardReason.EventProcessor, dataCategory); options.LogInfo("Event dropped by processor {0}", processor.GetType().Name); break; } diff --git a/src/Sentry/Platforms/Cocoa/SentrySdk.cs b/src/Sentry/Platforms/Cocoa/SentrySdk.cs index 90adf733c9..89bc689013 100644 --- a/src/Sentry/Platforms/Cocoa/SentrySdk.cs +++ b/src/Sentry/Platforms/Cocoa/SentrySdk.cs @@ -295,7 +295,8 @@ private static bool SuppressNativeCrash(SentryOptions options, CocoaSdk.SentryEv } var sentryEvent = evt.ToSentryEvent(); - if (SentryEventHelper.ProcessEvent(sentryEvent, manualProcessors, null, options) is not { } processedEvent) + if (SentryEventHelper.ProcessEvent(sentryEvent, manualProcessors, null, options, DataCategory.Error) + is not { } processedEvent) { return null; } diff --git a/src/Sentry/SentryClient.cs b/src/Sentry/SentryClient.cs index 905070861e..64da285d5e 100644 --- a/src/Sentry/SentryClient.cs +++ b/src/Sentry/SentryClient.cs @@ -117,12 +117,19 @@ public SentryId CaptureFeedback(SentryFeedback feedback, out CaptureFeedbackResu evt.Level = scope.Level; } + if (SentryEventHelper.ProcessEvent(evt, scope.GetAllEventProcessors(), hint, _options, DataCategory.Feedback) + is not { } processedEvent) + { + result = CaptureFeedbackResult.DroppedByEventProcessor; + return SentryId.Empty; // Dropped by an event processor + } + var attachments = hint.Attachments.ToList(); - var envelope = Envelope.FromFeedback(evt, _options.DiagnosticLogger, attachments, scope.SessionUpdate); + var envelope = Envelope.FromFeedback(processedEvent, _options.DiagnosticLogger, attachments, scope.SessionUpdate); if (CaptureEnvelope(envelope)) { result = CaptureFeedbackResult.Success; - return evt.EventId; + return processedEvent.EventId; } result = CaptureFeedbackResult.UnknownError; return SentryId.Empty; @@ -345,7 +352,8 @@ private SentryId DoSendEvent(SentryEvent @event, SentryHint? hint, Scope? scope) } } - if (SentryEventHelper.ProcessEvent(@event, scope.GetAllEventProcessors(), hint, _options) is not { } processedEvent) + if (SentryEventHelper.ProcessEvent(@event, scope.GetAllEventProcessors(), hint, _options, DataCategory.Error) + is not { } processedEvent) { return SentryId.Empty; // Dropped by an event processor } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index d614075852..a0c9848a7c 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -51,6 +51,7 @@ namespace Sentry UnknownError = 1, DisabledHub = 2, EmptyMessage = 3, + DroppedByEventProcessor = 4, } public enum CheckInStatus { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index d614075852..a0c9848a7c 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -51,6 +51,7 @@ namespace Sentry UnknownError = 1, DisabledHub = 2, EmptyMessage = 3, + DroppedByEventProcessor = 4, } public enum CheckInStatus { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index d614075852..a0c9848a7c 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -51,6 +51,7 @@ namespace Sentry UnknownError = 1, DisabledHub = 2, EmptyMessage = 3, + DroppedByEventProcessor = 4, } public enum CheckInStatus { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 8d15f59117..12c19434e6 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -51,6 +51,7 @@ namespace Sentry UnknownError = 1, DisabledHub = 2, EmptyMessage = 3, + DroppedByEventProcessor = 4, } public enum CheckInStatus { diff --git a/test/Sentry.Tests/SentryClientTests.cs b/test/Sentry.Tests/SentryClientTests.cs index ce7f9c29ba..1fedab2ff8 100644 --- a/test/Sentry.Tests/SentryClientTests.cs +++ b/test/Sentry.Tests/SentryClientTests.cs @@ -976,6 +976,58 @@ public void CaptureFeedback_WithScope_ScopeCopiedToEvent() Assert.Equal(scope.Breadcrumbs, @event.Breadcrumbs); } + [Fact] + public void CaptureFeedback_WithEventProcessor_EventProcessorApplied() + { + //Arrange + var feedback = new SentryFeedback("Everything is great!"); + var eventProcessor = Substitute.For(); + eventProcessor.Process(Arg.Any()).Returns(e => + { + var evt = (SentryEvent)e[0]; + evt.Environment = "testing 123"; + return evt; + }); + _fixture.SentryOptions.AddEventProcessor(eventProcessor); + var sut = _fixture.GetSut(); + + Envelope envelope = null; + sut.Worker.When(w => w.EnqueueEnvelope(Arg.Any())) + .Do(callback => envelope = callback.Arg()); + + //Act + var result = sut.CaptureFeedback(feedback); + + //Assert + result.Should().NotBe(SentryId.Empty); + _ = sut.Worker.Received(1).EnqueueEnvelope(Arg.Any()); + envelope.Should().NotBeNull(); + envelope.Items.Should().Contain(item => item.TryGetType() == EnvelopeItem.TypeValueFeedback); + var item = envelope.Items.First(x => x.TryGetType() == EnvelopeItem.TypeValueFeedback); + var @event = (SentryEvent)((JsonSerializable)item.Payload).Source; + @event.Environment.Should().Be("testing 123"); + } + + [Fact] + public void CaptureFeedback_EventDropped_SendsClientReport() + { + //Arrange + var feedback = new SentryFeedback("Everything is great!"); + var eventProcessor = Substitute.For(); + eventProcessor.Process(Arg.Any()).Returns(_ => null); + _fixture.SentryOptions.AddEventProcessor(eventProcessor); + var sut = _fixture.GetSut(); + + //Act + var id = sut.CaptureFeedback(feedback, out var result); + + //Assert + result.Should().Be(CaptureFeedbackResult.DroppedByEventProcessor); + id.Should().Be(SentryId.Empty); + var expectedReason = DiscardReason.EventProcessor; + _fixture.ClientReportRecorder.Received(1).RecordDiscardedEvent(expectedReason, DataCategory.Feedback); + } + [Fact] public void CaptureFeedback_WithHint_HasHintAttachment() {