@@ -68,7 +68,7 @@ public PostHogClient(
6868 loggerFactory . CreateLogger < PostHogApiClient > ( )
6969 ) ;
7070 _asyncBatchHandler = new AsyncBatchHandler < CapturedEvent , CapturedEventBatchContext > (
71- batch => _apiClient . CaptureBatchAsync ( batch , CancellationToken . None ) ,
71+ CaptureBatchAsync ,
7272 batchContextFunc : ( ) => new CapturedEventBatchContext (
7373 new FallbackFeatureFlagCache (
7474 new MemoryFeatureFlagCache ( _timeProvider , 10000 , 0.2 ) ,
@@ -331,27 +331,83 @@ bool CaptureCore(
331331 _logger . LogWarnCaptureFailed ( eventName , capturedEvent . Properties . Count , _asyncBatchHandler . Count ) ;
332332 return false ;
333333
334- Task < CapturedEvent > BatchTask ( CapturedEventBatchContext context )
334+ async Task < CapturedEvent > BatchTask ( CapturedEventBatchContext context )
335335 {
336+ CapturedEvent enrichedEvent ;
336337 if ( flags is not null )
337338 {
338- AddFeatureFlagsToCapturedEvent ( capturedEvent , flags ) ;
339- return Task . FromResult ( capturedEvent ) ;
339+ enrichedEvent = AddFeatureFlagsToCapturedEvent ( capturedEvent , flags ) ;
340340 }
341-
342- if ( ! sendFeatureFlags )
341+ else if ( ! sendFeatureFlags )
342+ {
343+ enrichedEvent = capturedEvent ;
344+ }
345+ else if ( _featureFlagsLoader . IsLoaded )
346+ {
347+ // Prefer local evaluation when available
348+ enrichedEvent = await AddLocalFeatureFlagDataAsync ( captureContext . DistinctId , groups , capturedEvent ) ;
349+ }
350+ else
343351 {
344- return Task . FromResult ( capturedEvent ) ;
352+ // Otherwise we fall back to remote /flags call
353+ enrichedEvent = await AddFreshFeatureFlagDataAsync (
354+ context . FeatureFlagCache ,
355+ captureContext . DistinctId ,
356+ groups ,
357+ capturedEvent ) ;
345358 }
346359
347- // Prefer local evaluation when available
348- if ( _featureFlagsLoader . IsLoaded )
360+ return enrichedEvent ;
361+ }
362+ }
363+
364+ async Task CaptureBatchAsync ( IEnumerable < CapturedEvent > batch )
365+ {
366+ var beforeSend = _options . Value . BeforeSend ;
367+ if ( beforeSend is null )
368+ {
369+ await _apiClient . CaptureBatchAsync ( batch , CancellationToken . None ) ;
370+ return ;
371+ }
372+
373+ var events = batch
374+ . Select ( ApplyBeforeSend )
375+ . Where ( capturedEvent => capturedEvent is not null )
376+ . Select ( capturedEvent => capturedEvent ! )
377+ . ToArray ( ) ;
378+
379+ if ( events . Length is 0 )
380+ {
381+ return ;
382+ }
383+
384+ await _apiClient . CaptureBatchAsync ( events , CancellationToken . None ) ;
385+ }
386+
387+ CapturedEvent ? ApplyBeforeSend ( CapturedEvent capturedEvent )
388+ {
389+ var beforeSend = _options . Value . BeforeSend ;
390+ if ( beforeSend is null )
391+ {
392+ return capturedEvent ;
393+ }
394+
395+ try
396+ {
397+ var result = beforeSend ( capturedEvent ) ;
398+ if ( result is null )
349399 {
350- return AddLocalFeatureFlagDataAsync ( captureContext . DistinctId , groups , capturedEvent ) ;
400+ _logger . LogDebugBeforeSendDropped ( capturedEvent . EventName ) ;
351401 }
352402
353- // Otherwise we fall back to remote /flags call
354- return AddFreshFeatureFlagDataAsync ( context . FeatureFlagCache , captureContext . DistinctId , groups , capturedEvent ) ;
403+ return result ;
404+ }
405+ #pragma warning disable CA1031 // Customer callbacks can throw any exception; drop just this event.
406+ catch ( Exception ex )
407+ #pragma warning restore CA1031
408+ {
409+ _logger . LogErrorBeforeSendException ( ex , capturedEvent . EventName ) ;
410+ return null ;
355411 }
356412 }
357413
@@ -1435,4 +1491,16 @@ public static partial void LogErrorUnableToGetRemoteConfigPayload(
14351491 Level = LogLevel . Error ,
14361492 Message = "PostHog API call failed in {MethodName}; returning a no-op result." ) ]
14371493 public static partial void LogErrorApiCallFailed ( this ILogger < PostHogClient > logger , Exception exception , string methodName ) ;
1494+
1495+ [ LoggerMessage (
1496+ EventId = 25 ,
1497+ Level = LogLevel . Debug ,
1498+ Message = "Event {EventName} was dropped by the before send callback" ) ]
1499+ public static partial void LogDebugBeforeSendDropped ( this ILogger < PostHogClient > logger , string eventName ) ;
1500+
1501+ [ LoggerMessage (
1502+ EventId = 26 ,
1503+ Level = LogLevel . Error ,
1504+ Message = "Error in before send callback for event {EventName}; dropping event" ) ]
1505+ public static partial void LogErrorBeforeSendException ( this ILogger < PostHogClient > logger , Exception exception , string eventName ) ;
14381506}
0 commit comments