diff --git a/hooks/openfeature-hooks-opentelemetry/src/openfeature/contrib/hook/opentelemetry/__init__.py b/hooks/openfeature-hooks-opentelemetry/src/openfeature/contrib/hook/opentelemetry/__init__.py index 892e6245..9ee1b05e 100644 --- a/hooks/openfeature-hooks-opentelemetry/src/openfeature/contrib/hook/opentelemetry/__init__.py +++ b/hooks/openfeature-hooks-opentelemetry/src/openfeature/contrib/hook/opentelemetry/__init__.py @@ -21,7 +21,10 @@ class EventAttributes: class TracingHook(Hook): - def after( + def __init__(self, exclude_exceptions: bool = False): + self.exclude_exceptions = exclude_exceptions + + def finally_after( self, hook_context: HookContext, details: FlagEvaluationDetails, @@ -60,5 +63,13 @@ def after( def error( self, hook_context: HookContext, exception: Exception, hints: HookHints ) -> None: + if self.exclude_exceptions: + return + attributes = { + EventAttributes.KEY: hook_context.flag_key, + EventAttributes.RESULT_VALUE: json.dumps(hook_context.default_value), + } + if hook_context.provider_metadata: + attributes[EventAttributes.PROVIDER_NAME] = hook_context.provider_metadata.name current_span = trace.get_current_span() - current_span.record_exception(exception) + current_span.record_exception(exception, attributes) diff --git a/hooks/openfeature-hooks-opentelemetry/tests/test_otel.py b/hooks/openfeature-hooks-opentelemetry/tests/test_otel.py index f8b74385..da2ba996 100644 --- a/hooks/openfeature-hooks-opentelemetry/tests/test_otel.py +++ b/hooks/openfeature-hooks-opentelemetry/tests/test_otel.py @@ -12,12 +12,14 @@ from openfeature.provider.metadata import Metadata + + @pytest.fixture def mock_get_current_span(monkeypatch): monkeypatch.setattr(trace, "get_current_span", Mock()) -def test_after(mock_get_current_span): +def test_finally_after(mock_get_current_span): # Given hook = TracingHook() hook_context = HookContext( @@ -40,7 +42,7 @@ def test_after(mock_get_current_span): trace.get_current_span.return_value = mock_span # When - hook.after(hook_context, details, hints={}) + hook.finally_after(hook_context, details, hints={}) # Then mock_span.add_event.assert_called_once_with( @@ -79,7 +81,7 @@ def test_after_evaluation_error(mock_get_current_span): trace.get_current_span.return_value = mock_span # When - hook.after(hook_context, details, hints={}) + hook.finally_after(hook_context, details, hints={}) # Then mock_span.add_event.assert_called_once_with( @@ -102,8 +104,14 @@ def test_error(mock_get_current_span): flag_type=FlagType.BOOLEAN, default_value=False, evaluation_context=EvaluationContext(), + provider_metadata=Metadata(name="test-provider"), ) exception = Exception() + attributes = { + "feature_flag.key": "flag_key", + "feature_flag.result.value": "false", + "feature_flag.provider.name": "test-provider", + } mock_span = Mock(spec=Span) trace.get_current_span.return_value = mock_span @@ -112,4 +120,47 @@ def test_error(mock_get_current_span): hook.error(hook_context, exception, hints={}) # Then - mock_span.record_exception.assert_called_once_with(exception) + mock_span.record_exception.assert_called_once_with(exception, attributes) + +def test_error_exclude_exceptions(mock_get_current_span): + # Given + hook = TracingHook(exclude_exceptions=True) + hook_context = HookContext( + flag_key="flag_key", + flag_type=FlagType.BOOLEAN, + default_value=False, + evaluation_context=EvaluationContext(), + ) + exception = Exception() + + mock_span = Mock(spec=Span) + trace.get_current_span.return_value = mock_span + + # When + hook.error(hook_context, exception, hints={}) + # Then + mock_span.record_exception.assert_not_called() + + +def test_error_no_provider_metadata(mock_get_current_span): + # Given + hook = TracingHook() + hook_context = HookContext( + flag_key="flag_key", + flag_type=FlagType.BOOLEAN, + default_value=False, + evaluation_context=EvaluationContext(), + ) + exception = Exception() + attributes = { + "feature_flag.key": "flag_key", + "feature_flag.result.value": "false", + } + + mock_span = Mock(spec=Span) + trace.get_current_span.return_value = mock_span + + # When + hook.error(hook_context, exception, hints={}) + # Then + mock_span.record_exception.assert_called_once_with(exception, attributes) \ No newline at end of file