Skip to content

Commit d47b777

Browse files
committed
feat: Add exclude urls feature to HTTPX instrumentation
1 parent 7603a1f commit d47b777

File tree

1 file changed

+35
-6
lines changed
  • instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx

1 file changed

+35
-6
lines changed

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

+35-6
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,13 @@ def response_hook(span, request, response):
176176
from opentelemetry.trace import SpanKind, TracerProvider, get_tracer
177177
from opentelemetry.trace.span import Span
178178
from 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

182188
URL = 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__()
@@ -315,12 +324,16 @@ def handle_request(
315324
httpx.Response,
316325
]:
317326
"""Add request info to span."""
318-
if context.get_value("suppress_instrumentation"):
319-
return self._transport.handle_request(*args, **kwargs)
320-
321327
method, url, headers, stream, extensions = _extract_parameters(
322328
args, kwargs
323329
)
330+
331+
if self._excluded_urls and self._excluded_urls.url_disabled(url):
332+
return self._transport.handle_request(*args, **kwargs)
333+
334+
if context.get_value("suppress_instrumentation"):
335+
return self._transport.handle_request(*args, **kwargs)
336+
324337
span_attributes = _prepare_attributes(method, url)
325338

326339
request_info = RequestInfo(method, url, headers, stream, extensions)
@@ -370,6 +383,7 @@ class AsyncOpenTelemetryTransport(httpx.AsyncBaseTransport):
370383
right after the span is created
371384
response_hook: A hook that receives the span, request, and response
372385
that is called right before the span ends
386+
excluded_urls: List of urls that should be excluded from tracing
373387
"""
374388

375389
def __init__(
@@ -378,6 +392,7 @@ def __init__(
378392
tracer_provider: typing.Optional[TracerProvider] = None,
379393
request_hook: typing.Optional[RequestHook] = None,
380394
response_hook: typing.Optional[ResponseHook] = None,
395+
excluded_urls: typing.Optional[ExcludeList] = None,
381396
):
382397
self._transport = transport
383398
self._tracer = get_tracer(
@@ -387,6 +402,7 @@ def __init__(
387402
)
388403
self._request_hook = request_hook
389404
self._response_hook = response_hook
405+
self._excluded_urls = excluded_urls
390406

391407
async def __aenter__(self) -> "AsyncOpenTelemetryTransport":
392408
await self._transport.__aenter__()
@@ -407,12 +423,16 @@ async def handle_async_request(
407423
httpx.Response,
408424
]:
409425
"""Add request info to span."""
410-
if context.get_value("suppress_instrumentation"):
411-
return await self._transport.handle_async_request(*args, **kwargs)
412-
413426
method, url, headers, stream, extensions = _extract_parameters(
414427
args, kwargs
415428
)
429+
430+
if self._excluded_urls and self._excluded_urls.url_disabled(url):
431+
return await self._transport.handle_async_request(*args, **kwargs)
432+
433+
if context.get_value("suppress_instrumentation"):
434+
return await self._transport.handle_async_request(*args, **kwargs)
435+
416436
span_attributes = _prepare_attributes(method, url)
417437

418438
span_name = _get_default_span_name(
@@ -459,6 +479,7 @@ class _InstrumentedClient(httpx.Client):
459479
_tracer_provider = None
460480
_request_hook = None
461481
_response_hook = None
482+
_excluded_urls = None
462483

463484
def __init__(self, *args, **kwargs):
464485
super().__init__(*args, **kwargs)
@@ -478,6 +499,7 @@ class _InstrumentedAsyncClient(httpx.AsyncClient):
478499
_tracer_provider = None
479500
_request_hook = None
480501
_response_hook = None
502+
_excluded_urls = None
481503

482504
def __init__(self, *args, **kwargs):
483505
super().__init__(*args, **kwargs)
@@ -513,11 +535,18 @@ def _instrument(self, **kwargs):
513535
right after the span is created
514536
``response_hook``: A hook that receives the span, request, and response
515537
that is called right before the span ends
538+
``excluded_urls``: A string containing a comma-delimited
539+
list of regexes used to exclude URLs from tracking
516540
"""
517541
self._original_client = httpx.Client
518542
self._original_async_client = httpx.AsyncClient
519543
request_hook = kwargs.get("request_hook")
520544
response_hook = kwargs.get("response_hook")
545+
excluded_urls = kwargs.get("excluded_urls")
546+
if excluded_urls is None:
547+
excluded_urls = _excluded_urls_from_env
548+
else:
549+
excluded_urls = parse_excluded_urls(excluded_urls)
521550
if callable(request_hook):
522551
_InstrumentedClient._request_hook = request_hook
523552
_InstrumentedAsyncClient._request_hook = request_hook

0 commit comments

Comments
 (0)