diff --git a/CHANGELOG.md b/CHANGELOG.md index 92cfd11536..648da1ec51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Fixes +- Record byte-level client reports when event processors discard logs or trace metrics ([#5718](https://github.com/getsentry/sentry-java/pull/5718)) - Name the device-info caching thread `SentryDeviceInfoCache` so all threads spawned by the SDK are identifiable ([#5684](https://github.com/getsentry/sentry-java/pull/5684)) ## 8.47.0 diff --git a/sentry/src/main/java/io/sentry/SentryClient.java b/sentry/src/main/java/io/sentry/SentryClient.java index 4210e2994a..02934b1ed7 100644 --- a/sentry/src/main/java/io/sentry/SentryClient.java +++ b/sentry/src/main/java/io/sentry/SentryClient.java @@ -511,6 +511,7 @@ private SentryEvent processEvent( private SentryLogEvent processLogEvent( @NotNull SentryLogEvent event, final @NotNull List eventProcessors) { for (final EventProcessor processor : eventProcessors) { + final @NotNull SentryLogEvent eventBeforeProcessor = event; try { event = processor.process(event); } catch (Throwable e) { @@ -533,6 +534,13 @@ private SentryLogEvent processLogEvent( options .getClientReportRecorder() .recordLostEvent(DiscardReason.EVENT_PROCESSOR, DataCategory.LogItem); + final long logEventNumberOfBytes = + JsonSerializationUtils.byteSizeOf( + options.getSerializer(), options.getLogger(), eventBeforeProcessor); + options + .getClientReportRecorder() + .recordLostEvent( + DiscardReason.EVENT_PROCESSOR, DataCategory.LogByte, logEventNumberOfBytes); break; } } @@ -545,6 +553,7 @@ private SentryMetricsEvent processMetricsEvent( final @NotNull List eventProcessors, final @NotNull Hint hint) { for (final EventProcessor processor : eventProcessors) { + final @NotNull SentryMetricsEvent eventBeforeProcessor = event; try { event = processor.process(event, hint); } catch (Throwable e) { @@ -567,6 +576,15 @@ private SentryMetricsEvent processMetricsEvent( options .getClientReportRecorder() .recordLostEvent(DiscardReason.EVENT_PROCESSOR, DataCategory.TraceMetric); + final long metricsEventNumberOfBytes = + JsonSerializationUtils.byteSizeOf( + options.getSerializer(), options.getLogger(), eventBeforeProcessor); + options + .getClientReportRecorder() + .recordLostEvent( + DiscardReason.EVENT_PROCESSOR, + DataCategory.TraceMetricByte, + metricsEventNumberOfBytes); break; } } diff --git a/sentry/src/test/java/io/sentry/SentryClientTest.kt b/sentry/src/test/java/io/sentry/SentryClientTest.kt index 7ef8967de2..5b407bfef3 100644 --- a/sentry/src/test/java/io/sentry/SentryClientTest.kt +++ b/sentry/src/test/java/io/sentry/SentryClientTest.kt @@ -33,6 +33,7 @@ import io.sentry.test.injectForField import io.sentry.transport.ITransport import io.sentry.transport.ITransportGate import io.sentry.util.HintUtils +import io.sentry.util.JsonSerializationUtils import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.File @@ -341,6 +342,39 @@ class SentryClientTest { ) } + @Test + fun `when log is dropped by event processor, log byte discard is recorded`() { + val scope = createScope() + val logEvent = SentryLogEvent(SentryId(), SentryNanotimeDate(), "message", SentryLogLevel.WARN) + val logEventNumberOfBytes = + JsonSerializationUtils.byteSizeOf( + fixture.sentryOptions.serializer, + fixture.sentryOptions.logger, + logEvent, + ) + scope.addEventProcessor( + object : EventProcessor { + override fun process(event: SentryLogEvent): SentryLogEvent? = null + } + ) + + val sut = fixture.getSut() + sut.captureLog(logEvent, scope) + + verify(fixture.loggerBatchProcessor, never()).add(any()) + assertClientReport( + fixture.sentryOptions.clientReportRecorder, + listOf( + DiscardedEvent(DiscardReason.EVENT_PROCESSOR.reason, DataCategory.LogItem.category, 1), + DiscardedEvent( + DiscardReason.EVENT_PROCESSOR.reason, + DataCategory.LogByte.category, + logEventNumberOfBytes, + ), + ), + ) + } + @Test fun `when beforeSendLog is returns new instance, new instance is sent`() { val scope = createScope() @@ -418,6 +452,39 @@ class SentryClientTest { ) } + @Test + fun `when metric is dropped by event processor, metric byte discard is recorded`() { + val scope = createScope() + val metricsEvent = SentryMetricsEvent(SentryId(), SentryNanotimeDate(), "name", "gauge", 123.0) + val metricsEventNumberOfBytes = + JsonSerializationUtils.byteSizeOf( + fixture.sentryOptions.serializer, + fixture.sentryOptions.logger, + metricsEvent, + ) + scope.addEventProcessor( + object : EventProcessor { + override fun process(event: SentryMetricsEvent, hint: Hint): SentryMetricsEvent? = null + } + ) + + val sut = fixture.getSut() + sut.captureMetric(metricsEvent, scope, null) + + verify(fixture.metricsBatchProcessor, never()).add(any()) + assertClientReport( + fixture.sentryOptions.clientReportRecorder, + listOf( + DiscardedEvent(DiscardReason.EVENT_PROCESSOR.reason, DataCategory.TraceMetric.category, 1), + DiscardedEvent( + DiscardReason.EVENT_PROCESSOR.reason, + DataCategory.TraceMetricByte.category, + metricsEventNumberOfBytes, + ), + ), + ) + } + @Test fun `when beforeSendMetric is returns new instance, new instance is sent`() { val scope = createScope()