Skip to content
Merged
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
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report_android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ body:
- sentry-apollo
- sentry-apollo-3
- sentry-compose
- sentry-launchdarkly-android
- sentry-okhttp
- other
validations:
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report_java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ body:
- sentry-quartz
- sentry-openfeign
- sentry-openfeature
- sentry-launchdarkly-server
- sentry-apache-http-client-5
- sentry-okhttp
- sentry-reactor
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

- Implement OpenFeature Integration that tracks Feature Flag evaluations ([#4910](https://github.com/getsentry/sentry-java/pull/4910))
- To make use of it, add the `sentry-openfeature` dependency and register the the hook using: `openFeatureApiInstance.addHooks(new SentryOpenFeatureHook());`
- Implement LaunchDarkly Integrations that track Feature Flag evaluations ([#4917](https://github.com/getsentry/sentry-java/pull/4917))
- For Android, please add `sentry-launchdarkly-android` as a dependency and register the `SentryLaunchDarklyAndroidHook`
- For Server / JVM, please add `sentry-launchdarkly-server` as a dependency and register the `SentryLaunchDarklyServerHook`
- Detect oversized events and reduce their size ([#4903](https://github.com/getsentry/sentry-java/pull/4903))
- You can opt into this new behaviour by setting `enableEventSizeLimiting` to `true` (`sentry.enable-event-size-limiting=true` for Spring Boot `application.properties`)
- You may optionally register an `onOversizedEvent` callback to implement custom logic that is executed in case an oversized event is detected
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ Sentry SDK for Java and Android
| sentry-quartz | ![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-quartz?style=for-the-badge&logo=sentry&color=green) |
| sentry-openfeign | ![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-openfeign?style=for-the-badge&logo=sentry&color=green) |
| sentry-openfeature | ![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-openfeature?style=for-the-badge&logo=sentry&color=green) |
| 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) |
| 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) |
| 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) |
| 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) |
| 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) |
Expand Down
2 changes: 2 additions & 0 deletions buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ object Config {
val SENTRY_QUARTZ_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.quartz"
val SENTRY_JDBC_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.jdbc"
val SENTRY_OPENFEATURE_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.openfeature"
val SENTRY_LAUNCHDARKLY_SERVER_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.launchdarkly-server"
val SENTRY_LAUNCHDARKLY_ANDROID_SDK_NAME = "$SENTRY_ANDROID_SDK_NAME.launchdarkly"
val SENTRY_SERVLET_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.servlet"
val SENTRY_SERVLET_JAKARTA_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.servlet.jakarta"
val SENTRY_COMPOSE_HELPER_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.compose.helper"
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core",
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktorClient" }
ktor-client-java = { module = "io.ktor:ktor-client-java", version.ref = "ktorClient" }
launchdarkly-android = { module = "com.launchdarkly:launchdarkly-android-client-sdk", version = "5.9.2" }
launchdarkly-server = { module = "com.launchdarkly:launchdarkly-java-server-sdk", version = "7.10.2" }
log4j-api = { module = "org.apache.logging.log4j:log4j-api", version.ref = "log4j2" }
log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j2" }
leakcanary = { module = "com.squareup.leakcanary:leakcanary-android", version = "2.14" }
Expand Down
15 changes: 15 additions & 0 deletions sentry-launchdarkly-android/api/sentry-launchdarkly-android.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
public final class io/sentry/launchdarkly/android/BuildConfig {
public static final field BUILD_TYPE Ljava/lang/String;
public static final field DEBUG Z
public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String;
public static final field SENTRY_LAUNCHDARKLY_ANDROID_SDK_NAME Ljava/lang/String;
public static final field VERSION_NAME Ljava/lang/String;
public fun <init> ()V
}

public final class io/sentry/launchdarkly/android/SentryLaunchDarklyAndroidHook : com/launchdarkly/sdk/android/integrations/Hook {
public fun <init> ()V
public fun <init> (Lio/sentry/IScopes;)V
public fun afterEvaluation (Lcom/launchdarkly/sdk/android/integrations/EvaluationSeriesContext;Ljava/util/Map;Lcom/launchdarkly/sdk/EvaluationDetail;)Ljava/util/Map;
}

72 changes: 72 additions & 0 deletions sentry-launchdarkly-android/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
plugins {
id("com.android.library")
alias(libs.plugins.kotlin.android)
jacoco
alias(libs.plugins.jacoco.android)
alias(libs.plugins.gradle.versions)
}

android {
compileSdk = libs.versions.compileSdk.get().toInt()
namespace = "io.sentry.launchdarkly.android"

defaultConfig {
minSdk = libs.versions.minSdk.get().toInt()

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

// for AGP 4.1
buildConfigField("String", "VERSION_NAME", "\"${project.version}\"")
buildConfigField(
"String",
"SENTRY_LAUNCHDARKLY_ANDROID_SDK_NAME",
"\"${Config.Sentry.SENTRY_LAUNCHDARKLY_ANDROID_SDK_NAME}\"",
)
}

buildTypes {
getByName("debug") { consumerProguardFiles("proguard-rules.pro") }
getByName("release") { consumerProguardFiles("proguard-rules.pro") }
}

kotlin { compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 }

testOptions {
animationsDisabled = true
unitTests.apply {
isReturnDefaultValues = true
isIncludeAndroidResources = true
}
}

lint {
warningsAsErrors = true
checkDependencies = true

// We run a full lint analysis as build part in CI, so skip vital checks for assemble tasks.
checkReleaseBuilds = false
}

buildFeatures { buildConfig = true }

androidComponents.beforeVariants {
it.enable = !Config.Android.shouldSkipDebugVariant(it.buildType)
}
}

dependencies {
api(projects.sentry)

compileOnly(libs.launchdarkly.android)
compileOnly(libs.jetbrains.annotations)

// tests
testImplementation(projects.sentry)
testImplementation(projects.sentryTestSupport)
testImplementation(kotlin(Config.kotlinStdLib, Config.kotlinStdLibVersionAndroid))
testImplementation(libs.androidx.test.ext.junit)
testImplementation(libs.kotlin.test.junit)
testImplementation(libs.mockito.kotlin)
testImplementation(libs.mockito.inline)
testImplementation(libs.launchdarkly.android)
}
9 changes: 9 additions & 0 deletions sentry-launchdarkly-android/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
##---------------Begin: proguard configuration for LaunchDarkly Android ----------

# To ensure that stack traces is unambiguous
# https://developer.android.com/studio/build/shrink-code#decode-stack-trace
-keepattributes LineNumberTable,SourceFile

##---------------End: proguard configuration for LaunchDarkly Android ----------


Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package io.sentry.launchdarkly.android;

import com.launchdarkly.sdk.EvaluationDetail;
import com.launchdarkly.sdk.LDValue;
import com.launchdarkly.sdk.LDValueType;
import com.launchdarkly.sdk.android.integrations.EvaluationSeriesContext;
import com.launchdarkly.sdk.android.integrations.Hook;
import io.sentry.IScopes;
import io.sentry.ScopesAdapter;
import io.sentry.SentryIntegrationPackageStorage;
import io.sentry.SentryLevel;
import io.sentry.util.IntegrationUtils;
import java.util.Map;
import java.util.Objects;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public final class SentryLaunchDarklyAndroidHook extends Hook {
private final IScopes scopes;

static {
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-launchdarkly-android", BuildConfig.VERSION_NAME);
}

public SentryLaunchDarklyAndroidHook() {
this(ScopesAdapter.getInstance());
}

public SentryLaunchDarklyAndroidHook(final @NotNull IScopes scopes) {
super("SentryLaunchDarklyAndroidHook");
this.scopes = Objects.requireNonNull(scopes, "Scopes are required");
addPackageAndIntegrationInfo();
}

private void addPackageAndIntegrationInfo() {
IntegrationUtils.addIntegrationToSdkVersion("LaunchDarkly-Android");
}

@Override
public Map<String, Object> afterEvaluation(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a public method anyway, we could write a test which mimics the afterEvaluation call, and verifies a mocked scope .addFeatureFlag() call is being made. WDYT?

final EvaluationSeriesContext seriesContext,
final Map<String, Object> seriesData,
final EvaluationDetail<LDValue> evaluationDetail) {
if (evaluationDetail == null || seriesContext == null) {
return seriesData;
}

try {
final @Nullable String flagKey = seriesContext.flagKey;
final @Nullable LDValue value = evaluationDetail.getValue();

if (flagKey == null || value == null) {
return seriesData;
}

if (LDValueType.BOOLEAN.equals(value.getType())) {
final boolean flagValue = value.booleanValue();
scopes.addFeatureFlag(flagKey, flagValue);
}
} catch (final Exception e) {
scopes
.getOptions()
.getLogger()
.log(SentryLevel.ERROR, "Failed to capture feature flag evaluation", e);
}

return seriesData;
}
}
Loading
Loading