Skip to content

Commit 603a5f0

Browse files
committed
Merge remote-tracking branch 'origin/main' into no/distrust-emudhra-intermediates
2 parents 240cb84 + 37d61d8 commit 603a5f0

4 files changed

Lines changed: 70 additions & 4 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Fixes
66

7+
- Report the number of dropped spans in client reports when a batched span envelope item is discarded, instead of a quantity of 1 (e.g. for Flutter span streaming) ([#6265](https://github.com/getsentry/sentry-java/pull/6265))
78
- Keep sending events from Android 7.1.0 and older devices after Sentry changes its TLS certificate authority ([#6227](https://github.com/getsentry/sentry-java/pull/6227))
89
- In February 2027, Sentry moves its TLS certificates from DigiCert to Let's Encrypt and Google Trust Services ([announcement](https://sentry.io/changelog/were-changing-our-tls-certificate-authority-in-february-2027)).
910
- Android 7.1.0 (API 25) and older don't trust the Let's Encrypt root certificate (ISRG Root X1), so without this fix, the SDK on those devices can no longer send events to Sentry after the switch.

‎requirements.txt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
certifi==2025.7.14
1+
certifi==2026.10.19
22
charset-normalizer==3.4.2
33
idna==3.15
44
requests==2.33.0

‎sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ public void recordLostEnvelopeItem(
113113
recordLostEventInternal(
114114
reason.getReason(), DataCategory.TraceMetricByte.getCategory(), metricBytes);
115115
executeOnDiscard(reason, itemCategory, count);
116+
} else if (itemCategory.equals(DataCategory.Span)) {
117+
// Batched span items (span streaming) aren't created by this SDK yet, but hybrid SDKs
118+
// like sentry-dart send them through it via InternalSentrySdk.captureEnvelope.
119+
final long count = itemCountFromHeader(envelopeItem);
120+
recordLostEventInternal(reason.getReason(), itemCategory.getCategory(), count);
121+
executeOnDiscard(reason, itemCategory, count);
116122
} else {
117123
recordLostEventInternal(reason.getReason(), itemCategory.getCategory(), 1L);
118124
executeOnDiscard(reason, itemCategory, 1L);
@@ -156,9 +162,9 @@ private void recordLostEventInternal(
156162
storage.addCount(key, countToAdd);
157163
}
158164

159-
// The number of items batched into a log or metric envelope item is stored in its header, so we
160-
// read it from there instead of deserializing the payload. Deserializing on the discard path is
161-
// expensive and, under sustained rate limiting, caused a CPU busy-loop (JAVA-662).
165+
// The number of items batched into a log, metric or span envelope item is stored in its header,
166+
// so we read it from there instead of deserializing the payload. Deserializing on the discard
167+
// path is expensive and, under sustained rate limiting, caused a CPU busy-loop (JAVA-662).
162168
private long itemCountFromHeader(final @NotNull SentryEnvelopeItem envelopeItem) {
163169
final @Nullable Integer itemCount = envelopeItem.getHeader().getItemCount();
164170
return itemCount != null ? itemCount : 1L;

‎sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import org.mockito.kotlin.whenever
5959

6060
private const val LOG_CONTENT_TYPE = "application/vnd.sentry.items.log+json"
6161
private const val METRIC_CONTENT_TYPE = "application/vnd.sentry.items.trace-metric+json"
62+
private const val SPAN_CONTENT_TYPE = "application/vnd.sentry.items.span.v2+json"
6263

6364
class ClientReportTest {
6465
lateinit var opts: SentryOptions
@@ -503,6 +504,64 @@ class ClientReportTest {
503504
assertEquals(1, clientReport.quantityOf(DataCategory.TraceMetric))
504505
}
505506

507+
@Test
508+
fun `recording lost span item reads count from the header`() {
509+
val onDiscardMock = mock<SentryOptions.OnDiscardCallback>()
510+
givenClientReportRecorder { options -> options.onDiscard = onDiscardMock }
511+
512+
// Batched span items reach this SDK from hybrid SDKs (e.g. Flutter span streaming).
513+
val item =
514+
mockEnvelopeItem(
515+
SentryItemType.Span,
516+
SPAN_CONTENT_TYPE,
517+
itemCount = 500,
518+
data = "irrelevant payload".toByteArray(),
519+
)
520+
521+
clientReportRecorder.recordLostEnvelopeItem(DiscardReason.NETWORK_ERROR, item)
522+
523+
verify(onDiscardMock, times(1)).execute(DiscardReason.NETWORK_ERROR, DataCategory.Span, 500)
524+
525+
val clientReport = clientReportRecorder.resetCountsAndGenerateClientReport()
526+
assertEquals(500, clientReport.quantityOf(DataCategory.Span))
527+
}
528+
529+
@Test
530+
fun `recording rate limited span item reads count from the header`() {
531+
givenClientReportRecorder()
532+
533+
val item =
534+
mockEnvelopeItem(SentryItemType.Span, SPAN_CONTENT_TYPE, itemCount = 7, data = ByteArray(0))
535+
536+
clientReportRecorder.recordLostEnvelopeItem(DiscardReason.RATELIMIT_BACKOFF, item)
537+
538+
val clientReport = clientReportRecorder.resetCountsAndGenerateClientReport()
539+
val spanItem =
540+
clientReport!!.discardedEvents!!.single {
541+
it.category == DataCategory.Span.category &&
542+
it.reason == DiscardReason.RATELIMIT_BACKOFF.reason
543+
}
544+
assertEquals(7, spanItem.quantity)
545+
}
546+
547+
@Test
548+
fun `recording lost span item without item count in header falls back to one`() {
549+
givenClientReportRecorder()
550+
551+
val item =
552+
mockEnvelopeItem(
553+
SentryItemType.Span,
554+
SPAN_CONTENT_TYPE,
555+
itemCount = null,
556+
data = ByteArray(0),
557+
)
558+
559+
clientReportRecorder.recordLostEnvelopeItem(DiscardReason.NETWORK_ERROR, item)
560+
561+
val clientReport = clientReportRecorder.resetCountsAndGenerateClientReport()
562+
assertEquals(1, clientReport.quantityOf(DataCategory.Span))
563+
}
564+
506565
private fun mockEnvelopeItem(
507566
type: SentryItemType,
508567
contentType: String,

0 commit comments

Comments
 (0)