diff --git a/lib/java-server-sdk-otel/src/main/java/com/launchdarkly/integrations/TracingHook.java b/lib/java-server-sdk-otel/src/main/java/com/launchdarkly/integrations/TracingHook.java index 26e96ec..236dc08 100644 --- a/lib/java-server-sdk-otel/src/main/java/com/launchdarkly/integrations/TracingHook.java +++ b/lib/java-server-sdk-otel/src/main/java/com/launchdarkly/integrations/TracingHook.java @@ -23,24 +23,26 @@ public class TracingHook extends Hook { static final String INSTRUMENTATION_NAME = "launchdarkly-client"; static final String DATA_KEY_SPAN = "variationSpan"; static final String EVENT_NAME = "feature_flag"; - static final String SEMCONV_FEATURE_FLAG_PROVIDER_NAME = "feature_flag.provider_name"; + static final String SEMCONV_FEATURE_FLAG_PROVIDER_NAME = "feature_flag.provider.name"; static final String SEMCONV_FEATURE_FLAG_KEY = "feature_flag.key"; - static final String SEMCONV_FEATURE_FLAG_VARIANT = "feature_flag.variant"; - static final String CUSTOM_CONTEXT_KEY_ATTRIBUTE_NAME = "feature_flag.context.key"; + static final String SEMCONV_FEATURE_FLAG_VALUE = "feature_flag.result.value"; + static final String SEMCONV_FEATURE_FLAG_CONTEXT_ID = "feature_flag.context.id"; + static final String SEMCONV_FEATURE_FLAG_VARIATION_INDEX = "feature_flag.result.variationIndex"; + static final String SEMCONV_FEATURE_FLAG_IN_EXPERIMENT = "feature_flag.result.reason.inExperiment"; private final boolean withSpans; - private final boolean withVariant; + private final boolean withValue; /** * Creates a {@link TracingHook} * * @param withSpans will include child spans for the various hook series when they happen - * @param withVariant will include the variant of the feature flag in the recorded evaluation events + * @param withValue will include the value of the feature flag in the recorded evaluation events */ - TracingHook(boolean withSpans, boolean withVariant) { + TracingHook(boolean withSpans, boolean withValue) { super(HOOK_NAME); this.withSpans = withSpans; - this.withVariant = withVariant; + this.withValue = withValue; } @Override @@ -60,6 +62,7 @@ Map beforeEvaluationInternal(Tracer tracer, EvaluationSeriesCont AttributesBuilder attrBuilder = Attributes.builder(); attrBuilder.put(SEMCONV_FEATURE_FLAG_KEY, seriesContext.flagKey); attrBuilder.put(SEMCONV_FEATURE_FLAG_PROVIDER_NAME, PROVIDER_NAME); + attrBuilder.put(SEMCONV_FEATURE_FLAG_CONTEXT_ID, seriesContext.context.getFullyQualifiedKey()); builder.setAllAttributes(attrBuilder.build()); Span span = builder.startSpan(); Map retSeriesData = new HashMap<>(seriesData); @@ -78,9 +81,17 @@ public Map afterEvaluation(EvaluationSeriesContext seriesContext AttributesBuilder attrBuilder = Attributes.builder(); attrBuilder.put(SEMCONV_FEATURE_FLAG_KEY, seriesContext.flagKey); attrBuilder.put(SEMCONV_FEATURE_FLAG_PROVIDER_NAME, PROVIDER_NAME); - attrBuilder.put(CUSTOM_CONTEXT_KEY_ATTRIBUTE_NAME, seriesContext.context.getFullyQualifiedKey()); - if (withVariant) { - attrBuilder.put(SEMCONV_FEATURE_FLAG_VARIANT, evaluationDetail.getValue().toJsonString()); + attrBuilder.put(SEMCONV_FEATURE_FLAG_CONTEXT_ID, seriesContext.context.getFullyQualifiedKey()); + if (withValue) { + attrBuilder.put(SEMCONV_FEATURE_FLAG_VALUE, evaluationDetail.getValue().toJsonString()); + } + + if (evaluationDetail.getReason().isInExperiment()) { + attrBuilder.put(SEMCONV_FEATURE_FLAG_IN_EXPERIMENT, true); + } + + if (evaluationDetail.getVariationIndex() != EvaluationDetail.NO_VARIATION) { + attrBuilder.put(SEMCONV_FEATURE_FLAG_VARIATION_INDEX, evaluationDetail.getVariationIndex()); } // Here we make best effort the log the event and let the library handle the "no current span" case; which at the @@ -94,7 +105,7 @@ public Map afterEvaluation(EvaluationSeriesContext seriesContext */ public static class Builder { private boolean withSpans = false; - private boolean withVariant = false; + private boolean withValue = false; /** * The {@link TracingHook} will include child spans for the various hook series when they happen @@ -105,12 +116,23 @@ public Builder withSpans() { return this; } + /** + * The {@link TracingHook} will include the value of the feature flag in the recorded evaluation events + * @return the builder + */ + public Builder withValue() { + this.withValue = true; + return this; + } + /** * The {@link TracingHook} will include the variant of the feature flag in the recorded evaluation events * @return the builder + * @deprecated Use {@link #withValue()} instead */ + @Deprecated public Builder withVariant() { - this.withVariant = true; + this.withValue = true; return this; } @@ -118,7 +140,7 @@ public Builder withVariant() { * @return the {@link TracingHook} */ public TracingHook build() { - return new TracingHook(withSpans, withVariant); + return new TracingHook(withSpans, withValue); } } } diff --git a/lib/java-server-sdk-otel/src/test/java/com/launchdarkly/integrations/TracingHookTest.java b/lib/java-server-sdk-otel/src/test/java/com/launchdarkly/integrations/TracingHookTest.java index 49837f9..c5e0839 100644 --- a/lib/java-server-sdk-otel/src/test/java/com/launchdarkly/integrations/TracingHookTest.java +++ b/lib/java-server-sdk-otel/src/test/java/com/launchdarkly/integrations/TracingHookTest.java @@ -20,11 +20,11 @@ import java.util.List; import java.util.Map; -import static com.launchdarkly.integrations.TracingHook.CUSTOM_CONTEXT_KEY_ATTRIBUTE_NAME; +import static com.launchdarkly.integrations.TracingHook.SEMCONV_FEATURE_FLAG_CONTEXT_ID; import static com.launchdarkly.integrations.TracingHook.PROVIDER_NAME; import static com.launchdarkly.integrations.TracingHook.SEMCONV_FEATURE_FLAG_KEY; import static com.launchdarkly.integrations.TracingHook.SEMCONV_FEATURE_FLAG_PROVIDER_NAME; -import static com.launchdarkly.integrations.TracingHook.SEMCONV_FEATURE_FLAG_VARIANT; +import static com.launchdarkly.integrations.TracingHook.SEMCONV_FEATURE_FLAG_VALUE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -59,11 +59,11 @@ public void testAddsEventToParentSpanWtihoutVariation() { assertEquals(1, spanData.getEvents().size()); Attributes attributes = spanData.getEvents().get(0).getAttributes(); - assertEquals(3, attributes.size()); + assertEquals(4, attributes.size()); assertEquals(PROVIDER_NAME, attributes.get(AttributeKey.stringKey(SEMCONV_FEATURE_FLAG_PROVIDER_NAME))); assertEquals("testKey", attributes.get(AttributeKey.stringKey(SEMCONV_FEATURE_FLAG_KEY))); - assertNull(attributes.get(AttributeKey.stringKey(SEMCONV_FEATURE_FLAG_VARIANT))); - assertEquals("testContextKey", attributes.get(AttributeKey.stringKey(CUSTOM_CONTEXT_KEY_ATTRIBUTE_NAME))); + assertNull(attributes.get(AttributeKey.stringKey(SEMCONV_FEATURE_FLAG_VALUE))); + assertEquals("testContextKey", attributes.get(AttributeKey.stringKey(SEMCONV_FEATURE_FLAG_CONTEXT_ID))); } @Test @@ -90,11 +90,11 @@ public void testAddsEventToParentSpanWtihVariation() { assertEquals(1, spanData.getEvents().size()); Attributes attributes = spanData.getEvents().get(0).getAttributes(); - assertEquals(4, attributes.size()); + assertEquals(5, attributes.size()); assertEquals(PROVIDER_NAME, attributes.get(AttributeKey.stringKey(SEMCONV_FEATURE_FLAG_PROVIDER_NAME))); assertEquals("testKey", attributes.get(AttributeKey.stringKey(SEMCONV_FEATURE_FLAG_KEY))); - assertEquals("{\"evalKey\":\"evalValue\"}", attributes.get(AttributeKey.stringKey(SEMCONV_FEATURE_FLAG_VARIANT))); - assertEquals("testContextKey", attributes.get(AttributeKey.stringKey(CUSTOM_CONTEXT_KEY_ATTRIBUTE_NAME))); + assertEquals("{\"evalKey\":\"evalValue\"}", attributes.get(AttributeKey.stringKey(SEMCONV_FEATURE_FLAG_VALUE))); + assertEquals("testContextKey", attributes.get(AttributeKey.stringKey(SEMCONV_FEATURE_FLAG_CONTEXT_ID))); } @Test @@ -118,10 +118,10 @@ public void testCreatesChildSpanEventStillOnParent() { assertEquals("LDClient.testMethod", spanDataList.get(0).getName()); assertEquals("rootSpan", spanDataList.get(1).getName()); Attributes attributes = spanDataList.get(1).getEvents().get(0).getAttributes(); - assertEquals(3, attributes.size()); + assertEquals(4, attributes.size()); assertEquals("testKey", attributes.get(AttributeKey.stringKey(SEMCONV_FEATURE_FLAG_KEY))); assertEquals(PROVIDER_NAME, attributes.get(AttributeKey.stringKey(SEMCONV_FEATURE_FLAG_PROVIDER_NAME))); - assertEquals("testContextKey", attributes.get(AttributeKey.stringKey(CUSTOM_CONTEXT_KEY_ATTRIBUTE_NAME))); + assertEquals("testContextKey", attributes.get(AttributeKey.stringKey(SEMCONV_FEATURE_FLAG_CONTEXT_ID))); } @Test