diff --git a/CHANGELOG.md b/CHANGELOG.md index c5ce56423a..3dd53582ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,23 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -## Added +### Added - `opentelemetry-instrumentation-kafka-python` Instrument temporary fork, kafka-python-ng inside kafka-python's instrumentation ([#2537](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2537)) -## Breaking changes +### Breaking changes - `opentelemetry-bootstrap` Remove `opentelemetry-instrumentation-aws-lambda` from the defaults instrumentations ([#2786](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2786)) -## Fixed +### Fixed +- `opentelemetry-instrumentation-httpx` fix handling of async hooks + ([#2823](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2823)) - `opentelemetry-instrumentation-system-metrics` fix `process.runtime.cpu.utilization` values to be shown in range of 0 to 1 - ([2812](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2812)) + ([#2812](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2812)) - `opentelemetry-instrumentation-fastapi` fix `fastapi` auto-instrumentation by removing `fastapi-slim` support, `fastapi-slim` itself is discontinued from maintainers - ([2783](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2783)) + ([#2783](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2783)) - `opentelemetry-instrumentation-aws-lambda` Avoid exception when a handler is not present. ([#2750](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2750)) - `opentelemetry-instrumentation-django` Fix regression - `http.target` re-added back to old semconv duration metrics diff --git a/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py b/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py index 43db6c3a82..15ee59a183 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py @@ -192,6 +192,7 @@ async def async_response_hook(span, request, response): """ import logging import typing +from asyncio import iscoroutinefunction from types import TracebackType import httpx @@ -731,15 +732,19 @@ def _instrument(self, **kwargs): self._original_async_client = httpx.AsyncClient request_hook = kwargs.get("request_hook") response_hook = kwargs.get("response_hook") - async_request_hook = kwargs.get("async_request_hook", request_hook) - async_response_hook = kwargs.get("async_response_hook", response_hook) + async_request_hook = kwargs.get("async_request_hook") + async_response_hook = kwargs.get("async_response_hook") if callable(request_hook): _InstrumentedClient._request_hook = request_hook - if callable(async_request_hook): + if callable(async_request_hook) and iscoroutinefunction( + async_request_hook + ): _InstrumentedAsyncClient._request_hook = async_request_hook if callable(response_hook): _InstrumentedClient._response_hook = response_hook - if callable(async_response_hook): + if callable(async_response_hook) and iscoroutinefunction( + async_response_hook + ): _InstrumentedAsyncClient._response_hook = async_response_hook tracer_provider = kwargs.get("tracer_provider") _InstrumentedClient._tracer_provider = tracer_provider diff --git a/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py b/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py index 011b5e57d2..78938eb337 100644 --- a/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py +++ b/instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py @@ -780,9 +780,15 @@ def test_custom_tracer_provider(self): HTTPXClientInstrumentor().uninstrument() def test_response_hook(self): + response_hook_key = ( + "async_response_hook" + if asyncio.iscoroutinefunction(self.response_hook) + else "response_hook" + ) + response_hook_kwargs = {response_hook_key: self.response_hook} HTTPXClientInstrumentor().instrument( tracer_provider=self.tracer_provider, - response_hook=self.response_hook, + **response_hook_kwargs, ) client = self.create_client() result = self.perform_request(self.URL, client=client) @@ -823,9 +829,15 @@ def test_response_hook_sync_async_kwargs(self): HTTPXClientInstrumentor().uninstrument() def test_request_hook(self): + request_hook_key = ( + "async_request_hook" + if asyncio.iscoroutinefunction(self.request_hook) + else "request_hook" + ) + request_hook_kwargs = {request_hook_key: self.request_hook} HTTPXClientInstrumentor().instrument( tracer_provider=self.tracer_provider, - request_hook=self.request_hook, + **request_hook_kwargs, ) client = self.create_client() result = self.perform_request(self.URL, client=client) @@ -1214,3 +1226,36 @@ def test_basic_multiple(self): self.perform_request(self.URL, client=self.client) self.perform_request(self.URL, client=self.client2) self.assert_span(num_spans=2) + + def test_async_response_hook_does_nothing_if_not_coroutine(self): + HTTPXClientInstrumentor().instrument( + tracer_provider=self.tracer_provider, + async_response_hook=_response_hook, + ) + client = self.create_client() + result = self.perform_request(self.URL, client=client) + + self.assertEqual(result.text, "Hello!") + span = self.assert_span() + self.assertEqual( + dict(span.attributes), + { + SpanAttributes.HTTP_METHOD: "GET", + SpanAttributes.HTTP_URL: self.URL, + SpanAttributes.HTTP_STATUS_CODE: 200, + }, + ) + HTTPXClientInstrumentor().uninstrument() + + def test_async_request_hook_does_nothing_if_not_coroutine(self): + HTTPXClientInstrumentor().instrument( + tracer_provider=self.tracer_provider, + async_request_hook=_request_hook, + ) + client = self.create_client() + result = self.perform_request(self.URL, client=client) + + self.assertEqual(result.text, "Hello!") + span = self.assert_span() + self.assertEqual(span.name, "GET") + HTTPXClientInstrumentor().uninstrument()