@@ -371,20 +371,21 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
371371 /**
372372 * @inheritDoc
373373 */
374- public recordDroppedEvent ( reason : EventDropReason , category : DataCategory , _event ?: Event ) : void {
375- // Note: we use `event` in replay, where we overwrite this hook.
376-
374+ public recordDroppedEvent ( reason : EventDropReason , category : DataCategory , eventOrCount ?: Event | number ) : void {
377375 if ( this . _options . sendClientReports ) {
376+ // TODO v9: We do not need the `event` passed as third argument anymore, and can possibly remove this overload
377+ // If event is passed as third argument, we assume this is a count of 1
378+ const count = typeof eventOrCount === 'number' ? eventOrCount : 1 ;
379+
378380 // We want to track each category (error, transaction, session, replay_event) separately
379381 // but still keep the distinction between different type of outcomes.
380382 // We could use nested maps, but it's much easier to read and type this way.
381383 // A correct type for map-based implementation if we want to go that route
382384 // would be `Partial<Record<SentryRequestType, Partial<Record<Outcome, number>>>>`
383385 // With typescript 4.1 we could even use template literal types
384386 const key = `${ reason } :${ category } ` ;
385- DEBUG_BUILD && logger . log ( `Adding outcome: "${ key } "` ) ;
386-
387- this . _outcomes [ key ] = ( this . _outcomes [ key ] || 0 ) + 1 ;
387+ DEBUG_BUILD && logger . log ( `Recording outcome: "${ key } "${ count > 1 ? ` (${ count } times)` : '' } ` ) ;
388+ this . _outcomes [ key ] = ( this . _outcomes [ key ] || 0 ) + count ;
388389 }
389390 }
390391
@@ -794,11 +795,11 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
794795 . then ( processedEvent => {
795796 if ( processedEvent === null ) {
796797 this . recordDroppedEvent ( 'before_send' , dataCategory , event ) ;
797- if ( isTransactionEvent ( event ) ) {
798+ if ( isTransaction ) {
798799 const spans = event . spans || [ ] ;
799800 // the transaction itself counts as one span, plus all the child spans that are added
800801 const spanCount = 1 + spans . length ;
801- this . _outcomes [ 'span' ] = ( this . _outcomes [ 'span' ] || 0 ) + spanCount ;
802+ this . recordDroppedEvent ( 'before_send' , 'span' , spanCount ) ;
802803 }
803804 throw new SentryError ( `${ beforeSendLabel } returned \`null\`, will not send event.` , 'log' ) ;
804805 }
@@ -808,6 +809,18 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
808809 this . _updateSessionFromEvent ( session , processedEvent ) ;
809810 }
810811
812+ if ( isTransaction ) {
813+ const spanCountBefore =
814+ ( processedEvent . sdkProcessingMetadata && processedEvent . sdkProcessingMetadata . spanCountBeforeProcessing ) ||
815+ 0 ;
816+ const spanCountAfter = processedEvent . spans ? processedEvent . spans . length : 0 ;
817+
818+ const droppedSpanCount = spanCountBefore - spanCountAfter ;
819+ if ( droppedSpanCount > 0 ) {
820+ this . recordDroppedEvent ( 'before_send' , 'span' , droppedSpanCount ) ;
821+ }
822+ }
823+
811824 // None of the Sentry built event processor will update transaction name,
812825 // so if the transaction name has been changed by an event processor, we know
813826 // it has to come from custom event processor added by a user
@@ -973,6 +986,15 @@ function processBeforeSend(
973986 }
974987
975988 if ( beforeSendTransaction ) {
989+ if ( event . spans ) {
990+ // We store the # of spans before processing in SDK metadata,
991+ // so we can compare it afterwards to determine how many spans were dropped
992+ const spanCountBefore = event . spans . length ;
993+ event . sdkProcessingMetadata = {
994+ ...event . sdkProcessingMetadata ,
995+ spanCountBeforeProcessing : spanCountBefore ,
996+ } ;
997+ }
976998 return beforeSendTransaction ( event , hint ) ;
977999 }
9781000 }
0 commit comments