Skip to content
Open
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
<meta-data android:name="io.sentry.session-replay.screenshot-strategy" android:value="canvas" />
</application>
```
- Add Feature Flags ([#4812](https://github.com/getsentry/sentry-java/pull/4812)) and ([#4831](https://github.com/getsentry/sentry-java/pull/4831))
- You may use top level API (`Sentry.addFeatureFlag`) to add feature flag evaluations to both scope and the current active span
- Or you may directly add feature flag evaluations to any scope, transaction or span

### Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public final class io/sentry/opentelemetry/OtelSpanFactory : io/sentry/ISpanFact

public final class io/sentry/opentelemetry/OtelStrongRefSpanWrapper : io/sentry/opentelemetry/IOtelSpanWrapper {
public fun <init> (Lio/opentelemetry/api/trace/Span;Lio/sentry/opentelemetry/IOtelSpanWrapper;)V
public fun addFeatureFlag (Ljava/lang/String;Ljava/lang/Boolean;)V
public fun finish ()V
public fun finish (Lio/sentry/SpanStatus;)V
public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V
Expand Down Expand Up @@ -96,6 +97,7 @@ public final class io/sentry/opentelemetry/OtelStrongRefSpanWrapper : io/sentry/

public final class io/sentry/opentelemetry/OtelTransactionSpanForwarder : io/sentry/ITransaction {
public fun <init> (Lio/sentry/opentelemetry/IOtelSpanWrapper;)V
public fun addFeatureFlag (Ljava/lang/String;Ljava/lang/Boolean;)V
public fun finish ()V
public fun finish (Lio/sentry/SpanStatus;)V
public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,4 +310,9 @@ public void setContext(@Nullable String key, @Nullable Object context) {
public @Nullable Attributes getOpenTelemetrySpanAttributes() {
return delegate.getOpenTelemetrySpanAttributes();
}

@Override
public void addFeatureFlag(final @Nullable String flag, final @Nullable Boolean result) {
delegate.addFeatureFlag(flag, result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -309,4 +309,9 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource nameSou
}
return name;
}

@Override
public void addFeatureFlag(final @Nullable String flag, final @Nullable Boolean result) {
rootSpan.addFeatureFlag(flag, result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public final class io/sentry/opentelemetry/OtelSpanUtils {

public final class io/sentry/opentelemetry/OtelSpanWrapper : io/sentry/opentelemetry/IOtelSpanWrapper {
public fun <init> (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/IScopes;Lio/sentry/SentryDate;Lio/sentry/TracesSamplingDecision;Lio/sentry/opentelemetry/IOtelSpanWrapper;Lio/sentry/SpanId;Lio/sentry/Baggage;)V
public fun addFeatureFlag (Ljava/lang/String;Ljava/lang/Boolean;)V
public fun finish ()V
public fun finish (Lio/sentry/SpanStatus;)V
public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,11 @@ public Map<String, MeasurementValue> getMeasurements() {
return scopes;
}

@Override
public void addFeatureFlag(final @Nullable String flag, final @Nullable Boolean result) {
context.addFeatureFlag(flag, result);
}

@Override
public @NotNull Context storeInContext(Context context) {
final @Nullable ReadWriteSpan otelSpan = getSpan();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@
import io.sentry.SpanStatus;
import io.sentry.TransactionContext;
import io.sentry.TransactionOptions;
import io.sentry.featureflags.IFeatureFlagBuffer;
import io.sentry.protocol.Contexts;
import io.sentry.protocol.FeatureFlag;
import io.sentry.protocol.FeatureFlags;
import io.sentry.protocol.SentryId;
import io.sentry.protocol.TransactionNameSource;
import java.util.Arrays;
Expand Down Expand Up @@ -260,6 +263,16 @@ private void transferSpanDetails(
targetSpan.setData(entry.getKey(), entry.getValue());
}

final @NotNull SpanContext spanContext = sourceSpan.getSpanContext();
final @NotNull IFeatureFlagBuffer featureFlagBuffer = spanContext.getFeatureFlagBuffer();
final @Nullable FeatureFlags featureFlags = featureFlagBuffer.getFeatureFlags();
if (featureFlags != null) {
for (FeatureFlag featureFlag : featureFlags.getValues()) {
targetSpan.setData(
FeatureFlag.DATA_PREFIX + featureFlag.getFlag(), featureFlag.getResult());
}
}

final @NotNull Map<String, String> tags = sourceSpan.getTags();
for (Map.Entry<String, String> entry : tags.entrySet()) {
targetSpan.setTag(entry.getKey(), entry.getValue());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ public PersonController(PersonService personService, Tracer tracer) {
@GetMapping("{id}")
@WithSpan("personSpanThroughOtelAnnotation")
Person person(@PathVariable Long id) {
Sentry.addFeatureFlag("outer-feature-flag", true);
Span span = tracer.spanBuilder("spanCreatedThroughOtelApi").startSpan();
try (final @NotNull Scope spanScope = span.makeCurrent()) {
Sentry.logger().warn("warn Sentry logging");
Sentry.logger().error("error Sentry logging");
Sentry.logger().info("hello %s %s", "there", "world!");
Sentry.addFeatureFlag("my-feature-flag", true);
Sentry.addFeatureFlag("inner-feature-flag", true);
ISpan currentSpan = Sentry.getSpan();
ISpan sentrySpan = currentSpan.startChild("spanCreatedThroughSentryApi");
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry.systemtest

import io.sentry.protocol.FeatureFlag
import io.sentry.systemtest.util.TestHelper
import kotlin.test.Test
import kotlin.test.assertEquals
Expand All @@ -22,17 +23,31 @@ class PersonSystemTest {

testHelper.ensureErrorReceived { event ->
event.message?.formatted == "Trying person with id=1" &&
testHelper.doesEventHaveFlag(event, "my-feature-flag", true)
testHelper.doesEventHaveFlag(event, "inner-feature-flag", true)
}

testHelper.ensureErrorReceived { event ->
testHelper.doesEventHaveExceptionMessage(event, "Something went wrong [id=1]") &&
testHelper.doesEventHaveFlag(event, "my-feature-flag", true)
testHelper.doesEventHaveFlag(event, "inner-feature-flag", true)
}

testHelper.ensureTransactionReceived { transaction, envelopeHeader ->
testHelper.doesTransactionContainSpanWithOp(transaction, "spanCreatedThroughOtelApi") &&
testHelper.doesTransactionContainSpanWithOp(transaction, "spanCreatedThroughSentryApi")
testHelper.doesTransactionHave(transaction, op = "http.server") &&
testHelper.doesTransactionHaveSpanWith(
transaction,
op = "personSpanThroughOtelAnnotation",
featureFlag = FeatureFlag("flag.evaluation.outer-feature-flag", true),
) &&
testHelper.doesTransactionHaveSpanWith(
transaction,
op = "spanCreatedThroughOtelApi",
featureFlag = FeatureFlag("flag.evaluation.inner-feature-flag", true),
) &&
testHelper.doesTransactionHaveSpanWith(
transaction,
op = "spanCreatedThroughSentryApi",
noFeatureFlags = true,
)
}

Thread.sleep(10000)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public PersonController(PersonService personService, Tracer tracer) {

@GetMapping("{id}")
Person person(@PathVariable Long id) {
Sentry.addFeatureFlag("transaction-feature-flag", true);
Span span = tracer.spanBuilder("spanCreatedThroughOtelApi").startSpan();
try (final @NotNull Scope spanScope = span.makeCurrent()) {
Sentry.logger().warn("warn Sentry logging");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry.systemtest

import io.sentry.protocol.FeatureFlag
import io.sentry.systemtest.util.TestHelper
import kotlin.test.Test
import kotlin.test.assertEquals
Expand Down Expand Up @@ -31,8 +32,17 @@ class PersonSystemTest {
}

testHelper.ensureTransactionReceived { transaction, envelopeHeader ->
testHelper.doesTransactionContainSpanWithOp(transaction, "spanCreatedThroughOtelApi") &&
testHelper.doesTransactionContainSpanWithOp(transaction, "spanCreatedThroughSentryApi")
testHelper.doesTransactionHave(
transaction,
op = "http.server",
featureFlag = FeatureFlag("flag.evaluation.transaction-feature-flag", true),
) &&
testHelper.doesTransactionHaveSpanWith(
transaction,
op = "spanCreatedThroughOtelApi",
featureFlag = FeatureFlag("flag.evaluation.my-feature-flag", true),
) &&
testHelper.doesTransactionHaveSpanWith(transaction, op = "spanCreatedThroughSentryApi")
}

Thread.sleep(10000)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public PersonController(PersonService personService) {

@GetMapping("{id}")
Person person(@PathVariable Long id) {
Sentry.addFeatureFlag("transaction-feature-flag", true);
ISpan currentSpan = Sentry.getSpan();
ISpan sentrySpan = currentSpan.startChild("spanCreatedThroughSentryApi");
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry.systemtest

import io.sentry.protocol.FeatureFlag
import io.sentry.systemtest.util.TestHelper
import kotlin.test.Test
import kotlin.test.assertEquals
Expand Down Expand Up @@ -31,7 +32,16 @@ class PersonSystemTest {
}

testHelper.ensureTransactionReceived { transaction, envelopeHeader ->
testHelper.doesTransactionHaveOp(transaction, "http.server")
testHelper.doesTransactionHave(
transaction,
op = "http.server",
featureFlag = FeatureFlag("flag.evaluation.transaction-feature-flag", true),
) &&
testHelper.doesTransactionHaveSpanWith(
transaction,
op = "spanCreatedThroughSentryApi",
featureFlag = FeatureFlag("flag.evaluation.my-feature-flag", true),
)
}

Thread.sleep(10000)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ public PersonController(PersonService personService, Tracer tracer) {
@GetMapping("{id}")
@WithSpan("personSpanThroughOtelAnnotation")
Person person(@PathVariable Long id) {
Sentry.addFeatureFlag("outer-feature-flag", true);
Span span = tracer.spanBuilder("spanCreatedThroughOtelApi").startSpan();
try (final @NotNull Scope spanScope = span.makeCurrent()) {
Sentry.logger().warn("warn Sentry logging");
Sentry.logger().error("error Sentry logging");
Sentry.logger().info("hello %s %s", "there", "world!");
Sentry.addFeatureFlag("my-feature-flag", true);
Sentry.addFeatureFlag("inner-feature-flag", true);
ISpan currentSpan = Sentry.getSpan();
ISpan sentrySpan = currentSpan.startChild("spanCreatedThroughSentryApi");
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry.systemtest

import io.sentry.protocol.FeatureFlag
import io.sentry.systemtest.util.TestHelper
import kotlin.test.Test
import kotlin.test.assertEquals
Expand All @@ -22,17 +23,31 @@ class PersonSystemTest {

testHelper.ensureErrorReceived { event ->
event.message?.formatted == "Trying person with id=1" &&
testHelper.doesEventHaveFlag(event, "my-feature-flag", true)
testHelper.doesEventHaveFlag(event, "inner-feature-flag", true)
}

testHelper.ensureErrorReceived { event ->
testHelper.doesEventHaveExceptionMessage(event, "Something went wrong [id=1]") &&
testHelper.doesEventHaveFlag(event, "my-feature-flag", true)
testHelper.doesEventHaveFlag(event, "inner-feature-flag", true)
}

testHelper.ensureTransactionReceived { transaction, envelopeHeader ->
testHelper.doesTransactionContainSpanWithOp(transaction, "spanCreatedThroughOtelApi") &&
testHelper.doesTransactionContainSpanWithOp(transaction, "spanCreatedThroughSentryApi")
testHelper.doesTransactionHave(transaction, op = "http.server") &&
testHelper.doesTransactionHaveSpanWith(
transaction,
op = "personSpanThroughOtelAnnotation",
featureFlag = FeatureFlag("flag.evaluation.outer-feature-flag", true),
) &&
testHelper.doesTransactionHaveSpanWith(
transaction,
op = "spanCreatedThroughOtelApi",
featureFlag = FeatureFlag("flag.evaluation.inner-feature-flag", true),
) &&
testHelper.doesTransactionHaveSpanWith(
transaction,
op = "spanCreatedThroughSentryApi",
noFeatureFlags = true,
)
}

Thread.sleep(10000)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public PersonController(PersonService personService, Tracer tracer) {

@GetMapping("{id}")
Person person(@PathVariable Long id) {
Sentry.addFeatureFlag("transaction-feature-flag", true);
Span span = tracer.spanBuilder("spanCreatedThroughOtelApi").startSpan();
try (final @NotNull Scope spanScope = span.makeCurrent()) {
Sentry.logger().warn("warn Sentry logging");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry.systemtest

import io.sentry.protocol.FeatureFlag
import io.sentry.systemtest.util.TestHelper
import kotlin.test.Test
import kotlin.test.assertEquals
Expand Down Expand Up @@ -31,8 +32,17 @@ class PersonSystemTest {
}

testHelper.ensureTransactionReceived { transaction, envelopeHeader ->
testHelper.doesTransactionContainSpanWithOp(transaction, "spanCreatedThroughOtelApi") &&
testHelper.doesTransactionContainSpanWithOp(transaction, "spanCreatedThroughSentryApi")
testHelper.doesTransactionHave(
transaction,
op = "http.server",
featureFlag = FeatureFlag("flag.evaluation.transaction-feature-flag", true),
) &&
testHelper.doesTransactionHaveSpanWith(
transaction,
op = "spanCreatedThroughOtelApi",
featureFlag = FeatureFlag("flag.evaluation.my-feature-flag", true),
) &&
testHelper.doesTransactionHaveSpanWith(transaction, op = "spanCreatedThroughSentryApi")
}

Thread.sleep(10000)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public PersonController(PersonService personService) {

@GetMapping("{id}")
Person person(@PathVariable Long id) {
Sentry.addFeatureFlag("transaction-feature-flag", true);
ISpan currentSpan = Sentry.getSpan();
ISpan sentrySpan = currentSpan.startChild("spanCreatedThroughSentryApi");
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry.systemtest

import io.sentry.protocol.FeatureFlag
import io.sentry.systemtest.util.TestHelper
import kotlin.test.Test
import kotlin.test.assertEquals
Expand Down Expand Up @@ -31,7 +32,16 @@ class PersonSystemTest {
}

testHelper.ensureTransactionReceived { transaction, envelopeHeader ->
testHelper.doesTransactionHaveOp(transaction, "http.server")
testHelper.doesTransactionHave(
transaction,
op = "http.server",
featureFlag = FeatureFlag("flag.evaluation.transaction-feature-flag", true),
) &&
testHelper.doesTransactionHaveSpanWith(
transaction,
op = "spanCreatedThroughSentryApi",
featureFlag = FeatureFlag("flag.evaluation.my-feature-flag", true),
)
}

Thread.sleep(10000)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ public PersonController(PersonService personService, Tracer tracer) {
@GetMapping("{id}")
@WithSpan("personSpanThroughOtelAnnotation")
Person person(@PathVariable Long id) {
Sentry.addFeatureFlag("outer-feature-flag", true);
Span span = tracer.spanBuilder("spanCreatedThroughOtelApi").startSpan();
try (final @NotNull Scope spanScope = span.makeCurrent()) {
Sentry.logger().warn("warn Sentry logging");
Sentry.logger().error("error Sentry logging");
Sentry.logger().info("hello %s %s", "there", "world!");
Sentry.addFeatureFlag("my-feature-flag", true);
Sentry.addFeatureFlag("inner-feature-flag", true);
ISpan currentSpan = Sentry.getSpan();
ISpan sentrySpan = currentSpan.startChild("spanCreatedThroughSentryApi");
try {
Expand Down
Loading
Loading