Commit 81bb305
Add flag evaluation metrics via OTel counter and OpenFeature Hook (#11040)
Add flag evaluation metrics via OTel counter and OpenFeature Hook
Record a `feature_flag.evaluations` OTel counter on every flag evaluation
using an OpenFeature `finallyAfter` hook. The hook captures all evaluation
paths including type mismatches that occur above the provider level.
Attributes: feature_flag.key, feature_flag.result.variant,
feature_flag.result.reason, error.type (on error),
feature_flag.result.allocation_key (when present).
Counter is a no-op when DD_METRICS_OTEL_ENABLED is false or
opentelemetry-api is absent from the classpath.
Use own SdkMeterProvider with OTLP HTTP exporter for eval metrics
Replace GlobalOpenTelemetry.getMeterProvider() with a dedicated
SdkMeterProvider + OtlpHttpMetricExporter that sends metrics
directly to the DD Agent's OTLP endpoint (default :4318/v1/metrics).
This avoids the agent's OTel class shading issue where the agent
relocates io.opentelemetry.api.* to datadog.trace.bootstrap.otel.api.*,
making GlobalOpenTelemetry calls from the dd-openfeature jar hit the
unshaded no-op provider instead of the agent's shim.
Requires opentelemetry-sdk-metrics and opentelemetry-exporter-otlp
on the application classpath. Falls back to no-op if absent.
System tests: 11/17 pass. 6 failures are pre-existing DDEvaluator
gaps (reason mapping, parse errors, type mismatch strictness).
Address code review feedback for eval metrics
- Add explicit null guard for details in FlagEvalHook.finallyAfter()
- Add OTEL_EXPORTER_OTLP_ENDPOINT generic env var fallback with
/v1/metrics path appended (per OTel spec fallback chain)
- Add comments clarifying signal-specific vs generic endpoint behavior
Fix NoClassDefFoundError when OTel SDK absent from classpath
When the OTel SDK jars are not on the application classpath,
loading FlagEvalMetrics fails because field types reference
OTel SDK classes (SdkMeterProvider). This propagated as an
uncaught NoClassDefFoundError from the Provider constructor,
crashing provider initialization.
Fix:
- Change meterProvider field type from SdkMeterProvider to
Closeable (always on classpath), use local SdkMeterProvider
variable inside try block
- Catch NoClassDefFoundError in Provider constructor when
creating FlagEvalMetrics
- Null-safe getProviderHooks() and shutdown() when metrics
is null
Move FlagEvalHook construction inside try/catch block
FlagEvalHook references FlagEvalMetrics in its field declaration.
On JVMs that eagerly verify field types during class loading,
constructing FlagEvalHook outside the try/catch could throw
NoClassDefFoundError if OTel classes failed to load. Moving it
inside the try block ensures both metrics and hook are null-safe
when OTel is absent.
Add README for dd-openfeature with eval metrics setup
Documents the published artifact setup, evaluation metrics
dependencies (opentelemetry-sdk-metrics, opentelemetry-exporter-otlp),
OTLP endpoint configuration, metric attributes, and requirements.
Use ConfigHelper.env() instead of System.getenv()
System.getenv() is forbidden by the project's forbiddenApis rules.
Replace with ConfigHelper.env() which is the approved way to read
environment variables. Add config-utils as compileOnly dependency.
Address PR review feedback from manuel-alvarez-alvarez
- Remove transitive openfeature-sdk dep from README setup section
- Import ErrorCode at top of FlagEvalHook instead of inline FQN
Merge remote-tracking branch 'origin/master' into typo/evaluations-logging
Add evaluationLogging option and log errors when OTel SDK absent
- Add Options.evaluationLogging(boolean) — default true per EVALLOG.12
- When disabled: no metrics, no hook, no error
- When enabled + OTel SDK missing: log.error with instructions to
add deps or disable, degrade to no-op (matches Go/Python pattern)
- When enabled + OTel init failure: log.error with message, degrade
- Remove silent catch — FlagEvalMetrics now logs at error level for
NoClassDefFoundError and at error level for other init failures
Fix feature_flag.evaluations metric count always being zero
The OTel SDK defaults to DELTA temporality for counters. The Datadog
agent converts OTLP delta monotonic sums to rate metrics by dividing
by the export interval (10s). Five evaluations in under 1s produce
~0.5, which rounds to zero in the points payload.
Force CUMULATIVE temporality on the OtlpHttpMetricExporter so the
agent receives an absolute count rather than a rate, making
test_ffe_eval_metric_count reliable.
test(openfeature): verify cumulative temporality and count accumulation in FlagEvalMetrics
Address internal review feedback
- Remove exporterIsConfiguredWithCumulativeTemporalityForCounters
test (tested OTel SDK, not our code; the integration test is the
real regression guard)
- Fix Provider catch block comment to reflect that FlagEvalMetrics
may not have logged if we reach this point
- Include exception in log.error calls for NoClassDefFoundError and
general Exception to aid debugging
- Reword InMemoryMetricReader comment for precision
Improve error handling observability
- Add debug log to FlagEvalMetrics.record() catch block so metric
recording failures are visible in debug logs
- Widen Provider catch from NoClassDefFoundError to LinkageError to
cover IncompatibleClassChangeError and other classloader issues
from incompatible OTel SDK versions
- Add slf4j logger to Provider and log at error level when the
fallback catch fires
Use warn level for Provider fallback catch
The Provider catch is defense-in-depth for when FlagEvalMetrics
class itself can't load (OTel API absent entirely). The detailed
error message is logged inside FlagEvalMetrics when it CAN load
but SDK init fails. Using error level here caused the openfeature
smoke test to fail (it asserts no ERROR entries in application logs).
Remove evaluationLogging option — metrics always enabled
Evaluation metrics are always attempted. If the OTel SDK is absent,
the provider degrades gracefully with a warning. There is no user-
facing toggle to disable metrics — this matches the Go and Python
SDKs which also always attempt metrics.
Co-authored-by: devflow.devflow-routing-intake <devflow.devflow-routing-intake@kubernetes.us1.ddbuild.io>1 parent f064e18 commit 81bb305
8 files changed
Lines changed: 661 additions & 0 deletions
File tree
- products/feature-flagging/feature-flagging-api
- src
- main/java/datadog/trace/api/openfeature
- test/java/datadog/trace/api/openfeature
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
Lines changed: 8 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
47 | 51 | | |
48 | 52 | | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
49 | 56 | | |
50 | 57 | | |
51 | 58 | | |
| 59 | + | |
52 | 60 | | |
53 | 61 | | |
54 | 62 | | |
| |||
Lines changed: 41 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
Lines changed: 157 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
Lines changed: 31 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
| |||
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
| 18 | + | |
| 19 | + | |
17 | 20 | | |
18 | 21 | | |
| 22 | + | |
| 23 | + | |
19 | 24 | | |
20 | 25 | | |
21 | 26 | | |
| 27 | + | |
22 | 28 | | |
23 | 29 | | |
24 | 30 | | |
25 | 31 | | |
26 | 32 | | |
27 | 33 | | |
| 34 | + | |
| 35 | + | |
28 | 36 | | |
29 | 37 | | |
30 | 38 | | |
| |||
37 | 45 | | |
38 | 46 | | |
39 | 47 | | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
40 | 60 | | |
41 | 61 | | |
42 | 62 | | |
| |||
77 | 97 | | |
78 | 98 | | |
79 | 99 | | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
80 | 108 | | |
81 | 109 | | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
82 | 113 | | |
83 | 114 | | |
84 | 115 | | |
| |||
0 commit comments