Skip to content

Commit b289b5a

Browse files
committed
log when dropping envelopes
1 parent e10be73 commit b289b5a

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

packages/dart/lib/src/transport/rate_limiter.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ class RateLimiter {
2626
DataCategory.fromItemType(item.header.type),
2727
);
2828

29+
// Log debug when individual envelope items are dropped due to rate limiting
30+
_options.log(
31+
SentryLevel.debug,
32+
'Envelope item of type "${item.header.type}" was dropped due to rate limiting',
33+
);
34+
2935
final originalObject = item.originalObject;
3036
if (originalObject is SentryTransaction) {
3137
_options.recorder.recordLostEvent(
@@ -48,9 +54,20 @@ class RateLimiter {
4854

4955
// no reason to continue
5056
if (toSend.isEmpty) {
57+
// Log error when entire envelope is dropped due to rate limiting
58+
_options.log(
59+
SentryLevel.debug,
60+
'All envelope items were dropped due to rate limiting. No events will be sent to Sentry.',
61+
);
5162
return null;
5263
}
5364

65+
// Log debug when some items were dropped but envelope can still be sent
66+
_options.log(
67+
SentryLevel.debug,
68+
'${dropItems.length} envelope item(s) were dropped due to rate limiting, but ${toSend.length} item(s) will still be sent',
69+
);
70+
5471
return SentryEnvelope(envelope.header, toSend);
5572
} else {
5673
return envelope;

packages/dart/test/protocol/rate_limiter_test.dart

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,121 @@ void main() {
319319
expect(DataCategory.fromItemType('unknown'), DataCategory.unknown);
320320
});
321321
});
322+
323+
group('RateLimiter logging', () {
324+
test('logs debug for dropped item and full envelope', () {
325+
final options = defaultTestOptions();
326+
options.debug = true;
327+
options.diagnosticLevel = SentryLevel.debug;
328+
329+
final logCalls = <_LogCall>[];
330+
void mockLogger(
331+
SentryLevel level,
332+
String message, {
333+
String? logger,
334+
Object? exception,
335+
StackTrace? stackTrace,
336+
}) {
337+
logCalls.add(_LogCall(level, message));
338+
}
339+
340+
options.log = mockLogger;
341+
342+
final rateLimiter = RateLimiter(options);
343+
344+
final eventItem = SentryEnvelopeItem.fromEvent(SentryEvent());
345+
final envelope = SentryEnvelope(
346+
SentryEnvelopeHeader.newEventId(),
347+
[eventItem],
348+
);
349+
350+
// Apply rate limit for error (event)
351+
rateLimiter.updateRetryAfterLimits(
352+
'1:error:key, 5:error:organization', null, 1);
353+
354+
// Filter should drop the entire envelope
355+
final result = rateLimiter.filter(envelope);
356+
expect(result, isNull);
357+
358+
// Expect 2 debug logs: item dropped + all items dropped
359+
expect(logCalls.length, 2);
360+
361+
final itemLog = logCalls[0];
362+
expect(itemLog.level, SentryLevel.debug);
363+
expect(
364+
itemLog.message,
365+
contains(
366+
'Envelope item of type "event" was dropped due to rate limiting'),
367+
);
368+
369+
final fullDropLog = logCalls[1];
370+
expect(fullDropLog.level, SentryLevel.debug);
371+
expect(
372+
fullDropLog.message,
373+
contains('All envelope items were dropped due to rate limiting'),
374+
);
375+
});
376+
377+
test('logs debug when some items dropped and some sent', () {
378+
final options = defaultTestOptions();
379+
options.debug = true;
380+
options.diagnosticLevel = SentryLevel.debug;
381+
382+
final logCalls = <_LogCall>[];
383+
void mockLogger(
384+
SentryLevel level,
385+
String message, {
386+
String? logger,
387+
Object? exception,
388+
StackTrace? stackTrace,
389+
}) {
390+
logCalls.add(_LogCall(level, message));
391+
}
392+
393+
options.log = mockLogger;
394+
395+
final rateLimiter = RateLimiter(options);
396+
397+
// One event (error) and one transaction
398+
final eventItem = SentryEnvelopeItem.fromEvent(SentryEvent());
399+
final transaction = fixture.getTransaction();
400+
final transactionItem = SentryEnvelopeItem.fromTransaction(transaction);
401+
402+
final envelope = SentryEnvelope(
403+
SentryEnvelopeHeader.newEventId(),
404+
[eventItem, transactionItem],
405+
);
406+
407+
// Apply rate limit only for errors so the transaction can still be sent
408+
rateLimiter.updateRetryAfterLimits('60:error:key', null, 1);
409+
410+
final result = rateLimiter.filter(envelope);
411+
expect(result, isNotNull);
412+
expect(result!.items.length, 1);
413+
expect(result.items.first.header.type, 'transaction');
414+
415+
// Expect 2 debug logs: per-item drop + summary
416+
expect(logCalls.length, 2);
417+
418+
final itemLog = logCalls[0];
419+
expect(itemLog.level, SentryLevel.debug);
420+
expect(
421+
itemLog.message,
422+
contains(
423+
'Envelope item of type "event" was dropped due to rate limiting'),
424+
);
425+
426+
final summaryLog = logCalls[1];
427+
expect(summaryLog.level, SentryLevel.debug);
428+
expect(
429+
summaryLog.message,
430+
allOf([
431+
contains('1 envelope item(s) were dropped due to rate limiting'),
432+
contains('but 1 item(s) will still be sent'),
433+
]),
434+
);
435+
});
436+
});
322437
}
323438

324439
class Fixture {
@@ -348,3 +463,10 @@ class Fixture {
348463
return SentryTransaction(tracer);
349464
}
350465
}
466+
467+
class _LogCall {
468+
final SentryLevel level;
469+
final String message;
470+
471+
_LogCall(this.level, this.message);
472+
}

0 commit comments

Comments
 (0)