@@ -95,16 +95,39 @@ static void NormalizeOptions(PostHogOptions options, ILogger<PostHogClient> logg
9595 logger . LogWarningProjectApiKeyDeprecated ( ) ;
9696 }
9797
98- options . ProjectToken = options . ProjectToken ? . Trim ( ) ;
99- options . PersonalApiKey = options . PersonalApiKey . NullIfEmpty ( ) ;
100- options . HostUrl = options . HostUrl . NormalizeHostUrl ( ) ;
98+ options . Normalize ( ) ;
10199
102- if ( string . IsNullOrEmpty ( options . ProjectToken ) )
100+ if ( options . ProjectToken is null )
103101 {
104102 logger . LogErrorProjectTokenRequired ( ) ;
105103 }
106104 }
107105
106+ bool IsDisabled ( string methodName )
107+ {
108+ if ( ! _options . Value . Disabled )
109+ {
110+ return false ;
111+ }
112+
113+ _logger . LogWarningClientDisabled ( methodName ) ;
114+ return true ;
115+ }
116+
117+ bool RequiresMissingPersonalApiKey ( AllFeatureFlagsOptions ? options , string methodName )
118+ => options is { OnlyEvaluateLocally : true } && IsPersonalApiKeyMissing ( methodName ) ;
119+
120+ bool IsPersonalApiKeyMissing ( string methodName )
121+ {
122+ if ( _options . Value . PersonalApiKey is not null )
123+ {
124+ return false ;
125+ }
126+
127+ _logger . LogWarningPersonalApiKeyMissing ( methodName ) ;
128+ return true ;
129+ }
130+
108131 /// <summary>
109132 /// To marry up whatever a user does before they sign up or log in with what they do after you need to make an
110133 /// alias call. This will allow you to answer questions like "Which marketing channels leads to users churning
@@ -122,27 +145,48 @@ public async Task<ApiResult> AliasAsync(
122145 string previousId ,
123146 string newId ,
124147 CancellationToken cancellationToken )
125- => await _apiClient . AliasAsync ( previousId , newId , cancellationToken ) ;
148+ {
149+ if ( IsDisabled ( nameof ( AliasAsync ) ) )
150+ {
151+ return new ApiResult ( 0 ) ;
152+ }
153+
154+ return await _apiClient . AliasAsync ( previousId , newId , cancellationToken ) ;
155+ }
126156
127157 /// <inheritdoc/>
128158 public async Task < ApiResult > IdentifyAsync (
129159 string distinctId ,
130160 Dictionary < string , object > ? personPropertiesToSet ,
131161 Dictionary < string , object > ? personPropertiesToSetOnce ,
132162 CancellationToken cancellationToken )
133- => await _apiClient . IdentifyAsync (
163+ {
164+ if ( IsDisabled ( nameof ( IdentifyAsync ) ) )
165+ {
166+ return new ApiResult ( 0 ) ;
167+ }
168+
169+ return await _apiClient . IdentifyAsync (
134170 distinctId ,
135171 personPropertiesToSet ,
136172 personPropertiesToSetOnce ,
137173 cancellationToken ) ;
174+ }
138175
139176 /// <inheritdoc/>
140177 public Task < ApiResult > GroupIdentifyAsync (
141178 string type ,
142179 StringOrValue < int > key ,
143180 Dictionary < string , object > ? properties ,
144181 CancellationToken cancellationToken )
145- => _apiClient . GroupIdentifyAsync ( type , key , properties , cancellationToken ) ;
182+ {
183+ if ( IsDisabled ( nameof ( GroupIdentifyAsync ) ) )
184+ {
185+ return Task . FromResult ( new ApiResult ( 0 ) ) ;
186+ }
187+
188+ return _apiClient . GroupIdentifyAsync ( type , key , properties , cancellationToken ) ;
189+ }
146190
147191 /// <inheritdoc/>
148192 public Task < ApiResult > GroupIdentifyAsync (
@@ -151,7 +195,14 @@ public Task<ApiResult> GroupIdentifyAsync(
151195 StringOrValue < int > key ,
152196 Dictionary < string , object > ? properties ,
153197 CancellationToken cancellationToken )
154- => _apiClient . GroupIdentifyAsync ( type , key , properties , cancellationToken , distinctId ) ;
198+ {
199+ if ( IsDisabled ( nameof ( GroupIdentifyAsync ) ) )
200+ {
201+ return Task . FromResult ( new ApiResult ( 0 ) ) ;
202+ }
203+
204+ return _apiClient . GroupIdentifyAsync ( type , key , properties , cancellationToken , distinctId ) ;
205+ }
155206
156207 /// <inheritdoc/>
157208 public bool Capture (
@@ -162,6 +213,11 @@ public bool Capture(
162213 bool sendFeatureFlags ,
163214 DateTimeOffset ? timestamp = null )
164215 {
216+ if ( IsDisabled ( nameof ( Capture ) ) )
217+ {
218+ return false ;
219+ }
220+
165221 // If custom timestamp provided, add it to properties
166222 if ( timestamp . HasValue )
167223 {
@@ -218,6 +274,11 @@ public bool CaptureException(
218274 bool sendFeatureFlags ,
219275 DateTimeOffset ? timestamp = null )
220276 {
277+ if ( IsDisabled ( nameof ( CaptureException ) ) )
278+ {
279+ return false ;
280+ }
281+
221282 if ( exception == null )
222283 {
223284 _logger . LogErrorCaptureExceptionNull ( ) ;
@@ -305,6 +366,16 @@ public async Task<bool> IsFeatureEnabledAsync(
305366 FeatureFlagOptions ? options ,
306367 CancellationToken cancellationToken )
307368 {
369+ if ( IsDisabled ( nameof ( IsFeatureEnabledAsync ) ) )
370+ {
371+ return false ;
372+ }
373+
374+ if ( RequiresMissingPersonalApiKey ( options , nameof ( IsFeatureEnabledAsync ) ) )
375+ {
376+ return false ;
377+ }
378+
308379 var result = await GetFeatureFlagAsync (
309380 featureKey ,
310381 distinctId ,
@@ -321,6 +392,16 @@ public async Task<bool> IsFeatureEnabledAsync(
321392 FeatureFlagOptions ? options ,
322393 CancellationToken cancellationToken )
323394 {
395+ if ( IsDisabled ( nameof ( GetFeatureFlagAsync ) ) )
396+ {
397+ return null ;
398+ }
399+
400+ if ( RequiresMissingPersonalApiKey ( options , nameof ( GetFeatureFlagAsync ) ) )
401+ {
402+ return null ;
403+ }
404+
324405 LocalEvaluator ? localEvaluator ;
325406 try
326407 {
@@ -461,7 +542,12 @@ void HandleRemoteError(Exception ex, string errorType)
461542 /// <inheritdoc/>
462543 public async Task < JsonDocument ? > GetRemoteConfigPayloadAsync ( string key , CancellationToken cancellationToken )
463544 {
464- if ( _options . Value . PersonalApiKey is null )
545+ if ( IsDisabled ( nameof ( GetRemoteConfigPayloadAsync ) ) )
546+ {
547+ return null ;
548+ }
549+
550+ if ( IsPersonalApiKeyMissing ( nameof ( GetRemoteConfigPayloadAsync ) ) )
465551 {
466552 _logger . LogWarningPersonalApiKeyRequiredForRemoteConfigPayload ( ) ;
467553 return null ;
@@ -565,6 +651,16 @@ public async Task<IReadOnlyDictionary<string, FeatureFlag>> GetAllFeatureFlagsAs
565651 AllFeatureFlagsOptions ? options ,
566652 CancellationToken cancellationToken )
567653 {
654+ if ( IsDisabled ( nameof ( GetAllFeatureFlagsAsync ) ) )
655+ {
656+ return new Dictionary < string , FeatureFlag > ( ) ;
657+ }
658+
659+ if ( RequiresMissingPersonalApiKey ( options , nameof ( GetAllFeatureFlagsAsync ) ) )
660+ {
661+ return new Dictionary < string , FeatureFlag > ( ) ;
662+ }
663+
568664 if ( _options . Value . PersonalApiKey is not null )
569665 {
570666 // Attempt to load local feature flags.
@@ -650,7 +746,12 @@ public async Task LoadFeatureFlagsAsync(CancellationToken cancellationToken)
650746 {
651747 _logger . LogInfoLoadFeatureFlags ( ) ;
652748
653- if ( _options . Value . PersonalApiKey is null )
749+ if ( IsDisabled ( nameof ( LoadFeatureFlagsAsync ) ) )
750+ {
751+ return ;
752+ }
753+
754+ if ( IsPersonalApiKeyMissing ( nameof ( LoadFeatureFlagsAsync ) ) )
654755 {
655756 _logger . LogWarningPersonalApiKeyRequired ( ) ;
656757 return ;
@@ -678,7 +779,15 @@ public async Task LoadFeatureFlagsAsync(CancellationToken cancellationToken)
678779 }
679780
680781 /// <inheritdoc/>
681- public async Task FlushAsync ( ) => await _asyncBatchHandler . FlushAsync ( ) ;
782+ public async Task FlushAsync ( )
783+ {
784+ if ( IsDisabled ( nameof ( FlushAsync ) ) )
785+ {
786+ return ;
787+ }
788+
789+ await _asyncBatchHandler . FlushAsync ( ) ;
790+ }
682791
683792 /// <inheritdoc/>
684793 public string Version => VersionConstants . Version ;
@@ -688,6 +797,11 @@ public async Task LoadFeatureFlagsAsync(CancellationToken cancellationToken)
688797 [ Obsolete ( "This method is for internal use only and may go away soon." ) ]
689798 internal async Task < LocalEvaluator ? > GetLocalEvaluatorAsync ( CancellationToken cancellationToken )
690799 {
800+ if ( IsDisabled ( nameof ( GetLocalEvaluatorAsync ) ) )
801+ {
802+ return null ;
803+ }
804+
691805 try
692806 {
693807 return await _featureFlagsLoader . GetFeatureFlagsForLocalEvaluationAsync ( cancellationToken ) ;
@@ -705,7 +819,15 @@ public async Task LoadFeatureFlagsAsync(CancellationToken cancellationToken)
705819 /// <summary>
706820 /// Clears the local flags cache.
707821 /// </summary>
708- public void ClearLocalFlagsCache ( ) => _featureFlagsLoader . Clear ( ) ;
822+ public void ClearLocalFlagsCache ( )
823+ {
824+ if ( IsDisabled ( nameof ( ClearLocalFlagsCache ) ) )
825+ {
826+ return ;
827+ }
828+
829+ _featureFlagsLoader . Clear ( ) ;
830+ }
709831
710832 /// <inheritdoc/>
711833 public async ValueTask DisposeAsync ( )
@@ -875,4 +997,16 @@ public static partial void LogErrorUnableToGetRemoteConfigPayload(
875997 Level = LogLevel . Error ,
876998 Message = "CaptureException failed with an exception" ) ]
877999 public static partial void LogErrorCaptureExceptionFailed ( this ILogger < PostHogClient > logger , Exception exception ) ;
1000+
1001+ [ LoggerMessage (
1002+ EventId = 21 ,
1003+ Level = LogLevel . Warning ,
1004+ Message = "PostHog SDK is disabled; {MethodName} is a no-op." ) ]
1005+ public static partial void LogWarningClientDisabled ( this ILogger < PostHogClient > logger , string methodName ) ;
1006+
1007+ [ LoggerMessage (
1008+ EventId = 22 ,
1009+ Level = LogLevel . Warning ,
1010+ Message = "PostHog personal_api_key is not configured; {MethodName} is a no-op." ) ]
1011+ public static partial void LogWarningPersonalApiKeyMissing ( this ILogger < PostHogClient > logger , string methodName ) ;
8781012}
0 commit comments