Skip to content

Commit 23b6ef9

Browse files
feat(flags): Implement OpenFeature Integration (#4910)
* Implement OpenFeature Integration * use evaluation context * Format code * add missing mentions * api; register as integration and package * changelog * replace hard coded dependency --------- Co-authored-by: Sentry Github Bot <[email protected]>
1 parent 1b841a7 commit 23b6ef9

File tree

16 files changed

+661
-46
lines changed

16 files changed

+661
-46
lines changed

.craft.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ targets:
4141
maven:io.sentry:sentry-android-fragment:
4242
maven:io.sentry:sentry-bom:
4343
maven:io.sentry:sentry-openfeign:
44+
#maven:io.sentry:sentry-openfeature:
4445
maven:io.sentry:sentry-opentelemetry-agent:
4546
maven:io.sentry:sentry-opentelemetry-agentcustomization:
4647
maven:io.sentry:sentry-opentelemetry-agentless:

.github/ISSUE_TEMPLATE/bug_report_java.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ body:
3535
- sentry-graphql-22
3636
- sentry-quartz
3737
- sentry-openfeign
38+
- sentry-openfeature
3839
- sentry-apache-http-client-5
3940
- sentry-okhttp
4041
- sentry-reactor

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
### Features
66

7+
- Implement OpenFeature Integration that tracks Feature Flag evaluations ([#4910](https://github.com/getsentry/sentry-java/pull/4910))
8+
- To make use of it, add the `sentry-openfeature` dependency and register the the hook using: `openFeatureApiInstance.addHooks(new SentryOpenFeatureHook());`
79
- Detect oversized events and reduce their size ([#4903](https://github.com/getsentry/sentry-java/pull/4903))
810
- You can opt into this new behaviour by setting `enableEventSizeLimiting` to `true` (`sentry.enable-event-size-limiting=true` for Spring Boot `application.properties`)
911
- You may optionally register an `onOversizedEvent` callback to implement custom logic that is executed in case an oversized event is detected

README.md

Lines changed: 47 additions & 46 deletions
Large diffs are not rendered by default.

buildSrc/src/main/java/Config.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ object Config {
7777
val SENTRY_GRAPHQL22_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.graphql22"
7878
val SENTRY_QUARTZ_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.quartz"
7979
val SENTRY_JDBC_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.jdbc"
80+
val SENTRY_OPENFEATURE_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.openfeature"
8081
val SENTRY_SERVLET_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.servlet"
8182
val SENTRY_SERVLET_JAKARTA_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.servlet.jakarta"
8283
val SENTRY_COMPOSE_HELPER_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.compose.helper"

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ minSdk = "21"
4040
spotless = "7.0.4"
4141
gummyBears = "0.12.0"
4242
camerax = "1.3.0"
43+
openfeature = "1.18.2"
4344

4445
[plugins]
4546
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
@@ -124,6 +125,7 @@ nopen-annotations = { module = "com.jakewharton.nopen:nopen-annotations", versio
124125
nopen-checker = { module = "com.jakewharton.nopen:nopen-checker", version.ref = "nopen" }
125126
nullaway = { module = "com.uber.nullaway:nullaway", version = "0.9.5" }
126127
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
128+
openfeature = { module = "dev.openfeature:sdk", version.ref = "openfeature" }
127129
otel = { module = "io.opentelemetry:opentelemetry-sdk", version.ref = "otel" }
128130
otel-extension-autoconfigure = { module = "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure", version.ref = "otel" }
129131
otel-extension-autoconfigure-spi = { module = "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi", version.ref = "otel" }
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
public final class io/sentry/openfeature/BuildConfig {
2+
public static final field SENTRY_OPENFEATURE_SDK_NAME Ljava/lang/String;
3+
public static final field VERSION_NAME Ljava/lang/String;
4+
}
5+
6+
public final class io/sentry/openfeature/SentryOpenFeatureHook : dev/openfeature/sdk/BooleanHook {
7+
public fun <init> ()V
8+
public fun after (Ldev/openfeature/sdk/HookContext;Ldev/openfeature/sdk/FlagEvaluationDetails;Ljava/util/Map;)V
9+
}
10+
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import net.ltgt.gradle.errorprone.errorprone
2+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
3+
4+
plugins {
5+
`java-library`
6+
id("io.sentry.javadoc")
7+
alias(libs.plugins.kotlin.jvm)
8+
jacoco
9+
alias(libs.plugins.errorprone)
10+
alias(libs.plugins.gradle.versions)
11+
alias(libs.plugins.buildconfig)
12+
}
13+
14+
tasks.withType<KotlinCompile>().configureEach {
15+
compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8
16+
compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
17+
compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
18+
}
19+
20+
dependencies {
21+
api(projects.sentry)
22+
23+
compileOnly(libs.openfeature)
24+
25+
compileOnly(libs.jetbrains.annotations)
26+
compileOnly(libs.nopen.annotations)
27+
errorprone(libs.errorprone.core)
28+
errorprone(libs.nopen.checker)
29+
errorprone(libs.nullaway)
30+
31+
// tests
32+
testImplementation(projects.sentry)
33+
testImplementation(projects.sentryTestSupport)
34+
testImplementation(kotlin(Config.kotlinStdLib))
35+
testImplementation(libs.kotlin.test.junit)
36+
testImplementation(libs.mockito.kotlin)
37+
testImplementation(libs.mockito.inline)
38+
testImplementation(libs.openfeature)
39+
}
40+
41+
configure<SourceSetContainer> { test { java.srcDir("src/test/java") } }
42+
43+
jacoco { toolVersion = libs.versions.jacoco.get() }
44+
45+
tasks.jacocoTestReport {
46+
reports {
47+
xml.required.set(true)
48+
html.required.set(false)
49+
}
50+
}
51+
52+
tasks {
53+
jacocoTestCoverageVerification {
54+
violationRules { rule { limit { minimum = Config.QualityPlugins.Jacoco.minimumCoverage } } }
55+
}
56+
check {
57+
dependsOn(jacocoTestCoverageVerification)
58+
dependsOn(jacocoTestReport)
59+
}
60+
}
61+
62+
tasks.withType<JavaCompile>().configureEach {
63+
options.errorprone {
64+
check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR)
65+
option("NullAway:AnnotatedPackages", "io.sentry")
66+
}
67+
}
68+
69+
buildConfig {
70+
useJavaOutput()
71+
packageName("io.sentry.openfeature")
72+
buildConfigField(
73+
"String",
74+
"SENTRY_OPENFEATURE_SDK_NAME",
75+
"\"${Config.Sentry.SENTRY_OPENFEATURE_SDK_NAME}\"",
76+
)
77+
buildConfigField("String", "VERSION_NAME", "\"${project.version}\"")
78+
}
79+
80+
tasks.jar {
81+
manifest {
82+
attributes(
83+
"Sentry-Version-Name" to project.version,
84+
"Sentry-SDK-Name" to Config.Sentry.SENTRY_OPENFEATURE_SDK_NAME,
85+
"Sentry-SDK-Package-Name" to "maven:io.sentry:sentry-openfeature",
86+
"Implementation-Vendor" to "Sentry",
87+
"Implementation-Title" to project.name,
88+
"Implementation-Version" to project.version,
89+
)
90+
}
91+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package io.sentry.openfeature;
2+
3+
import static io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion;
4+
5+
import dev.openfeature.sdk.BooleanHook;
6+
import dev.openfeature.sdk.FlagEvaluationDetails;
7+
import dev.openfeature.sdk.FlagValueType;
8+
import dev.openfeature.sdk.HookContext;
9+
import io.sentry.IScopes;
10+
import io.sentry.ScopesAdapter;
11+
import io.sentry.SentryIntegrationPackageStorage;
12+
import io.sentry.SentryLevel;
13+
import java.util.Map;
14+
import java.util.Objects;
15+
import org.jetbrains.annotations.NotNull;
16+
import org.jetbrains.annotations.Nullable;
17+
import org.jetbrains.annotations.VisibleForTesting;
18+
19+
public final class SentryOpenFeatureHook implements BooleanHook {
20+
private final IScopes scopes;
21+
22+
static {
23+
SentryIntegrationPackageStorage.getInstance()
24+
.addPackage("maven:io.sentry:sentry-openfeature", BuildConfig.VERSION_NAME);
25+
}
26+
27+
public SentryOpenFeatureHook() {
28+
this(ScopesAdapter.getInstance());
29+
addPackageAndIntegrationInfo();
30+
}
31+
32+
private void addPackageAndIntegrationInfo() {
33+
addIntegrationToSdkVersion("OpenFeature");
34+
}
35+
36+
@VisibleForTesting
37+
SentryOpenFeatureHook(@NotNull IScopes scopes) {
38+
this.scopes = Objects.requireNonNull(scopes, "Scopes are required");
39+
}
40+
41+
@Override
42+
public void after(
43+
final @Nullable HookContext<Boolean> context,
44+
final @Nullable FlagEvaluationDetails<Boolean> details,
45+
final @Nullable Map<String, Object> hints) {
46+
if (context == null || details == null) {
47+
return;
48+
}
49+
try {
50+
final @Nullable String flagKey = details.getFlagKey();
51+
final @Nullable FlagValueType type = context.getType();
52+
final @Nullable Object value = details.getValue();
53+
54+
if (flagKey == null || type == null || value == null) {
55+
return;
56+
}
57+
58+
if (!FlagValueType.BOOLEAN.equals(type)) {
59+
return;
60+
}
61+
62+
if (!(value instanceof Boolean)) {
63+
return;
64+
}
65+
final @NotNull Boolean flagValue = (Boolean) value;
66+
67+
scopes.addFeatureFlag(flagKey, flagValue);
68+
} catch (Exception e) {
69+
scopes
70+
.getOptions()
71+
.getLogger()
72+
.log(SentryLevel.ERROR, "Failed to capture feature flag evaluation", e);
73+
}
74+
}
75+
}

sentry-samples/sentry-samples-spring-boot-jakarta/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ dependencies {
5353
implementation(projects.sentryGraphql22)
5454
implementation(projects.sentryQuartz)
5555
implementation(projects.sentryAsyncProfiler)
56+
implementation(projects.sentryOpenfeature)
57+
58+
// OpenFeature SDK
59+
implementation(libs.openfeature)
5660

5761
// database query tracing
5862
implementation(projects.sentryJdbc)

0 commit comments

Comments
 (0)