@@ -176,7 +176,13 @@ def response_hook(span, request, response):
176176from opentelemetry .trace import SpanKind , TracerProvider , get_tracer
177177from opentelemetry .trace .span import Span
178178from opentelemetry .trace .status import Status
179+ from opentelemetry .utils .http import (
180+ ExcludeList ,
181+ get_excluded_urls ,
182+ parse_excluded_urls ,
183+ )
179184
185+ _excluded_urls_from_env = get_excluded_urls ("HTTPX" )
180186_logger = logging .getLogger (__name__ )
181187
182188URL = typing .Tuple [bytes , bytes , typing .Optional [int ], bytes ]
@@ -276,6 +282,7 @@ class SyncOpenTelemetryTransport(httpx.BaseTransport):
276282 right after the span is created
277283 response_hook: A hook that receives the span, request, and response
278284 that is called right before the span ends
285+ excluded_urls: List of urls that should be excluded from tracing
279286 """
280287
281288 def __init__ (
@@ -284,6 +291,7 @@ def __init__(
284291 tracer_provider : typing .Optional [TracerProvider ] = None ,
285292 request_hook : typing .Optional [RequestHook ] = None ,
286293 response_hook : typing .Optional [ResponseHook ] = None ,
294+ excluded_urls : typing .Optional [ExcludeList ] = None ,
287295 ):
288296 self ._transport = transport
289297 self ._tracer = get_tracer (
@@ -293,6 +301,7 @@ def __init__(
293301 )
294302 self ._request_hook = request_hook
295303 self ._response_hook = response_hook
304+ self ._excluded_urls = excluded_urls
296305
297306 def __enter__ (self ) -> "SyncOpenTelemetryTransport" :
298307 self ._transport .__enter__ ()
@@ -317,10 +326,13 @@ def handle_request(
317326 """Add request info to span."""
318327 if context .get_value ("suppress_instrumentation" ):
319328 return self ._transport .handle_request (* args , ** kwargs )
320-
321329 method , url , headers , stream , extensions = _extract_parameters (
322330 args , kwargs
323331 )
332+
333+ if self ._excluded_urls and self ._excluded_urls .url_disabled (url ):
334+ return self ._transport .handle_request (* args , ** kwargs )
335+
324336 span_attributes = _prepare_attributes (method , url )
325337
326338 request_info = RequestInfo (method , url , headers , stream , extensions )
@@ -370,6 +382,7 @@ class AsyncOpenTelemetryTransport(httpx.AsyncBaseTransport):
370382 right after the span is created
371383 response_hook: A hook that receives the span, request, and response
372384 that is called right before the span ends
385+ excluded_urls: List of urls that should be excluded from tracing
373386 """
374387
375388 def __init__ (
@@ -378,6 +391,7 @@ def __init__(
378391 tracer_provider : typing .Optional [TracerProvider ] = None ,
379392 request_hook : typing .Optional [RequestHook ] = None ,
380393 response_hook : typing .Optional [ResponseHook ] = None ,
394+ excluded_urls : typing .Optional [ExcludeList ] = None ,
381395 ):
382396 self ._transport = transport
383397 self ._tracer = get_tracer (
@@ -387,6 +401,7 @@ def __init__(
387401 )
388402 self ._request_hook = request_hook
389403 self ._response_hook = response_hook
404+ self ._excluded_urls = excluded_urls
390405
391406 async def __aenter__ (self ) -> "AsyncOpenTelemetryTransport" :
392407 await self ._transport .__aenter__ ()
@@ -407,12 +422,16 @@ async def handle_async_request(
407422 httpx .Response ,
408423 ]:
409424 """Add request info to span."""
410- if context .get_value ("suppress_instrumentation" ):
411- return await self ._transport .handle_async_request (* args , ** kwargs )
412-
413425 method , url , headers , stream , extensions = _extract_parameters (
414426 args , kwargs
415427 )
428+
429+ if self ._excluded_urls and self ._excluded_urls .url_disabled (url ):
430+ return await self ._transport .handle_async_request (* args , ** kwargs )
431+
432+ if context .get_value ("suppress_instrumentation" ):
433+ return await self ._transport .handle_async_request (* args , ** kwargs )
434+
416435 span_attributes = _prepare_attributes (method , url )
417436
418437 span_name = _get_default_span_name (
@@ -459,6 +478,7 @@ class _InstrumentedClient(httpx.Client):
459478 _tracer_provider = None
460479 _request_hook = None
461480 _response_hook = None
481+ _excluded_urls = None
462482
463483 def __init__ (self , * args , ** kwargs ):
464484 super ().__init__ (* args , ** kwargs )
@@ -478,6 +498,7 @@ class _InstrumentedAsyncClient(httpx.AsyncClient):
478498 _tracer_provider = None
479499 _request_hook = None
480500 _response_hook = None
501+ _excluded_urls = None
481502
482503 def __init__ (self , * args , ** kwargs ):
483504 super ().__init__ (* args , ** kwargs )
@@ -513,11 +534,18 @@ def _instrument(self, **kwargs):
513534 right after the span is created
514535 ``response_hook``: A hook that receives the span, request, and response
515536 that is called right before the span ends
537+ ``excluded_urls``: A string containing a comma-delimited
538+ list of regexes used to exclude URLs from tracking
516539 """
517540 self ._original_client = httpx .Client
518541 self ._original_async_client = httpx .AsyncClient
519542 request_hook = kwargs .get ("request_hook" )
520543 response_hook = kwargs .get ("response_hook" )
544+ excluded_urls = kwargs .get ("excluded_urls" )
545+ if excluded_urls is None :
546+ excluded_urls = _excluded_urls_from_env
547+ else :
548+ excluded_urls = parse_excluded_urls (excluded_urls )
521549 if callable (request_hook ):
522550 _InstrumentedClient ._request_hook = request_hook
523551 _InstrumentedAsyncClient ._request_hook = request_hook
@@ -536,9 +564,11 @@ def _uninstrument(self, **kwargs):
536564 _InstrumentedClient ._tracer_provider = None
537565 _InstrumentedClient ._request_hook = None
538566 _InstrumentedClient ._response_hook = None
567+ _InstrumentedClient ._excluded_urls = None
539568 _InstrumentedAsyncClient ._tracer_provider = None
540569 _InstrumentedAsyncClient ._request_hook = None
541570 _InstrumentedAsyncClient ._response_hook = None
571+ _InstrumentedAsyncClient ._excluded_urls = None
542572
543573 @staticmethod
544574 def instrument_client (
0 commit comments