Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,23 +1,69 @@
package com.posthog.java.sample;

import com.posthog.server.PostHog;
import com.posthog.server.PostHogCaptureOptions;
import com.posthog.server.PostHogConfig;
import com.posthog.server.PostHog;
import com.posthog.server.PostHogFeatureFlagOptions;
import com.posthog.server.PostHogInterface;

import com.posthog.server.PostHogSendFeatureFlagOptions;
import java.util.HashMap;
import java.util.Map;

/**
* Simple Java 1.8 example demonstrating PostHog usage
*/
public class PostHogJavaExample {
private static PostHogInterface postHog;

public static void main(String[] args) {
PostHogConfig config = PostHogConfig
.builder("phc_wz4KZkikEluCCdfY2B2h7MXYygNGdTqFgjbU7I1ZdVR")
.builder("phc_qYXiHw5odMiVWF7Dwh2sHWS7Hj6FsutBNp2SEaMqS0A")
.personalApiKey("phx_example")
.host("http://localhost:8010")
.localEvaluation(true)
.debug(true)
.onFeatureFlags(() -> {
if (postHog.isFeatureEnabled("distinct-id", "beta-feature", false)) {
System.out.println("The feature is enabled.");
}

Object flagValue = postHog.getFeatureFlag("distinct-id", "multi-variate-flag", "default");
String flagVariate = flagValue instanceof String ? (String) flagValue : "default";
Object flagPayload = postHog.getFeatureFlagPayload("distinct-id", "multi-variate-flag");

System.out.println("The flag variant was: " + flagVariate);
System.out.println("Received flag payload: " + flagPayload);

Boolean hasFilePreview = postHog.isFeatureEnabled(
"distinct-id",
"file-previews",
PostHogFeatureFlagOptions
.builder()
.defaultValue(false)
.personProperty("email", "[email protected]")
.build());

postHog.capture(
"distinct-id",
"file_uploaded",
PostHogCaptureOptions
.builder()
.property("file_name", "document.pdf")
.property("file_size", 123456)
.sendFeatureFlags(PostHogSendFeatureFlagOptions
.builder()
.personProperty("email", "[email protected]")
.onlyEvaluateLocally(true)
.build())
.build());

System.out.println("File previews enabled: " + hasFilePreview);

postHog.flush();
postHog.close();
})
.build();

PostHogInterface postHog = PostHog.with(config);
postHog = PostHog.with(config);

postHog.group("distinct-id", "company", "some-company-id");
postHog.capture(
Expand All @@ -36,24 +82,9 @@ public static void main(String[] args) {
// AVOID - Anonymous inner class holds reference to outer class.
// The following won't serialize properly.
// postHog.identify("user-123", new HashMap<String, Object>() {{
// put("key", "value");
// put("key", "value");
// }});

postHog.alias("distinct-id", "alias-id");


if (postHog.isFeatureEnabled("distinct-id", "beta-feature", false)) {
System.out.println("The feature is enabled.");
}

Object flagValue = postHog.getFeatureFlag("distinct-id", "multi-variate-flag", "default");
String flagVariate = flagValue instanceof String ? (String) flagValue : "default";
Object flagPayload = postHog.getFeatureFlagPayload("distinct-id", "multi-variate-flag");

System.out.println("The flag variant was: " + flagVariate);
System.out.println("Received flag payload: " + flagPayload);

postHog.flush();
postHog.close();
}
}
2 changes: 2 additions & 0 deletions posthog-server/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Next

- feat: Add local evaluation for feature flags ([#299](https://github.com/PostHog/posthog-android/issues/299))

## 1.1.0 - 2025-10-03

- feat: `timestamp` can now be overridden when capturing an event ([#297](https://github.com/PostHog/posthog-android/issues/297))
Expand Down
55 changes: 55 additions & 0 deletions posthog-server/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ PostHogConfig config = PostHogConfig.builder("phc_your_api_key_here")
- `flushIntervalSeconds`: Interval between automatic flushes (default: `30`)
- `featureFlagCacheSize`: The maximum number of feature flags results to cache (default: `1000`)
- `featureFlagCacheMaxAgeMs`: The maximum age of a feature flag cache record in memory in milliseconds (default: `300000` or five minutes)
- `localEvaluation`: Enable local evaluation of feature flags (default: `false`)
- `personalApiKey`: Personal API key required for local evaluation (default: `null`)
- `pollIntervalSeconds`: Interval for polling flag definitions for local evaluation (default: `30`)

## Capturing Events

Expand Down Expand Up @@ -202,6 +205,58 @@ postHog.identify("user123", userProperties, userPropertiesSetOnce);

## Feature Flags

### Local Evaluation (Experimental)

Local evaluation allows the SDK to evaluate feature flags locally without making API calls for each flag check. This reduces latency and API costs.

**How it works:**

1. The SDK periodically polls for flag definitions from PostHog (every 30 seconds by default)
2. Flags are evaluated locally using cached definitions and properties provided by the caller
3. If evaluation is inconclusive (missing properties, etc.), the SDK falls back to the API

**Requirements:**

- A feature flags secure API key _or_ a personal API key
- A feature flags secure API key can be obtained via PostHog → Settings → Project → Feature Flags → Feature Flags Secure API key
- A personal API key can be generated via PostHog → Settings → Account → Personal API Keys
- The `localEvaluation` config option set to `true`

#### Kotlin

```kotlin
val config = PostHogConfig(
apiKey = "phc_your_api_key_here",
host = "https://your-posthog-instance.com",
localEvaluation = true,
personalApiKey = "phx_your_personal_api_key_here",
pollIntervalSeconds = 30 // Optional: customize polling interval
)
```

#### Java

```java
PostHogConfig config = PostHogConfig.builder("phc_your_api_key_here")
.host("https://your-posthog-instance.com")
.localEvaluation(true)
.personalApiKey("phx_your_personal_api_key_here")
.pollIntervalSeconds(30) // Optional: customize polling interval
.build();
```

**Benefits:**

- **Reduced latency**: No API call needed for most flag evaluations
- **Lower costs**: Fewer API requests in most cases
- **Offline support**: Flags continue to work with cached definitions

**Limitations:**

- Requires person/group properties to be provided with each call
- Falls back to API for cohort-based flags without local cohort data
- May not reflect real-time flag changes (respects polling interval)

### Check if Feature is Enabled

#### Kotlin
Expand Down
14 changes: 12 additions & 2 deletions posthog-server/api/posthog-server.api
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,11 @@ public class com/posthog/server/PostHogConfig {
public static final field DEFAULT_HOST Ljava/lang/String;
public static final field DEFAULT_MAX_BATCH_SIZE I
public static final field DEFAULT_MAX_QUEUE_SIZE I
public static final field DEFAULT_POLL_INTERVAL_SECONDS I
public static final field DEFAULT_US_ASSETS_HOST Ljava/lang/String;
public static final field DEFAULT_US_HOST Ljava/lang/String;
public fun <init> (Ljava/lang/String;Ljava/lang/String;ZZZZIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;Ljava/net/Proxy;III)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;ZZZZIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;Ljava/net/Proxy;IIIILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;ZZZZIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;Ljava/net/Proxy;IIIZLjava/lang/String;I)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;ZZZZIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;Ljava/net/Proxy;IIIZLjava/lang/String;IILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun addBeforeSend (Lcom/posthog/PostHogBeforeSend;)V
public final fun addIntegration (Lcom/posthog/PostHogIntegration;)V
public static final fun builder (Ljava/lang/String;)Lcom/posthog/server/PostHogConfig$Builder;
Expand All @@ -102,9 +103,12 @@ public class com/posthog/server/PostHogConfig {
public final fun getFlushAt ()I
public final fun getFlushIntervalSeconds ()I
public final fun getHost ()Ljava/lang/String;
public final fun getLocalEvaluation ()Z
public final fun getMaxBatchSize ()I
public final fun getMaxQueueSize ()I
public final fun getOnFeatureFlags ()Lcom/posthog/PostHogOnFeatureFlags;
public final fun getPersonalApiKey ()Ljava/lang/String;
public final fun getPollIntervalSeconds ()I
public final fun getPreloadFeatureFlags ()Z
public final fun getProxy ()Ljava/net/Proxy;
public final fun getRemoteConfig ()Z
Expand All @@ -117,9 +121,12 @@ public class com/posthog/server/PostHogConfig {
public final fun setFeatureFlagCalledCacheSize (I)V
public final fun setFlushAt (I)V
public final fun setFlushIntervalSeconds (I)V
public final fun setLocalEvaluation (Z)V
public final fun setMaxBatchSize (I)V
public final fun setMaxQueueSize (I)V
public final fun setOnFeatureFlags (Lcom/posthog/PostHogOnFeatureFlags;)V
public final fun setPersonalApiKey (Ljava/lang/String;)V
public final fun setPollIntervalSeconds (I)V
public final fun setPreloadFeatureFlags (Z)V
public final fun setProxy (Ljava/net/Proxy;)V
public final fun setRemoteConfig (Z)V
Expand All @@ -137,9 +144,12 @@ public final class com/posthog/server/PostHogConfig$Builder {
public final fun flushAt (I)Lcom/posthog/server/PostHogConfig$Builder;
public final fun flushIntervalSeconds (I)Lcom/posthog/server/PostHogConfig$Builder;
public final fun host (Ljava/lang/String;)Lcom/posthog/server/PostHogConfig$Builder;
public final fun localEvaluation (Z)Lcom/posthog/server/PostHogConfig$Builder;
public final fun maxBatchSize (I)Lcom/posthog/server/PostHogConfig$Builder;
public final fun maxQueueSize (I)Lcom/posthog/server/PostHogConfig$Builder;
public final fun onFeatureFlags (Lcom/posthog/PostHogOnFeatureFlags;)Lcom/posthog/server/PostHogConfig$Builder;
public final fun personalApiKey (Ljava/lang/String;)Lcom/posthog/server/PostHogConfig$Builder;
public final fun pollIntervalSeconds (I)Lcom/posthog/server/PostHogConfig$Builder;
public final fun preloadFeatureFlags (Z)Lcom/posthog/server/PostHogConfig$Builder;
public final fun proxy (Ljava/net/Proxy;)Lcom/posthog/server/PostHogConfig$Builder;
public final fun remoteConfig (Z)Lcom/posthog/server/PostHogConfig$Builder;
Expand Down
Loading
Loading