Skip to content

Commit 8a76183

Browse files
adinauertsushanthromtsnclaudegetsentry-bot
authored
fix(core): Apply byte category rate limits (#5716)
* feat: add trace_metric_byte to DataCategory enum * chore: regenerate API dump with TraceMetricByte in DataCategory * 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) <noreply@anthropic.com> * changelog: add trace_metric_byte data category entry Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(core): Apply byte category rate limits * changelog * Format code --------- Co-authored-by: tsushanth <78000697+tsushanth@users.noreply.github.com> Co-authored-by: Roman Zavarnitsyn <rom4ek93@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Sentry Github Bot <bot+github-bot@sentry.io>
1 parent 2ad4e0d commit 8a76183

3 files changed

Lines changed: 55 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
### Fixes
1010

1111
- 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))
12+
- Apply byte-category rate limits to log and trace metric envelope items ([#5716](https://github.com/getsentry/sentry-java/pull/5716))
1213

1314
## 8.47.0
1415

sentry/src/main/java/io/sentry/transport/RateLimiter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,11 @@ private boolean isRetryAfter(final @NotNull String itemType) {
212212
case "feedback":
213213
return Collections.singletonList(DataCategory.Feedback);
214214
case "log":
215-
return Collections.singletonList(DataCategory.LogItem);
215+
return Arrays.asList(DataCategory.LogItem, DataCategory.LogByte);
216216
case "span":
217217
return Collections.singletonList(DataCategory.Span);
218218
case "trace_metric":
219-
return Collections.singletonList(DataCategory.TraceMetric);
219+
return Arrays.asList(DataCategory.TraceMetric, DataCategory.TraceMetricByte);
220220
default:
221221
return Collections.singletonList(DataCategory.Unknown);
222222
}

sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,31 @@ class RateLimiterTest {
457457
verifyNoMoreInteractions(fixture.clientReportRecorder)
458458
}
459459

460+
@Test
461+
fun `drop log items as lost when log byte is rate limited`() {
462+
val rateLimiter = fixture.getSUT()
463+
val scopes = mock<IScopes>()
464+
whenever(scopes.options).thenReturn(SentryOptions())
465+
466+
val logEventItem =
467+
SentryEnvelopeItem.fromLogs(
468+
fixture.serializer,
469+
SentryLogEvents(
470+
listOf(SentryLogEvent(SentryId(), SentryLongDate(0), "hello", SentryLogLevel.INFO))
471+
),
472+
)
473+
val envelope = SentryEnvelope(SentryEnvelopeHeader(null), arrayListOf(logEventItem))
474+
475+
rateLimiter.updateRetryAfterLimits("60:log_byte:key", null, 1)
476+
val result = rateLimiter.filter(envelope, Hint())
477+
478+
assertNull(result)
479+
480+
verify(fixture.clientReportRecorder, times(1))
481+
.recordLostEnvelopeItem(eq(DiscardReason.RATELIMIT_BACKOFF), same(logEventItem))
482+
verifyNoMoreInteractions(fixture.clientReportRecorder)
483+
}
484+
460485
@Test
461486
fun `drop profileChunkUi items as lost`() {
462487
val rateLimiter = fixture.getSUT()
@@ -590,6 +615,33 @@ class RateLimiterTest {
590615
verifyNoMoreInteractions(fixture.clientReportRecorder)
591616
}
592617

618+
@Test
619+
fun `drop trace metric items as lost when trace metric byte is rate limited`() {
620+
val rateLimiter = fixture.getSUT()
621+
622+
// There is no span API yet so we'll create the envelope manually using EnvelopeReader
623+
// This mimics how hybrid SDKs would send trace_metric envelope items
624+
val spanPayload = """{"items":[]}"""
625+
val metricItemHeader =
626+
"""{"type":"trace_metric","length":${spanPayload.length},"content_type":"application/vnd.sentry.items.trace-metric+json","item_count":1}"""
627+
val envelopeHeader = """{}"""
628+
val rawEnvelope = "$envelopeHeader\n$metricItemHeader\n$spanPayload"
629+
630+
val options = SentryOptions()
631+
val envelopeReader = EnvelopeReader(JsonSerializer(options))
632+
val metricEnvelope = envelopeReader.read(rawEnvelope.byteInputStream())!!
633+
val metricItem = metricEnvelope.items.first()
634+
635+
rateLimiter.updateRetryAfterLimits("60:trace_metric_byte:key", null, 1)
636+
val result = rateLimiter.filter(metricEnvelope, Hint())
637+
638+
assertNull(result)
639+
640+
verify(fixture.clientReportRecorder, times(1))
641+
.recordLostEnvelopeItem(eq(DiscardReason.RATELIMIT_BACKOFF), same(metricItem))
642+
verifyNoMoreInteractions(fixture.clientReportRecorder)
643+
}
644+
593645
@Test
594646
fun `apply rate limits notifies observers`() {
595647
val rateLimiter = fixture.getSUT()

0 commit comments

Comments
 (0)