diff --git a/README.md b/README.md
index c710de664..a277600df 100644
--- a/README.md
+++ b/README.md
@@ -120,17 +120,18 @@ See [here](https://javadoc.io/doc/dev.openfeature/sdk/latest/) for the Javadocs.
## 🌟 Features
-| Status | Features | Description |
-| ------ |-----------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| ✅ | [Providers](#providers) | Integrate with a commercial, open source, or in-house feature management tool. |
-| ✅ | [Targeting](#targeting) | Contextually-aware flag evaluation using [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context). |
-| ✅ | [Hooks](#hooks) | Add functionality to various stages of the flag evaluation life-cycle. |
-| ✅ | [Logging](#logging) | Integrate with popular logging packages. |
-| ✅ | [Domains](#domains) | Logically bind clients with providers. |
-| ✅ | [Eventing](#eventing) | React to state changes in the provider or flag management system. |
-| ✅ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. |
-| ✅ | [Transaction Context Propagation](#transaction-context-propagation) | Set a specific [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context) for a transaction (e.g. an HTTP request or a thread). |
-| ✅ | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. |
+| Status | Features | Description |
+| ------ |---------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| ✅ | [Providers](#providers) | Integrate with a commercial, open source, or in-house feature management tool. |
+| ✅ | [Targeting](#targeting) | Contextually-aware flag evaluation using [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context). |
+| ✅ | [Hooks](#hooks) | Add functionality to various stages of the flag evaluation life-cycle. |
+| ✅ | [Tracking](#tracking) | Associate user actions with feature flag evaluations. |
+| ✅ | [Logging](#logging) | Integrate with popular logging packages. |
+| ✅ | [Domains](#domains) | Logically bind clients with providers. |
+| ✅ | [Eventing](#eventing) | React to state changes in the provider or flag management system. |
+| ✅ | [Shutdown](#shutdown) | Gracefully clean up a provider during application shutdown. |
+| ✅ | [Transaction Context Propagation](#transaction-context-propagation) | Set a specific [evaluation context](https://openfeature.dev/docs/reference/concepts/evaluation-context) for a transaction (e.g. an HTTP request or a thread). |
+| ✅ | [Extending](#extending) | Extend OpenFeature with custom providers and hooks. |
Implemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌
@@ -215,6 +216,16 @@ Once you've added a hook as a dependency, it can be registered at the global, cl
FlagEvaluationOptions.builder().hook(new ExampleHook()).build());
```
+### Tracking
+
+The [tracking API](https://openfeature.dev/specification/sections/tracking/) allows you to use OpenFeature abstractions to associate user actions with feature flag evaluations.
+This is essential for robust experimentation powered by feature flags. Note that, unlike methods that handle feature flag evaluations, calling `track(...)` may throw an `IllegalArgumentException` if an empty string is passed as the `trackingEventName`.
+
+```java
+OpenFeatureAPI api = OpenFeatureAPI.getInstance();
+api.getClient().track("visited-promo-page", new MutableTrackingEventDetails(99.77).add("currency", "USD"));
+```
+
### Logging
The Java SDK uses SLF4J. See the [SLF4J manual](https://slf4j.org/manual.html) for complete documentation.
diff --git a/src/main/java/dev/openfeature/sdk/Client.java b/src/main/java/dev/openfeature/sdk/Client.java
index 7b41b9b07..441d31e2b 100644
--- a/src/main/java/dev/openfeature/sdk/Client.java
+++ b/src/main/java/dev/openfeature/sdk/Client.java
@@ -5,17 +5,19 @@
/**
* Interface used to resolve flags of varying types.
*/
-public interface Client extends Features, EventBus {
+public interface Client extends Features, Tracking, EventBus {
ClientMetadata getMetadata();
/**
* Return an optional client-level evaluation context.
+ *
* @return {@link EvaluationContext}
*/
EvaluationContext getEvaluationContext();
/**
* Set the client-level evaluation context.
+ *
* @param ctx Client level context.
*/
Client setEvaluationContext(EvaluationContext ctx);
@@ -30,12 +32,14 @@ public interface Client extends Features, EventBus {
/**
* Fetch the hooks associated to this client.
+ *
* @return A list of {@link Hook}s.
*/
List getHooks();
/**
* Returns the current state of the associated provider.
+ *
* @return the provider state
*/
ProviderState getProviderState();
diff --git a/src/main/java/dev/openfeature/sdk/FeatureProvider.java b/src/main/java/dev/openfeature/sdk/FeatureProvider.java
index f73b6cdfa..706818e85 100644
--- a/src/main/java/dev/openfeature/sdk/FeatureProvider.java
+++ b/src/main/java/dev/openfeature/sdk/FeatureProvider.java
@@ -71,4 +71,14 @@ default ProviderState getState() {
return ProviderState.READY;
}
+ /**
+ * Feature provider implementations can opt in for to support Tracking by implementing this method.
+ *
+ * @param eventName The name of the tracking event
+ * @param context Evaluation context used in flag evaluation (Optional)
+ * @param details Data pertinent to a particular tracking event (Optional)
+ */
+ default void track(String eventName, EvaluationContext context, TrackingEventDetails details) {
+
+ }
}
diff --git a/src/main/java/dev/openfeature/sdk/ImmutableTrackingEventDetails.java b/src/main/java/dev/openfeature/sdk/ImmutableTrackingEventDetails.java
new file mode 100644
index 000000000..b535bb7da
--- /dev/null
+++ b/src/main/java/dev/openfeature/sdk/ImmutableTrackingEventDetails.java
@@ -0,0 +1,53 @@
+package dev.openfeature.sdk;
+
+import dev.openfeature.sdk.internal.ExcludeFromGeneratedCoverageReport;
+import lombok.experimental.Delegate;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+
+
+/**
+ * ImmutableTrackingEventDetails represents data pertinent to a particular tracking event.
+ */
+public class ImmutableTrackingEventDetails implements TrackingEventDetails {
+
+ @Delegate(excludes = DelegateExclusions.class)
+ private final ImmutableStructure structure;
+
+ private final Number value;
+
+ public ImmutableTrackingEventDetails() {
+ this.value = null;
+ this.structure = new ImmutableStructure();
+ }
+
+ public ImmutableTrackingEventDetails(final Number value) {
+ this.value = value;
+ this.structure = new ImmutableStructure();
+ }
+
+ public ImmutableTrackingEventDetails(final Number value, final Map attributes) {
+ this.value = value;
+ this.structure = new ImmutableStructure(attributes);
+ }
+
+ /**
+ * Returns the optional tracking value.
+ */
+ public Optional getValue() {
+ return Optional.ofNullable(value);
+ }
+
+
+ @SuppressWarnings("all")
+ private static class DelegateExclusions {
+ @ExcludeFromGeneratedCoverageReport
+ public Map merge(Function