httpx: fix handling of async hooks (#2823)

This commit is contained in:
Riccardo Magliocchetti
2024-08-26 18:54:57 +02:00
committed by GitHub
parent 19f8e775ce
commit 830397ecbf
3 changed files with 63 additions and 11 deletions

View File

@ -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

View File

@ -192,6 +192,7 @@ API
"""
import logging
import typing
from asyncio import iscoroutinefunction
from types import TracebackType
import httpx
@ -731,15 +732,19 @@ class HTTPXClientInstrumentor(BaseInstrumentor):
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

View File

@ -780,9 +780,15 @@ class BaseTestCases:
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 @@ class BaseTestCases:
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 @@ class TestAsyncInstrumentationIntegration(BaseTestCases.BaseInstrumentorTest):
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()