mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2025-07-28 20:52:57 +08:00
httpx: fix handling of async hooks (#2823)
This commit is contained in:

committed by
GitHub

parent
19f8e775ce
commit
830397ecbf
12
CHANGELOG.md
12
CHANGELOG.md
@ -7,23 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
## Added
|
### Added
|
||||||
|
|
||||||
- `opentelemetry-instrumentation-kafka-python` Instrument temporary fork, kafka-python-ng
|
- `opentelemetry-instrumentation-kafka-python` Instrument temporary fork, kafka-python-ng
|
||||||
inside kafka-python's instrumentation
|
inside kafka-python's instrumentation
|
||||||
([#2537](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2537))
|
([#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
|
- `opentelemetry-bootstrap` Remove `opentelemetry-instrumentation-aws-lambda` from the defaults instrumentations
|
||||||
([#2786](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2786))
|
([#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
|
- `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
|
- `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.
|
- `opentelemetry-instrumentation-aws-lambda` Avoid exception when a handler is not present.
|
||||||
([#2750](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2750))
|
([#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
|
- `opentelemetry-instrumentation-django` Fix regression - `http.target` re-added back to old semconv duration metrics
|
||||||
|
@ -192,6 +192,7 @@ API
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import typing
|
import typing
|
||||||
|
from asyncio import iscoroutinefunction
|
||||||
from types import TracebackType
|
from types import TracebackType
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
@ -731,15 +732,19 @@ class HTTPXClientInstrumentor(BaseInstrumentor):
|
|||||||
self._original_async_client = httpx.AsyncClient
|
self._original_async_client = httpx.AsyncClient
|
||||||
request_hook = kwargs.get("request_hook")
|
request_hook = kwargs.get("request_hook")
|
||||||
response_hook = kwargs.get("response_hook")
|
response_hook = kwargs.get("response_hook")
|
||||||
async_request_hook = kwargs.get("async_request_hook", request_hook)
|
async_request_hook = kwargs.get("async_request_hook")
|
||||||
async_response_hook = kwargs.get("async_response_hook", response_hook)
|
async_response_hook = kwargs.get("async_response_hook")
|
||||||
if callable(request_hook):
|
if callable(request_hook):
|
||||||
_InstrumentedClient._request_hook = 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
|
_InstrumentedAsyncClient._request_hook = async_request_hook
|
||||||
if callable(response_hook):
|
if callable(response_hook):
|
||||||
_InstrumentedClient._response_hook = 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
|
_InstrumentedAsyncClient._response_hook = async_response_hook
|
||||||
tracer_provider = kwargs.get("tracer_provider")
|
tracer_provider = kwargs.get("tracer_provider")
|
||||||
_InstrumentedClient._tracer_provider = tracer_provider
|
_InstrumentedClient._tracer_provider = tracer_provider
|
||||||
|
@ -780,9 +780,15 @@ class BaseTestCases:
|
|||||||
HTTPXClientInstrumentor().uninstrument()
|
HTTPXClientInstrumentor().uninstrument()
|
||||||
|
|
||||||
def test_response_hook(self):
|
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(
|
HTTPXClientInstrumentor().instrument(
|
||||||
tracer_provider=self.tracer_provider,
|
tracer_provider=self.tracer_provider,
|
||||||
response_hook=self.response_hook,
|
**response_hook_kwargs,
|
||||||
)
|
)
|
||||||
client = self.create_client()
|
client = self.create_client()
|
||||||
result = self.perform_request(self.URL, client=client)
|
result = self.perform_request(self.URL, client=client)
|
||||||
@ -823,9 +829,15 @@ class BaseTestCases:
|
|||||||
HTTPXClientInstrumentor().uninstrument()
|
HTTPXClientInstrumentor().uninstrument()
|
||||||
|
|
||||||
def test_request_hook(self):
|
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(
|
HTTPXClientInstrumentor().instrument(
|
||||||
tracer_provider=self.tracer_provider,
|
tracer_provider=self.tracer_provider,
|
||||||
request_hook=self.request_hook,
|
**request_hook_kwargs,
|
||||||
)
|
)
|
||||||
client = self.create_client()
|
client = self.create_client()
|
||||||
result = self.perform_request(self.URL, client=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.client)
|
||||||
self.perform_request(self.URL, client=self.client2)
|
self.perform_request(self.URL, client=self.client2)
|
||||||
self.assert_span(num_spans=2)
|
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()
|
||||||
|
Reference in New Issue
Block a user