11import {
2- DataCategory ,
32 Envelope ,
3+ EnvelopeItem ,
4+ EventDropReason ,
45 InternalBaseTransportOptions ,
56 Transport ,
67 TransportRequestExecutor ,
78} from '@sentry/types' ;
89import {
9- getEnvelopeType ,
10+ createEnvelope ,
11+ envelopeItemTypeToDataCategory ,
12+ forEachEnvelopeItem ,
1013 isRateLimited ,
14+ logger ,
1115 makePromiseBuffer ,
1216 PromiseBuffer ,
1317 RateLimits ,
1418 resolvedSyncPromise ,
19+ SentryError ,
1520 serializeEnvelope ,
1621 updateRateLimits ,
1722} from '@sentry/utils' ;
1823
24+ import { IS_DEBUG_BUILD } from '../flags' ;
25+
1926export const DEFAULT_TRANSPORT_BUFFER_SIZE = 30 ;
2027
2128/**
@@ -34,22 +41,58 @@ export function createTransport(
3441 const flush = ( timeout ?: number ) : PromiseLike < boolean > => buffer . drain ( timeout ) ;
3542
3643 function send ( envelope : Envelope ) : PromiseLike < void > {
37- const envCategory = getEnvelopeType ( envelope ) ;
38- const category = envCategory === 'event' ? 'error' : ( envCategory as DataCategory ) ;
44+ const filteredEnvelopeItems : EnvelopeItem [ ] = [ ] ;
45+
46+ // Drop rate limited items from envelope
47+ forEachEnvelopeItem ( envelope , ( item , type ) => {
48+ const envelopeItemDataCategory = envelopeItemTypeToDataCategory ( type ) ;
49+ if ( isRateLimited ( rateLimits , envelopeItemDataCategory ) ) {
50+ options . recordDroppedEvent ( 'ratelimit_backoff' , envelopeItemDataCategory ) ;
51+ } else {
52+ filteredEnvelopeItems . push ( item ) ;
53+ }
54+ } ) ;
3955
40- // Don't add to buffer if transport is already rate- limited
41- if ( isRateLimited ( rateLimits , category ) ) {
56+ // Skip sending if envelope is empty after filtering out rate limited events
57+ if ( filteredEnvelopeItems . length === 0 ) {
4258 return resolvedSyncPromise ( ) ;
4359 }
4460
45- const requestTask = ( ) : PromiseLike < void > =>
46- makeRequest ( { body : serializeEnvelope ( envelope ) } ) . then ( ( { headers } ) : void => {
47- if ( headers ) {
48- rateLimits = updateRateLimits ( rateLimits , headers ) ;
49- }
61+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
62+ const filteredEnvelope : Envelope = createEnvelope ( envelope [ 0 ] , filteredEnvelopeItems as any ) ;
63+
64+ // Creates client report for each item in an envelope
65+ const recordEnvelopeLoss = ( reason : EventDropReason ) : void => {
66+ forEachEnvelopeItem ( filteredEnvelope , ( _ , type ) => {
67+ options . recordDroppedEvent ( reason , envelopeItemTypeToDataCategory ( type ) ) ;
5068 } ) ;
69+ } ;
70+
71+ const requestTask = ( ) : PromiseLike < void > =>
72+ makeRequest ( { body : serializeEnvelope ( filteredEnvelope ) } ) . then (
73+ ( { headers } ) : void => {
74+ if ( headers ) {
75+ rateLimits = updateRateLimits ( rateLimits , headers ) ;
76+ }
77+ } ,
78+ error => {
79+ IS_DEBUG_BUILD && logger . error ( 'Failed while recording event:' , error ) ;
80+ recordEnvelopeLoss ( 'network_error' ) ;
81+ } ,
82+ ) ;
5183
52- return buffer . add ( requestTask ) ;
84+ return buffer . add ( requestTask ) . then (
85+ result => result ,
86+ error => {
87+ if ( error instanceof SentryError ) {
88+ IS_DEBUG_BUILD && logger . error ( 'Skipped sending event due to full buffer' ) ;
89+ recordEnvelopeLoss ( 'queue_overflow' ) ;
90+ return resolvedSyncPromise ( ) ;
91+ } else {
92+ throw error ;
93+ }
94+ } ,
95+ ) ;
5396 }
5497
5598 return {
0 commit comments