Repository navigation
Add debug logging for feature flag onboarding #12897
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3613869
334209b
4f27f55
c3b3317
19562a9
0cae04f
e518e5a
6554ed2
92567d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| package datadog.trace.api.openfeature; | ||
|
|
||
| import static datadog.trace.api.openfeature.Provider.debugQuietly; | ||
| import static java.util.Arrays.asList; | ||
|
|
||
| import datadog.trace.api.featureflag.FeatureFlaggingGateway; | ||
|
|
@@ -225,6 +226,10 @@ public boolean initialize( | |
| final long timeout, final TimeUnit unit, final EvaluationContext context) throws Exception { | ||
| FeatureFlaggingGateway.activate(); | ||
| FeatureFlaggingGateway.addConfigListener(this); | ||
| debugQuietly( | ||
| log, | ||
| "Feature Flagging provider configuration available at subscription: {}", | ||
| hasConfiguration()); | ||
| return initializationLatch.await(timeout, unit) || hasConfiguration(); | ||
| } | ||
|
|
||
|
|
@@ -259,29 +264,42 @@ public <T> ProviderEvaluation<T> evaluate( | |
| // ProviderEvaluation returned, so the hook's consent decision is pinned to this evaluation's | ||
| // config and cannot drift on a concurrent Remote Config swap. | ||
| final ServerConfiguration config = configuration.get(); | ||
| final ProviderEvaluation<T> result = resolve(target, key, defaultValue, context, config); | ||
| DebugLoggingHook.resolution(key, defaultValue, context, result, config); | ||
| return result; | ||
| } | ||
|
|
||
| private <T> ProviderEvaluation<T> resolve( | ||
| final Class<T> target, | ||
| final String key, | ||
| final T defaultValue, | ||
| final EvaluationContext context, | ||
| final ServerConfiguration config) { | ||
| // Boolean.TRUE.equals covers both null (privacy-preserving default) and Boolean.FALSE without | ||
| // an NPE — the field is boxed so a malformed UFC message doesn't abort the whole parse. | ||
| final boolean observeFullEvaluationData = | ||
| config != null && Boolean.TRUE.equals(config.observeFullEvaluationData); | ||
| try { | ||
| if (config == null) { | ||
| return error(defaultValue, ErrorCode.PROVIDER_NOT_READY, null, observeFullEvaluationData); | ||
| return error( | ||
| key, defaultValue, ErrorCode.PROVIDER_NOT_READY, null, observeFullEvaluationData); | ||
| } | ||
|
|
||
| if (context == null) { | ||
| return error(defaultValue, ErrorCode.INVALID_CONTEXT, null, observeFullEvaluationData); | ||
| return error(key, defaultValue, ErrorCode.INVALID_CONTEXT, null, observeFullEvaluationData); | ||
| } | ||
|
|
||
| final Flag flag = config.flags.get(key); | ||
| if (flag == null) { | ||
| if (config.invalidFlags != null && config.invalidFlags.containsKey(key)) { | ||
| return error( | ||
| key, | ||
| defaultValue, | ||
| ErrorCode.PARSE_ERROR, | ||
| "invalid configuration for flag " + key, | ||
| observeFullEvaluationData); | ||
| } | ||
| return error(defaultValue, ErrorCode.FLAG_NOT_FOUND, null, observeFullEvaluationData); | ||
| return error(key, defaultValue, ErrorCode.FLAG_NOT_FOUND, null, observeFullEvaluationData); | ||
| } | ||
|
|
||
| if (!flag.enabled) { | ||
|
|
@@ -294,6 +312,7 @@ public <T> ProviderEvaluation<T> evaluate( | |
|
|
||
| if (flag.allocations == null) { | ||
| return error( | ||
| key, | ||
| defaultValue, | ||
| ErrorCode.GENERAL, | ||
| "Missing allocations for flag " + key, | ||
|
|
@@ -332,7 +351,11 @@ public <T> ProviderEvaluation<T> evaluate( | |
| } else { | ||
| if (targetingKey == null) { | ||
| return error( | ||
| defaultValue, ErrorCode.TARGETING_KEY_MISSING, null, observeFullEvaluationData); | ||
| key, | ||
| defaultValue, | ||
| ErrorCode.TARGETING_KEY_MISSING, | ||
| null, | ||
| observeFullEvaluationData); | ||
| } | ||
| // To match a split, subject must match ALL underlying shards | ||
| boolean allShardsMatch = true; | ||
|
|
@@ -366,12 +389,13 @@ public <T> ProviderEvaluation<T> evaluate( | |
| .flagMetadata(consentMetadata(observeFullEvaluationData)) | ||
| .build(); | ||
| } catch (final PatternSyntaxException e) { | ||
| return error(defaultValue, ErrorCode.PARSE_ERROR, e.getMessage(), observeFullEvaluationData); | ||
| return error( | ||
| key, defaultValue, ErrorCode.PARSE_ERROR, e.getMessage(), observeFullEvaluationData); | ||
| } catch (final NumberFormatException e) { | ||
| return error( | ||
| defaultValue, ErrorCode.TYPE_MISMATCH, e.getMessage(), observeFullEvaluationData); | ||
| key, defaultValue, ErrorCode.TYPE_MISMATCH, e.getMessage(), observeFullEvaluationData); | ||
| } catch (final Exception e) { | ||
| return error(defaultValue, ErrorCode.GENERAL, e.getMessage(), observeFullEvaluationData); | ||
| return error(key, defaultValue, ErrorCode.GENERAL, e.getMessage(), observeFullEvaluationData); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -382,10 +406,12 @@ private static ImmutableMetadata consentMetadata(final boolean observeFullEvalua | |
| } | ||
|
|
||
| private static <T> ProviderEvaluation<T> error( | ||
| final String key, | ||
| final T defaultValue, | ||
| final ErrorCode code, | ||
| final String errorMessage, | ||
| final boolean observeFullEvaluationData) { | ||
| DebugLoggingHook.resolutionError(key, code, errorMessage); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. --- found with local Claude review P2 — The raw The comment just below says these messages "can echo raw evaluation-context values, so they must never reach any consumer". Suggestion: pass the consent-filtered message, or explicitly document in the README that debug mode logs raw PII. The same question applies to the raw context in |
||
| // Under consent-off the errorMessage is dropped: exception messages from the outer catch blocks | ||
| // (NumberFormatException, generic Exception) can echo raw evaluation-context values, so they | ||
| // must never reach any consumer of ProviderEvaluation.getErrorMessage() — not just our own | ||
|
|
@@ -611,6 +637,7 @@ private static <T> ProviderEvaluation<T> resolveVariant( | |
| final Variant variant = flag.variations.get(variationKey); | ||
| if (variant == null) { | ||
| return error( | ||
| key, | ||
| defaultValue, | ||
| ErrorCode.GENERAL, | ||
| "Variant not found for: " + variationKey, | ||
|
|
@@ -619,6 +646,7 @@ private static <T> ProviderEvaluation<T> resolveVariant( | |
|
|
||
| if (!isTypeCompatible(target, flag.variationType)) { | ||
| return error( | ||
| key, | ||
| defaultValue, | ||
| ErrorCode.TYPE_MISMATCH, | ||
| "Requested type " | ||
|
|
@@ -633,6 +661,7 @@ private static <T> ProviderEvaluation<T> resolveVariant( | |
| mappedValue = mapValue(target, variant.value); | ||
| } catch (final NumberFormatException e) { | ||
| return error( | ||
| key, | ||
| defaultValue, | ||
| ErrorCode.PARSE_ERROR, | ||
| "Variant '" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
--- found with local Claude review
P3 — Nit: the version is hardcoded (
logback-classic:1.6.5). Prefer adding it to the version catalog (libs), like the other test dependencies.