@@ -419,21 +419,21 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
419
419
/**
420
420
* @inheritDoc
421
421
*/
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 {
425
423
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
+
426
428
// We want to track each category (error, transaction, session, replay_event) separately
427
429
// but still keep the distinction between different type of outcomes.
428
430
// We could use nested maps, but it's much easier to read and type this way.
429
431
// A correct type for map-based implementation if we want to go that route
430
432
// would be `Partial<Record<SentryRequestType, Partial<Record<Outcome, number>>>>`
431
433
// With typescript 4.1 we could even use template literal types
432
434
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 ;
437
437
}
438
438
}
439
439
@@ -778,6 +778,12 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
778
778
. then ( processedEvent => {
779
779
if ( processedEvent === null ) {
780
780
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
+ }
781
787
throw new SentryError ( `${ beforeSendLabel } returned \`null\`, will not send event.` , 'log' ) ;
782
788
}
783
789
@@ -786,6 +792,18 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
786
792
this . _updateSessionFromEvent ( session , processedEvent ) ;
787
793
}
788
794
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
+
789
807
// None of the Sentry built event processor will update transaction name,
790
808
// so if the transaction name has been changed by an event processor, we know
791
809
// it has to come from custom event processor added by a user
@@ -924,6 +942,15 @@ function processBeforeSend(
924
942
}
925
943
926
944
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
+ }
927
954
return beforeSendTransaction ( event , hint ) ;
928
955
}
929
956
0 commit comments