Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
### Fixes

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

## 8.47.0

Expand Down
4 changes: 2 additions & 2 deletions sentry/src/main/java/io/sentry/transport/RateLimiter.java
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,11 @@ private boolean isRetryAfter(final @NotNull String itemType) {
case "feedback":
return Collections.singletonList(DataCategory.Feedback);
case "log":
return Collections.singletonList(DataCategory.LogItem);
return Arrays.asList(DataCategory.LogItem, DataCategory.LogByte);
case "span":
return Collections.singletonList(DataCategory.Span);
case "trace_metric":
return Collections.singletonList(DataCategory.TraceMetric);
return Arrays.asList(DataCategory.TraceMetric, DataCategory.TraceMetricByte);
default:
return Collections.singletonList(DataCategory.Unknown);
}
Expand Down
52 changes: 52 additions & 0 deletions sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,31 @@ class RateLimiterTest {
verifyNoMoreInteractions(fixture.clientReportRecorder)
}

@Test
fun `drop log items as lost when log byte is rate limited`() {
val rateLimiter = fixture.getSUT()
val scopes = mock<IScopes>()
whenever(scopes.options).thenReturn(SentryOptions())

val logEventItem =
SentryEnvelopeItem.fromLogs(
fixture.serializer,
SentryLogEvents(
listOf(SentryLogEvent(SentryId(), SentryLongDate(0), "hello", SentryLogLevel.INFO))
),
)
val envelope = SentryEnvelope(SentryEnvelopeHeader(null), arrayListOf(logEventItem))

rateLimiter.updateRetryAfterLimits("60:log_byte:key", null, 1)
val result = rateLimiter.filter(envelope, Hint())

assertNull(result)

verify(fixture.clientReportRecorder, times(1))
.recordLostEnvelopeItem(eq(DiscardReason.RATELIMIT_BACKOFF), same(logEventItem))
verifyNoMoreInteractions(fixture.clientReportRecorder)
}

@Test
fun `drop profileChunkUi items as lost`() {
val rateLimiter = fixture.getSUT()
Expand Down Expand Up @@ -590,6 +615,33 @@ class RateLimiterTest {
verifyNoMoreInteractions(fixture.clientReportRecorder)
}

@Test
fun `drop trace metric items as lost when trace metric byte is rate limited`() {
val rateLimiter = fixture.getSUT()

// There is no span API yet so we'll create the envelope manually using EnvelopeReader
// This mimics how hybrid SDKs would send trace_metric envelope items
val spanPayload = """{"items":[]}"""
val metricItemHeader =
"""{"type":"trace_metric","length":${spanPayload.length},"content_type":"application/vnd.sentry.items.trace-metric+json","item_count":1}"""
val envelopeHeader = """{}"""
val rawEnvelope = "$envelopeHeader\n$metricItemHeader\n$spanPayload"

val options = SentryOptions()
val envelopeReader = EnvelopeReader(JsonSerializer(options))
val metricEnvelope = envelopeReader.read(rawEnvelope.byteInputStream())!!
val metricItem = metricEnvelope.items.first()

rateLimiter.updateRetryAfterLimits("60:trace_metric_byte:key", null, 1)
val result = rateLimiter.filter(metricEnvelope, Hint())

assertNull(result)

verify(fixture.clientReportRecorder, times(1))
.recordLostEnvelopeItem(eq(DiscardReason.RATELIMIT_BACKOFF), same(metricItem))
verifyNoMoreInteractions(fixture.clientReportRecorder)
}

@Test
fun `apply rate limits notifies observers`() {
val rateLimiter = fixture.getSUT()
Expand Down
Loading