Skip to content

Commit 301c2da

Browse files
feat(flags): Implement LaunchDarkly Integrations (#4917)
* Implement OpenFeature Integration * use evaluation context * Format code * add missing mentions * api; register as integration and package * changelog * replace hard coded dependency * Add launchdarkly integrations * changelog * issue templates and readme * remove comments from build gradle files * convert to java * remove kotlin from build.gradle * add tests * Format code --------- Co-authored-by: Sentry Github Bot <[email protected]>
1 parent dffdd14 commit 301c2da

File tree

16 files changed

+823
-0
lines changed

16 files changed

+823
-0
lines changed

.github/ISSUE_TEMPLATE/bug_report_android.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ body:
1616
- sentry-apollo
1717
- sentry-apollo-3
1818
- sentry-compose
19+
- sentry-launchdarkly-android
1920
- sentry-okhttp
2021
- other
2122
validations:

.github/ISSUE_TEMPLATE/bug_report_java.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ body:
3636
- sentry-quartz
3737
- sentry-openfeign
3838
- sentry-openfeature
39+
- sentry-launchdarkly-server
3940
- sentry-apache-http-client-5
4041
- sentry-okhttp
4142
- sentry-reactor

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
- Implement OpenFeature Integration that tracks Feature Flag evaluations ([#4910](https://github.com/getsentry/sentry-java/pull/4910))
88
- To make use of it, add the `sentry-openfeature` dependency and register the the hook using: `openFeatureApiInstance.addHooks(new SentryOpenFeatureHook());`
9+
- Implement LaunchDarkly Integrations that track Feature Flag evaluations ([#4917](https://github.com/getsentry/sentry-java/pull/4917))
10+
- For Android, please add `sentry-launchdarkly-android` as a dependency and register the `SentryLaunchDarklyAndroidHook`
11+
- For Server / JVM, please add `sentry-launchdarkly-server` as a dependency and register the `SentryLaunchDarklyServerHook`
912
- Detect oversized events and reduce their size ([#4903](https://github.com/getsentry/sentry-java/pull/4903))
1013
- You can opt into this new behaviour by setting `enableEventSizeLimiting` to `true` (`sentry.enable-event-size-limiting=true` for Spring Boot `application.properties`)
1114
- 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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ Sentry SDK for Java and Android
6060
| sentry-quartz | ![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-quartz?style=for-the-badge&logo=sentry&color=green) |
6161
| sentry-openfeign | ![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-openfeign?style=for-the-badge&logo=sentry&color=green) |
6262
| sentry-openfeature | ![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-openfeature?style=for-the-badge&logo=sentry&color=green) |
63+
| sentry-launchdarkly-android | ![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-launchdarkly-android?style=for-the-badge&logo=sentry&color=green) |
64+
| sentry-launchdarkly-server | ![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-launchdarkly-server?style=for-the-badge&logo=sentry&color=green) |
6365
| sentry-opentelemetry-agent | ![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-opentelemetry-agent?style=for-the-badge&logo=sentry&color=green) |
6466
| sentry-opentelemetry-agentcustomization | ![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-opentelemetry-agentcustomization?style=for-the-badge&logo=sentry&color=green) |
6567
| sentry-opentelemetry-core | ![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-opentelemetry-core?style=for-the-badge&logo=sentry&color=green) |

buildSrc/src/main/java/Config.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ object Config {
7878
val SENTRY_QUARTZ_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.quartz"
7979
val SENTRY_JDBC_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.jdbc"
8080
val SENTRY_OPENFEATURE_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.openfeature"
81+
val SENTRY_LAUNCHDARKLY_SERVER_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.launchdarkly-server"
82+
val SENTRY_LAUNCHDARKLY_ANDROID_SDK_NAME = "$SENTRY_ANDROID_SDK_NAME.launchdarkly"
8183
val SENTRY_SERVLET_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.servlet"
8284
val SENTRY_SERVLET_JAKARTA_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.servlet.jakarta"
8385
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
@@ -117,6 +117,8 @@ kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core",
117117
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
118118
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktorClient" }
119119
ktor-client-java = { module = "io.ktor:ktor-client-java", version.ref = "ktorClient" }
120+
launchdarkly-android = { module = "com.launchdarkly:launchdarkly-android-client-sdk", version = "5.9.2" }
121+
launchdarkly-server = { module = "com.launchdarkly:launchdarkly-java-server-sdk", version = "7.10.2" }
120122
log4j-api = { module = "org.apache.logging.log4j:log4j-api", version.ref = "log4j2" }
121123
log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j2" }
122124
leakcanary = { module = "com.squareup.leakcanary:leakcanary-android", version = "2.14" }
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
public final class io/sentry/launchdarkly/android/BuildConfig {
2+
public static final field BUILD_TYPE Ljava/lang/String;
3+
public static final field DEBUG Z
4+
public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String;
5+
public static final field SENTRY_LAUNCHDARKLY_ANDROID_SDK_NAME Ljava/lang/String;
6+
public static final field VERSION_NAME Ljava/lang/String;
7+
public fun <init> ()V
8+
}
9+
10+
public final class io/sentry/launchdarkly/android/SentryLaunchDarklyAndroidHook : com/launchdarkly/sdk/android/integrations/Hook {
11+
public fun <init> ()V
12+
public fun <init> (Lio/sentry/IScopes;)V
13+
public fun afterEvaluation (Lcom/launchdarkly/sdk/android/integrations/EvaluationSeriesContext;Ljava/util/Map;Lcom/launchdarkly/sdk/EvaluationDetail;)Ljava/util/Map;
14+
}
15+
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
plugins {
2+
id("com.android.library")
3+
alias(libs.plugins.kotlin.android)
4+
jacoco
5+
alias(libs.plugins.jacoco.android)
6+
alias(libs.plugins.gradle.versions)
7+
}
8+
9+
android {
10+
compileSdk = libs.versions.compileSdk.get().toInt()
11+
namespace = "io.sentry.launchdarkly.android"
12+
13+
defaultConfig {
14+
minSdk = libs.versions.minSdk.get().toInt()
15+
16+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
17+
18+
// for AGP 4.1
19+
buildConfigField("String", "VERSION_NAME", "\"${project.version}\"")
20+
buildConfigField(
21+
"String",
22+
"SENTRY_LAUNCHDARKLY_ANDROID_SDK_NAME",
23+
"\"${Config.Sentry.SENTRY_LAUNCHDARKLY_ANDROID_SDK_NAME}\"",
24+
)
25+
}
26+
27+
buildTypes {
28+
getByName("debug") { consumerProguardFiles("proguard-rules.pro") }
29+
getByName("release") { consumerProguardFiles("proguard-rules.pro") }
30+
}
31+
32+
kotlin { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 }
33+
34+
testOptions {
35+
animationsDisabled = true
36+
unitTests.apply {
37+
isReturnDefaultValues = true
38+
isIncludeAndroidResources = true
39+
}
40+
}
41+
42+
lint {
43+
warningsAsErrors = true
44+
checkDependencies = true
45+
46+
// We run a full lint analysis as build part in CI, so skip vital checks for assemble tasks.
47+
checkReleaseBuilds = false
48+
}
49+
50+
buildFeatures { buildConfig = true }
51+
52+
androidComponents.beforeVariants {
53+
it.enable = !Config.Android.shouldSkipDebugVariant(it.buildType)
54+
}
55+
}
56+
57+
dependencies {
58+
api(projects.sentry)
59+
60+
compileOnly(libs.launchdarkly.android)
61+
compileOnly(libs.jetbrains.annotations)
62+
63+
// tests
64+
testImplementation(projects.sentry)
65+
testImplementation(projects.sentryTestSupport)
66+
testImplementation(kotlin(Config.kotlinStdLib, Config.kotlinStdLibVersionAndroid))
67+
testImplementation(libs.androidx.test.ext.junit)
68+
testImplementation(libs.kotlin.test.junit)
69+
testImplementation(libs.mockito.kotlin)
70+
testImplementation(libs.mockito.inline)
71+
testImplementation(libs.launchdarkly.android)
72+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
##---------------Begin: proguard configuration for LaunchDarkly Android ----------
2+
3+
# To ensure that stack traces is unambiguous
4+
# https://developer.android.com/studio/build/shrink-code#decode-stack-trace
5+
-keepattributes LineNumberTable,SourceFile
6+
7+
##---------------End: proguard configuration for LaunchDarkly Android ----------
8+
9+
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package io.sentry.launchdarkly.android;
2+
3+
import com.launchdarkly.sdk.EvaluationDetail;
4+
import com.launchdarkly.sdk.LDValue;
5+
import com.launchdarkly.sdk.LDValueType;
6+
import com.launchdarkly.sdk.android.integrations.EvaluationSeriesContext;
7+
import com.launchdarkly.sdk.android.integrations.Hook;
8+
import io.sentry.IScopes;
9+
import io.sentry.ScopesAdapter;
10+
import io.sentry.SentryIntegrationPackageStorage;
11+
import io.sentry.SentryLevel;
12+
import io.sentry.util.IntegrationUtils;
13+
import java.util.Map;
14+
import java.util.Objects;
15+
import org.jetbrains.annotations.NotNull;
16+
import org.jetbrains.annotations.Nullable;
17+
18+
public final class SentryLaunchDarklyAndroidHook extends Hook {
19+
private final IScopes scopes;
20+
21+
static {
22+
SentryIntegrationPackageStorage.getInstance()
23+
.addPackage("maven:io.sentry:sentry-launchdarkly-android", BuildConfig.VERSION_NAME);
24+
}
25+
26+
public SentryLaunchDarklyAndroidHook() {
27+
this(ScopesAdapter.getInstance());
28+
}
29+
30+
public SentryLaunchDarklyAndroidHook(final @NotNull IScopes scopes) {
31+
super("SentryLaunchDarklyAndroidHook");
32+
this.scopes = Objects.requireNonNull(scopes, "Scopes are required");
33+
addPackageAndIntegrationInfo();
34+
}
35+
36+
private void addPackageAndIntegrationInfo() {
37+
IntegrationUtils.addIntegrationToSdkVersion("LaunchDarkly-Android");
38+
}
39+
40+
@Override
41+
public Map<String, Object> afterEvaluation(
42+
final EvaluationSeriesContext seriesContext,
43+
final Map<String, Object> seriesData,
44+
final EvaluationDetail<LDValue> evaluationDetail) {
45+
if (evaluationDetail == null || seriesContext == null) {
46+
return seriesData;
47+
}
48+
49+
try {
50+
final @Nullable String flagKey = seriesContext.flagKey;
51+
final @Nullable LDValue value = evaluationDetail.getValue();
52+
53+
if (flagKey == null || value == null) {
54+
return seriesData;
55+
}
56+
57+
if (LDValueType.BOOLEAN.equals(value.getType())) {
58+
final boolean flagValue = value.booleanValue();
59+
scopes.addFeatureFlag(flagKey, flagValue);
60+
}
61+
} catch (final Exception e) {
62+
scopes
63+
.getOptions()
64+
.getLogger()
65+
.log(SentryLevel.ERROR, "Failed to capture feature flag evaluation", e);
66+
}
67+
68+
return seriesData;
69+
}
70+
}

0 commit comments

Comments
 (0)