From 640e2d41059346c0ae40205cb6ece878f74ff2e7 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 19 Dec 2025 13:26:00 +0100 Subject: [PATCH] Record client report for discarded metrics envelope item --- .../clientreport/ClientReportRecorder.java | 15 +++++++++ .../sentry/clientreport/ClientReportTest.kt | 32 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java b/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java index eccf311cf83..0180b16977d 100644 --- a/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java +++ b/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java @@ -8,6 +8,8 @@ import io.sentry.SentryLevel; import io.sentry.SentryLogEvent; import io.sentry.SentryLogEvents; +import io.sentry.SentryMetricsEvent; +import io.sentry.SentryMetricsEvents; import io.sentry.SentryOptions; import io.sentry.protocol.SentrySpan; import io.sentry.protocol.SentryTransaction; @@ -115,6 +117,19 @@ public void recordLostEnvelopeItem( } else { options.getLogger().log(SentryLevel.ERROR, "Unable to parse lost logs envelope item."); } + } else if (itemCategory.equals(DataCategory.TraceMetric)) { + final @Nullable SentryMetricsEvents metrics = + envelopeItem.getMetrics(options.getSerializer()); + if (metrics != null) { + final @NotNull List items = metrics.getItems(); + final long count = items.size(); + recordLostEventInternal(reason.getReason(), itemCategory.getCategory(), count); + executeOnDiscard(reason, itemCategory, count); + } else { + options + .getLogger() + .log(SentryLevel.ERROR, "Unable to parse lost metrics envelope item."); + } } else { recordLostEventInternal(reason.getReason(), itemCategory.getCategory(), 1L); executeOnDiscard(reason, itemCategory, 1L); diff --git a/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt b/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt index ae4a5f35362..95639d8c581 100644 --- a/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt +++ b/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt @@ -20,6 +20,8 @@ import io.sentry.SentryLogEvent import io.sentry.SentryLogEvents import io.sentry.SentryLogLevel import io.sentry.SentryLongDate +import io.sentry.SentryMetricsEvent +import io.sentry.SentryMetricsEvents import io.sentry.SentryOptions import io.sentry.SentryReplayEvent import io.sentry.SentryTracer @@ -382,6 +384,36 @@ class ClientReportTest { assertEquals(226, logByte.quantity) } + @Test + fun `recording lost client report counts metric entries`() { + val onDiscardMock = mock() + givenClientReportRecorder { options -> options.onDiscard = onDiscardMock } + + val envelope = + testHelper.newEnvelope( + SentryEnvelopeItem.fromMetrics( + opts.serializer, + SentryMetricsEvents( + listOf( + SentryMetricsEvent(SentryId(), SentryLongDate(1), "metric1", "counter", 1.0), + SentryMetricsEvent(SentryId(), SentryLongDate(2), "metric2", "gauge", 2.0), + SentryMetricsEvent(SentryId(), SentryLongDate(3), "metric3", "distribution", 3.0), + ) + ), + ) + ) + + clientReportRecorder.recordLostEnvelopeItem(DiscardReason.NETWORK_ERROR, envelope.items.first()) + + verify(onDiscardMock, times(1)) + .execute(DiscardReason.NETWORK_ERROR, DataCategory.TraceMetric, 3) + + val clientReport = clientReportRecorder.resetCountsAndGenerateClientReport() + val metricItem = + clientReport!!.discardedEvents!!.first { it.category == DataCategory.TraceMetric.category } + assertEquals(3, metricItem.quantity) + } + private fun givenClientReportRecorder( callback: Sentry.OptionsConfiguration? = null ) {