Skip to content

Commit 24f4b7e

Browse files
committed
fix: sync response hooks being used on httpx.AsyncClient
1 parent 0871dd4 commit 24f4b7e

File tree

2 files changed

+66
-11
lines changed

2 files changed

+66
-11
lines changed

instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/__init__.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,16 @@ def response_hook(span, request, response):
131131
# status_code, headers, stream, extensions = response
132132
pass
133133
134-
HTTPXClientInstrumentor().instrument(request_hook=request_hook, response_hook=response_hook)
134+
async def async_request_hook(span, request):
135+
# method, url, headers, stream, extensions = request
136+
pass
137+
138+
async def async_response_hook(span, request, response):
139+
# method, url, headers, stream, extensions = request
140+
# status_code, headers, stream, extensions = response
141+
pass
142+
143+
HTTPXClientInstrumentor().instrument(request_hook=request_hook, response_hook=response_hook, async_request_hook=async_request_hook, async_response_hook=async_response_hook)
135144
136145
137146
Or if you are using the transport classes directly:
@@ -376,8 +385,8 @@ def __init__(
376385
self,
377386
transport: httpx.AsyncBaseTransport,
378387
tracer_provider: typing.Optional[TracerProvider] = None,
379-
request_hook: typing.Optional[RequestHook] = None,
380-
response_hook: typing.Optional[ResponseHook] = None,
388+
request_hook: typing.Optional[AsyncRequestHook] = None,
389+
response_hook: typing.Optional[AsyncResponseHook] = None,
381390
):
382391
self._transport = transport
383392
self._tracer = get_tracer(
@@ -509,21 +518,27 @@ def _instrument(self, **kwargs):
509518
Args:
510519
**kwargs: Optional arguments
511520
``tracer_provider``: a TracerProvider, defaults to global
512-
``request_hook``: A hook that receives the span and request that is called
513-
right after the span is created
514-
``response_hook``: A hook that receives the span, request, and response
515-
that is called right before the span ends
521+
``request_hook``: A ``httpx.Client`` hook that receives the span and request
522+
that is called right after the span is created
523+
``response_hook``: A ``httpx.Client`` hook that receives the span, request,
524+
and response that is called right before the span ends
525+
``async_request_hook``: Async ``request_hook`` for ``httpx.AsyncClient``
526+
``async_response_hook``: Async``response_hook`` for ``httpx.AsyncClient``
516527
"""
517528
self._original_client = httpx.Client
518529
self._original_async_client = httpx.AsyncClient
519530
request_hook = kwargs.get("request_hook")
520531
response_hook = kwargs.get("response_hook")
532+
async_request_hook = kwargs.get("async_request_hook", request_hook)
533+
async_response_hook = kwargs.get("async_response_hook", response_hook)
521534
if callable(request_hook):
522535
_InstrumentedClient._request_hook = request_hook
523-
_InstrumentedAsyncClient._request_hook = request_hook
536+
if callable(async_request_hook):
537+
_InstrumentedAsyncClient._request_hook = async_request_hook
524538
if callable(response_hook):
525539
_InstrumentedClient._response_hook = response_hook
526-
_InstrumentedAsyncClient._response_hook = response_hook
540+
if callable(async_response_hook):
541+
_InstrumentedAsyncClient._response_hook = async_response_hook
527542
tracer_provider = kwargs.get("tracer_provider")
528543
_InstrumentedClient._tracer_provider = tracer_provider
529544
_InstrumentedAsyncClient._tracer_provider = tracer_provider
@@ -544,8 +559,12 @@ def _uninstrument(self, **kwargs):
544559
def instrument_client(
545560
client: typing.Union[httpx.Client, httpx.AsyncClient],
546561
tracer_provider: TracerProvider = None,
547-
request_hook: typing.Optional[RequestHook] = None,
548-
response_hook: typing.Optional[ResponseHook] = None,
562+
request_hook: typing.Union[
563+
typing.Optional[RequestHook], typing.Optional[AsyncRequestHook]
564+
] = None,
565+
response_hook: typing.Union[
566+
typing.Optional[ResponseHook], typing.Optional[AsyncResponseHook]
567+
] = None,
549568
) -> None:
550569
"""Instrument httpx Client or AsyncClient
551570

instrumentation/opentelemetry-instrumentation-httpx/tests/test_httpx_integration.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,28 @@ def test_response_hook(self):
421421
)
422422
HTTPXClientInstrumentor().uninstrument()
423423

424+
def test_response_hook_sync_async_kwargs(self):
425+
HTTPXClientInstrumentor().instrument(
426+
tracer_provider=self.tracer_provider,
427+
response_hook=_response_hook,
428+
async_response_hook=_async_response_hook,
429+
)
430+
client = self.create_client()
431+
result = self.perform_request(self.URL, client=client)
432+
433+
self.assertEqual(result.text, "Hello!")
434+
span = self.assert_span()
435+
self.assertEqual(
436+
span.attributes,
437+
{
438+
SpanAttributes.HTTP_METHOD: "GET",
439+
SpanAttributes.HTTP_URL: self.URL,
440+
SpanAttributes.HTTP_STATUS_CODE: 200,
441+
HTTP_RESPONSE_BODY: "Hello!",
442+
},
443+
)
444+
HTTPXClientInstrumentor().uninstrument()
445+
424446
def test_request_hook(self):
425447
HTTPXClientInstrumentor().instrument(
426448
tracer_provider=self.tracer_provider,
@@ -434,6 +456,20 @@ def test_request_hook(self):
434456
self.assertEqual(span.name, "GET" + self.URL)
435457
HTTPXClientInstrumentor().uninstrument()
436458

459+
def test_request_hook_sync_async_kwargs(self):
460+
HTTPXClientInstrumentor().instrument(
461+
tracer_provider=self.tracer_provider,
462+
request_hook=_request_hook,
463+
async_request_hook=_async_request_hook,
464+
)
465+
client = self.create_client()
466+
result = self.perform_request(self.URL, client=client)
467+
468+
self.assertEqual(result.text, "Hello!")
469+
span = self.assert_span()
470+
self.assertEqual(span.name, "GET" + self.URL)
471+
HTTPXClientInstrumentor().uninstrument()
472+
437473
def test_request_hook_no_span_update(self):
438474
HTTPXClientInstrumentor().instrument(
439475
tracer_provider=self.tracer_provider,

0 commit comments

Comments
 (0)