diff --git a/hooks/open-telemetry/README.md b/hooks/open-telemetry/README.md index 86e573733..eff867947 100644 --- a/hooks/open-telemetry/README.md +++ b/hooks/open-telemetry/README.md @@ -36,7 +36,7 @@ Failed evaluations can be allowed to set span status to `ERROR`. You can configu #### Custom dimensions (attributes) You can write your own logic to extract custom dimensions from [flag evaluation metadata](https://github.com/open-feature/spec/blob/main/specification/types.md#flag-metadata) by setting a callback to `dimensionExtractor`. -Extracted dimensions will be added to successful falg evaluation spans. +These extracted dimensions will be added to successful flag evaluation spans. ```java TracesHookOptions options = TracesHookOptions.builder() @@ -50,7 +50,8 @@ Extracted dimensions will be added to successful falg evaluation spans. TracesHook tracesHook = new TracesHook(options); ``` -Alternatively, you can add dimensions at hook construction time using builder option `extraAttributes`, +Alternatively, you can add dimensions at hook construction time using builder option `extraAttributes`. +These extracted dimensions will be added to successful flag evaluation spans as well as flag evaluation error spans. ```java TracesHookOptions options = TracesHookOptions.builder() @@ -143,7 +144,8 @@ MetricHookOptions hookOptions = MetricHookOptions.builder() final MetricsHook metricHook = new MetricsHook(openTelemetry, hookOptions); ``` -Alternatively, you can add dimensions at hook construction time using builder option `extraAttributes`, +Alternatively, you can add dimensions at hook construction time using builder option `extraAttributes`. +Dimensions added through `extraAttributes` option will be included in both flag evaluation success and evaluation error metrics. ```java MetricHookOptions hookOptions = MetricHookOptions.builder() diff --git a/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/MetricsHook.java b/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/MetricsHook.java index 925f60415..54404ea93 100644 --- a/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/MetricsHook.java +++ b/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/MetricsHook.java @@ -123,6 +123,7 @@ public void error(HookContext ctx, Exception error, Map hints) { final AttributesBuilder attributesBuilder = Attributes.builder(); attributesBuilder.put(flagKeyAttributeKey, ctx.getFlagKey()); attributesBuilder.put(providerNameAttributeKey, ctx.getProviderMetadata().getName()); + attributesBuilder.putAll(extraDimensions); evaluationErrorCounter.add(+1, attributesBuilder.build()); } diff --git a/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/TracesHook.java b/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/TracesHook.java index 2c2cc361a..8a5926ebd 100644 --- a/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/TracesHook.java +++ b/hooks/open-telemetry/src/main/java/dev/openfeature/contrib/hooks/otel/TracesHook.java @@ -51,7 +51,8 @@ public TracesHook(TracesHookOptions options) { * @param details Information about how the flag was resolved, including any resolved values. * @param hints An immutable mapping of data for users to communicate to the hooks. */ - @Override public void after(HookContext ctx, FlagEvaluationDetails details, Map hints) { + @Override + public void after(HookContext ctx, FlagEvaluationDetails details, Map hints) { Span currentSpan = Span.current(); if (currentSpan == null) { return; @@ -82,7 +83,8 @@ public TracesHook(TracesHookOptions options) { * @param error The exception that was thrown. * @param hints An immutable mapping of data for users to communicate to the hooks. */ - @Override public void error(HookContext ctx, Exception error, Map hints) { + @Override + public void error(HookContext ctx, Exception error, Map hints) { Span currentSpan = Span.current(); if (currentSpan == null) { return; @@ -92,8 +94,12 @@ public TracesHook(TracesHookOptions options) { currentSpan.setStatus(StatusCode.ERROR); } - Attributes attributes = Attributes.of(flagKeyAttributeKey, ctx.getFlagKey(), providerNameAttributeKey, - ctx.getProviderMetadata().getName()); + Attributes attributes = Attributes.builder() + .put(flagKeyAttributeKey, ctx.getFlagKey()) + .put(providerNameAttributeKey, ctx.getProviderMetadata().getName()) + .putAll(extraAttributes) + .build(); + currentSpan.recordException(error, attributes); } } diff --git a/hooks/open-telemetry/src/test/java/dev/openfeature/contrib/hooks/otel/MetricsHookTest.java b/hooks/open-telemetry/src/test/java/dev/openfeature/contrib/hooks/otel/MetricsHookTest.java index a826fbb50..db3fe3d5e 100644 --- a/hooks/open-telemetry/src/test/java/dev/openfeature/contrib/hooks/otel/MetricsHookTest.java +++ b/hooks/open-telemetry/src/test/java/dev/openfeature/contrib/hooks/otel/MetricsHookTest.java @@ -149,7 +149,13 @@ public void after_stage_validation_of_custom_dimensions(){ @Test public void error_stage_validation() { // given - final MetricsHook metricHook = new MetricsHook(telemetryExtension.getOpenTelemetry()); + final MetricsHook metricHook = + new MetricsHook(telemetryExtension.getOpenTelemetry(), + MetricHookOptions.builder() + .extraAttributes(Attributes.builder() + .put("scope", "app-a") + .build()) + .build()); final Exception exception = new Exception("some_exception"); @@ -172,6 +178,7 @@ public void error_stage_validation() { assertThat(attributes.get(flagKeyAttributeKey)).isEqualTo("key"); assertThat(attributes.get(providerNameAttributeKey)).isEqualTo("UnitTest"); + assertThat(attributes.get(AttributeKey.stringKey("scope"))).isEqualTo("app-a"); } diff --git a/hooks/open-telemetry/src/test/java/dev/openfeature/contrib/hooks/otel/TracesHookTest.java b/hooks/open-telemetry/src/test/java/dev/openfeature/contrib/hooks/otel/TracesHookTest.java index cb4a8ad0e..b8ca237d5 100644 --- a/hooks/open-telemetry/src/test/java/dev/openfeature/contrib/hooks/otel/TracesHookTest.java +++ b/hooks/open-telemetry/src/test/java/dev/openfeature/contrib/hooks/otel/TracesHookTest.java @@ -119,11 +119,20 @@ void should_record_exception_and_status_in_span_during_error_method_execution() RuntimeException runtimeException = new RuntimeException("could not resolve the flag"); mockedSpan.when(Span::current).thenReturn(span); - TracesHook tracesHook = new TracesHook(); + final TracesHook tracesHook = new TracesHook( + TracesHookOptions.builder() + .extraAttributes(Attributes.builder() + .put("scope", "app-a") + .build()) + .build()); + tracesHook.error(hookContext, runtimeException, null); - Attributes expectedAttr = Attributes.of(flagKeyAttributeKey, "test_key", - providerNameAttributeKey, "test provider"); + final Attributes expectedAttr = Attributes.builder() + .put(flagKeyAttributeKey, "test_key") + .put(providerNameAttributeKey, "test provider") + .put("scope", "app-a") + .build(); verify(span).recordException(runtimeException, expectedAttr); verify(span, times(0)).setStatus(any());