@@ -419,21 +419,21 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
419419 /**
420420 * @inheritDoc
421421 */
422- public recordDroppedEvent ( reason : EventDropReason , category : DataCategory , _event ?: Event ) : void {
423- // Note: we use `event` in replay, where we overwrite this hook.
424-
422+ public recordDroppedEvent ( reason : EventDropReason , category : DataCategory , eventOrCount ?: Event | number ) : void {
425423 if ( this . _options . sendClientReports ) {
424+ // TODO v9: We do not need the `event` passed as third argument anymore, and can possibly remove this overload
425+ // If event is passed as third argument, we assume this is a count of 1
426+ const count = typeof eventOrCount === 'number' ? eventOrCount : 1 ;
427+
426428 // We want to track each category (error, transaction, session, replay_event) separately
427429 // but still keep the distinction between different type of outcomes.
428430 // We could use nested maps, but it's much easier to read and type this way.
429431 // A correct type for map-based implementation if we want to go that route
430432 // would be `Partial<Record<SentryRequestType, Partial<Record<Outcome, number>>>>`
431433 // With typescript 4.1 we could even use template literal types
432434 const key = `${ reason } :${ category } ` ;
433- DEBUG_BUILD && logger . log ( `Adding outcome: "${ key } "` ) ;
434-
435- // The following works because undefined + 1 === NaN and NaN is falsy
436- this . _outcomes [ key ] = this . _outcomes [ key ] + 1 || 1 ;
435+ DEBUG_BUILD && logger . log ( `Recording outcome: "${ key } "${ count > 1 ? ` (${ count } times)` : '' } ` ) ;
436+ this . _outcomes [ key ] = ( this . _outcomes [ key ] || 0 ) + count ;
437437 }
438438 }
439439
@@ -778,6 +778,12 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
778778 . then ( processedEvent => {
779779 if ( processedEvent === null ) {
780780 this . recordDroppedEvent ( 'before_send' , dataCategory , event ) ;
781+ if ( isTransaction ) {
782+ const spans = event . spans || [ ] ;
783+ // the transaction itself counts as one span, plus all the child spans that are added
784+ const spanCount = 1 + spans . length ;
785+ this . recordDroppedEvent ( 'before_send' , 'span' , spanCount ) ;
786+ }
781787 throw new SentryError ( `${ beforeSendLabel } returned \`null\`, will not send event.` , 'log' ) ;
782788 }
783789
@@ -786,6 +792,18 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
786792 this . _updateSessionFromEvent ( session , processedEvent ) ;
787793 }
788794
795+ if ( isTransaction ) {
796+ const spanCountBefore =
797+ ( processedEvent . sdkProcessingMetadata && processedEvent . sdkProcessingMetadata . spanCountBeforeProcessing ) ||
798+ 0 ;
799+ const spanCountAfter = processedEvent . spans ? processedEvent . spans . length : 0 ;
800+
801+ const droppedSpanCount = spanCountBefore - spanCountAfter ;
802+ if ( droppedSpanCount > 0 ) {
803+ this . recordDroppedEvent ( 'before_send' , 'span' , droppedSpanCount ) ;
804+ }
805+ }
806+
789807 // None of the Sentry built event processor will update transaction name,
790808 // so if the transaction name has been changed by an event processor, we know
791809 // it has to come from custom event processor added by a user
@@ -924,6 +942,15 @@ function processBeforeSend(
924942 }
925943
926944 if ( isTransactionEvent ( event ) && beforeSendTransaction ) {
945+ if ( event . spans ) {
946+ // We store the # of spans before processing in SDK metadata,
947+ // so we can compare it afterwards to determine how many spans were dropped
948+ const spanCountBefore = event . spans . length ;
949+ event . sdkProcessingMetadata = {
950+ ...event . sdkProcessingMetadata ,
951+ spanCountBeforeProcessing : spanCountBefore ,
952+ } ;
953+ }
927954 return beforeSendTransaction ( event , hint ) ;
928955 }
929956
0 commit comments