From 99872c69491333f3279de097d45424830c361dd8 Mon Sep 17 00:00:00 2001 From: tsushanth <78000697+tsushanth@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:35:16 -0700 Subject: [PATCH 1/5] feat: add trace_metric_byte to DataCategory enum --- sentry/src/main/java/io/sentry/DataCategory.java | 1 + 1 file changed, 1 insertion(+) diff --git a/sentry/src/main/java/io/sentry/DataCategory.java b/sentry/src/main/java/io/sentry/DataCategory.java index 3702fd713a1..b34388859e2 100644 --- a/sentry/src/main/java/io/sentry/DataCategory.java +++ b/sentry/src/main/java/io/sentry/DataCategory.java @@ -12,6 +12,7 @@ public enum DataCategory { LogItem("log_item"), LogByte("log_byte"), TraceMetric("trace_metric"), + TraceMetricByte("trace_metric_byte"), Monitor("monitor"), Profile("profile"), ProfileChunkUi("profile_chunk_ui"), From 1f50618dad7fa91dfbf464c2990b8ddaa92d1241 Mon Sep 17 00:00:00 2001 From: tsushanth <78000697+tsushanth@users.noreply.github.com> Date: Wed, 1 Jul 2026 13:27:20 -0700 Subject: [PATCH 2/5] chore: regenerate API dump with TraceMetricByte in DataCategory --- sentry/api/sentry.api | 1 + 1 file changed, 1 insertion(+) diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 383ea92b116..d62ecf41193 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -373,6 +373,7 @@ public final class io/sentry/DataCategory : java/lang/Enum { public static final field Session Lio/sentry/DataCategory; public static final field Span Lio/sentry/DataCategory; public static final field TraceMetric Lio/sentry/DataCategory; + public static final field TraceMetricByte Lio/sentry/DataCategory; public static final field Transaction Lio/sentry/DataCategory; public static final field Unknown Lio/sentry/DataCategory; public static final field UserReport Lio/sentry/DataCategory; From 371100d9a8f79c7ef0047ce38a56c531661508b6 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Fri, 3 Jul 2026 11:08:33 +0200 Subject: [PATCH 3/5] feat: record TraceMetricByte in client reports when trace metrics are discarded Mirrors the LogByte pattern: records byte-level data category alongside the item-level one when trace metrics are dropped by beforeSendMetrics, queue overflow, or envelope discard. Co-Authored-By: Claude Opus 4.6 (1M context) --- sentry/src/main/java/io/sentry/SentryClient.java | 8 ++++++++ .../io/sentry/clientreport/ClientReportRecorder.java | 3 +++ .../java/io/sentry/metrics/MetricsBatchProcessor.java | 7 +++++++ sentry/src/test/java/io/sentry/SentryClientTest.kt | 10 ++++++++-- .../java/io/sentry/clientreport/ClientReportTest.kt | 3 +++ .../io/sentry/metrics/MetricsBatchProcessorTest.kt | 6 +++++- 6 files changed, 34 insertions(+), 3 deletions(-) diff --git a/sentry/src/main/java/io/sentry/SentryClient.java b/sentry/src/main/java/io/sentry/SentryClient.java index a739eddd9d9..4210e2994a7 100644 --- a/sentry/src/main/java/io/sentry/SentryClient.java +++ b/sentry/src/main/java/io/sentry/SentryClient.java @@ -1336,6 +1336,7 @@ public void captureMetric( } if (metricsEvent != null) { + final @NotNull SentryMetricsEvent tmpMetricsEvent = metricsEvent; metricsEvent = executeBeforeSendMetric(metricsEvent, hint); if (metricsEvent == null) { @@ -1345,6 +1346,13 @@ public void captureMetric( options .getClientReportRecorder() .recordLostEvent(DiscardReason.BEFORE_SEND, DataCategory.TraceMetric); + final long metricsEventNumberOfBytes = + JsonSerializationUtils.byteSizeOf( + options.getSerializer(), options.getLogger(), tmpMetricsEvent); + options + .getClientReportRecorder() + .recordLostEvent( + DiscardReason.BEFORE_SEND, DataCategory.TraceMetricByte, metricsEventNumberOfBytes); return; } diff --git a/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java b/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java index 0180b16977d..b3756ed32e4 100644 --- a/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java +++ b/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java @@ -124,6 +124,9 @@ public void recordLostEnvelopeItem( final @NotNull List items = metrics.getItems(); final long count = items.size(); recordLostEventInternal(reason.getReason(), itemCategory.getCategory(), count); + final long metricBytes = envelopeItem.getData().length; + recordLostEventInternal( + reason.getReason(), DataCategory.TraceMetricByte.getCategory(), metricBytes); executeOnDiscard(reason, itemCategory, count); } else { options diff --git a/sentry/src/main/java/io/sentry/metrics/MetricsBatchProcessor.java b/sentry/src/main/java/io/sentry/metrics/MetricsBatchProcessor.java index 169f36b973b..fb5430034de 100644 --- a/sentry/src/main/java/io/sentry/metrics/MetricsBatchProcessor.java +++ b/sentry/src/main/java/io/sentry/metrics/MetricsBatchProcessor.java @@ -11,6 +11,7 @@ import io.sentry.SentryMetricsEvents; import io.sentry.SentryOptions; import io.sentry.clientreport.DiscardReason; +import io.sentry.util.JsonSerializationUtils; import io.sentry.transport.ReusableCountLatch; import io.sentry.util.AutoClosableReentrantLock; import java.util.ArrayList; @@ -58,6 +59,12 @@ public void add(final @NotNull SentryMetricsEvent metricsEvent) { options .getClientReportRecorder() .recordLostEvent(DiscardReason.QUEUE_OVERFLOW, DataCategory.TraceMetric); + final long lostBytes = + JsonSerializationUtils.byteSizeOf( + options.getSerializer(), options.getLogger(), metricsEvent); + options + .getClientReportRecorder() + .recordLostEvent(DiscardReason.QUEUE_OVERFLOW, DataCategory.TraceMetricByte, lostBytes); return; } pendingCount.increment(); diff --git a/sentry/src/test/java/io/sentry/SentryClientTest.kt b/sentry/src/test/java/io/sentry/SentryClientTest.kt index f51345957e4..7ef8967de2b 100644 --- a/sentry/src/test/java/io/sentry/SentryClientTest.kt +++ b/sentry/src/test/java/io/sentry/SentryClientTest.kt @@ -388,7 +388,10 @@ class SentryClientTest { assertClientReport( fixture.sentryOptions.clientReportRecorder, - listOf(DiscardedEvent(DiscardReason.BEFORE_SEND.reason, DataCategory.TraceMetric.category, 1)), + listOf( + DiscardedEvent(DiscardReason.BEFORE_SEND.reason, DataCategory.TraceMetric.category, 1), + DiscardedEvent(DiscardReason.BEFORE_SEND.reason, DataCategory.TraceMetricByte.category, 120), + ), ) } @@ -408,7 +411,10 @@ class SentryClientTest { assertClientReport( fixture.sentryOptions.clientReportRecorder, - listOf(DiscardedEvent(DiscardReason.BEFORE_SEND.reason, DataCategory.TraceMetric.category, 1)), + listOf( + DiscardedEvent(DiscardReason.BEFORE_SEND.reason, DataCategory.TraceMetric.category, 1), + DiscardedEvent(DiscardReason.BEFORE_SEND.reason, DataCategory.TraceMetricByte.category, 120), + ), ) } diff --git a/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt b/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt index 95639d8c581..ceb254213cb 100644 --- a/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt +++ b/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt @@ -412,6 +412,9 @@ class ClientReportTest { val metricItem = clientReport!!.discardedEvents!!.first { it.category == DataCategory.TraceMetric.category } assertEquals(3, metricItem.quantity) + val metricByteItem = + clientReport.discardedEvents!!.first { it.category == DataCategory.TraceMetricByte.category } + assertEquals(envelope.items.first().data.size.toLong(), metricByteItem.quantity) } private fun givenClientReportRecorder( diff --git a/sentry/src/test/java/io/sentry/metrics/MetricsBatchProcessorTest.kt b/sentry/src/test/java/io/sentry/metrics/MetricsBatchProcessorTest.kt index da83ad882f6..0a0b5fc56a7 100644 --- a/sentry/src/test/java/io/sentry/metrics/MetricsBatchProcessorTest.kt +++ b/sentry/src/test/java/io/sentry/metrics/MetricsBatchProcessorTest.kt @@ -8,6 +8,7 @@ import io.sentry.SentryNanotimeDate import io.sentry.SentryOptions import io.sentry.clientreport.ClientReportTestHelper import io.sentry.clientreport.DiscardReason +import io.sentry.util.JsonSerializationUtils import io.sentry.clientreport.DiscardedEvent import io.sentry.protocol.SentryId import io.sentry.test.DeferredExecutorService @@ -80,9 +81,12 @@ class MetricsBatchProcessorTest { processor.add(droppedMetricsEvent) // verify that a client report was recorded for the dropped metrics item + val droppedBytes = + JsonSerializationUtils.byteSizeOf(options.serializer, options.logger, droppedMetricsEvent) val expectedEvents = mutableListOf( - DiscardedEvent(DiscardReason.QUEUE_OVERFLOW.reason, DataCategory.TraceMetric.category, 1) + DiscardedEvent(DiscardReason.QUEUE_OVERFLOW.reason, DataCategory.TraceMetric.category, 1), + DiscardedEvent(DiscardReason.QUEUE_OVERFLOW.reason, DataCategory.TraceMetricByte.category, droppedBytes), ) ClientReportTestHelper.assertClientReport(options.clientReportRecorder, expectedEvents) From 5dc77d0605a06fe267c9373e2172ba4ec69073e0 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Fri, 3 Jul 2026 11:09:24 +0200 Subject: [PATCH 4/5] changelog: add trace_metric_byte data category entry Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a290e2b3e9..1d7cc194f40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Features + +- Add `trace_metric_byte` data category and record byte-level client reports when trace metrics are discarded ([#5626](https://github.com/getsentry/sentry-java/pull/5626)) + ## 8.47.0 ### Behavioral Changes From 1e18411510323e3fea3f68a2066253adf6797fa5 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Fri, 3 Jul 2026 11:31:09 +0200 Subject: [PATCH 5/5] chore: apply spotless formatting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../java/io/sentry/metrics/MetricsBatchProcessor.java | 2 +- .../java/io/sentry/metrics/MetricsBatchProcessorTest.kt | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/sentry/src/main/java/io/sentry/metrics/MetricsBatchProcessor.java b/sentry/src/main/java/io/sentry/metrics/MetricsBatchProcessor.java index fb5430034de..2d5c78e5e89 100644 --- a/sentry/src/main/java/io/sentry/metrics/MetricsBatchProcessor.java +++ b/sentry/src/main/java/io/sentry/metrics/MetricsBatchProcessor.java @@ -11,9 +11,9 @@ import io.sentry.SentryMetricsEvents; import io.sentry.SentryOptions; import io.sentry.clientreport.DiscardReason; -import io.sentry.util.JsonSerializationUtils; import io.sentry.transport.ReusableCountLatch; import io.sentry.util.AutoClosableReentrantLock; +import io.sentry.util.JsonSerializationUtils; import java.util.ArrayList; import java.util.List; import java.util.Queue; diff --git a/sentry/src/test/java/io/sentry/metrics/MetricsBatchProcessorTest.kt b/sentry/src/test/java/io/sentry/metrics/MetricsBatchProcessorTest.kt index 0a0b5fc56a7..5ca1d0fe87a 100644 --- a/sentry/src/test/java/io/sentry/metrics/MetricsBatchProcessorTest.kt +++ b/sentry/src/test/java/io/sentry/metrics/MetricsBatchProcessorTest.kt @@ -8,11 +8,11 @@ import io.sentry.SentryNanotimeDate import io.sentry.SentryOptions import io.sentry.clientreport.ClientReportTestHelper import io.sentry.clientreport.DiscardReason -import io.sentry.util.JsonSerializationUtils import io.sentry.clientreport.DiscardedEvent import io.sentry.protocol.SentryId import io.sentry.test.DeferredExecutorService import io.sentry.test.injectForField +import io.sentry.util.JsonSerializationUtils import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -86,7 +86,11 @@ class MetricsBatchProcessorTest { val expectedEvents = mutableListOf( DiscardedEvent(DiscardReason.QUEUE_OVERFLOW.reason, DataCategory.TraceMetric.category, 1), - DiscardedEvent(DiscardReason.QUEUE_OVERFLOW.reason, DataCategory.TraceMetricByte.category, droppedBytes), + DiscardedEvent( + DiscardReason.QUEUE_OVERFLOW.reason, + DataCategory.TraceMetricByte.category, + droppedBytes, + ), ) ClientReportTestHelper.assertClientReport(options.clientReportRecorder, expectedEvents)