Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add exclude_list for urllib/urllib3 instrumentations #1733

Merged
merged 6 commits into from
Mar 31, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
urllib3
lzchen committed Mar 29, 2023
commit ab164d37f692104c94e6fdf0ddd65330962f8cfe
Original file line number Diff line number Diff line change
@@ -58,6 +58,7 @@ def response_hook(span, request_obj, response)

Exclude lists
*************

To exclude certain URLs from being tracked, set the environment variable ``OTEL_PYTHON_URLLIB_EXCLUDED_URLS``
(or ``OTEL_PYTHON_EXCLUDED_URLS`` as fallback) with comma delimited regexes representing which URLs to exclude.

@@ -195,14 +196,14 @@ def call_wrapped():
def _instrumented_open_call(
_, request, call_wrapped, get_or_create_headers
): # pylint: disable=too-many-locals
url = request.full_url
if excluded_urls and excluded_urls.url_disabled(url):
return call_wrapped()

if context.get_value(
_SUPPRESS_INSTRUMENTATION_KEY
) or context.get_value(_SUPPRESS_HTTP_INSTRUMENTATION_KEY):
return call_wrapped()

url = request.full_url
if excluded_urls and excluded_urls.url_disabled(url):
return call_wrapped()

method = request.get_method().upper()

Original file line number Diff line number Diff line change
@@ -60,6 +60,20 @@ def response_hook(span, request, response):
request_hook=request_hook, response_hook=response_hook)
)

Exclude lists
*************

To exclude certain URLs from being tracked, set the environment variable ``OTEL_PYTHON_URLLIB3_EXCLUDED_URLS``
(or ``OTEL_PYTHON_EXCLUDED_URLS`` as fallback) with comma delimited regexes representing which URLs to exclude.

For example,

::

export OTEL_PYTHON_URLLIB3_EXCLUDED_URLS="client/.*/info,healthcheck"

will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``.

API
---
"""
@@ -92,8 +106,16 @@ def response_hook(span, request, response):
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.trace import Span, SpanKind, Tracer, get_tracer
from opentelemetry.trace.status import Status
from opentelemetry.util.http import (
ExcludeList,
get_excluded_urls,
parse_excluded_urls,
remove_url_credentials,
)
from opentelemetry.util.http.httplib import set_ip_on_next_http_connection

_excluded_urls_from_env = get_excluded_urls("URLLIB3")

_UrlFilterT = typing.Optional[typing.Callable[[str], str]]
_RequestHookT = typing.Optional[
typing.Callable[
@@ -138,10 +160,14 @@ def _instrument(self, **kwargs):
``response_hook``: An optional callback which is invoked right before the span is finished processing a response.
``url_filter``: A callback to process the requested URL prior
to adding it as a span attribute.
``excluded_urls``: A string containing a comma-delimited
list of regexes used to exclude URLs from tracking
"""
tracer_provider = kwargs.get("tracer_provider")
tracer = get_tracer(__name__, __version__, tracer_provider)

excluded_urls = kwargs.get("excluded_urls")

meter_provider = kwargs.get("meter_provider")
meter = get_meter(__name__, __version__, meter_provider)

@@ -169,6 +195,9 @@ def _instrument(self, **kwargs):
request_hook=kwargs.get("request_hook"),
response_hook=kwargs.get("response_hook"),
url_filter=kwargs.get("url_filter"),
excluded_urls=_excluded_urls_from_env
if excluded_urls is None
else parse_excluded_urls(excluded_urls),
)

def _uninstrument(self, **kwargs):
@@ -183,13 +212,17 @@ def _instrument(
request_hook: _RequestHookT = None,
response_hook: _ResponseHookT = None,
url_filter: _UrlFilterT = None,
excluded_urls: ExcludeList = None,
):
def instrumented_urlopen(wrapped, instance, args, kwargs):
if _is_instrumentation_suppressed():
return wrapped(*args, **kwargs)

method = _get_url_open_arg("method", args, kwargs).upper()
url = _get_url(instance, args, kwargs, url_filter)
if excluded_urls and excluded_urls.url_disabled(url):
return wrapped(*args, **kwargs)

method = _get_url_open_arg("method", args, kwargs).upper()
headers = _prepare_headers(kwargs)
body = _get_url_open_arg("body", args, kwargs)

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
BatchSpanProcessor,
ConsoleSpanExporter,
)
from opentelemetry.instrumentation.urllib3 import URLLib3Instrumentor
import urllib3

URLLib3Instrumentor().instrument(
excluded_urls="https://www.example.com",
)

http = urllib3.PoolManager()

provider = TracerProvider()
processor = BatchSpanProcessor(ConsoleSpanExporter())
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("Request parent span") as span:
response = http.request("GET", "https://www.example.com/")
response2 = http.request("GET", "https://www.example.org/")

input()
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.test.mock_textmap import MockTextMapPropagator
from opentelemetry.test.test_base import TestBase
from opentelemetry.util.http import get_excluded_urls

# pylint: disable=too-many-public-methods

@@ -39,6 +40,21 @@ class TestURLLib3Instrumentor(TestBase):

def setUp(self):
super().setUp()

self.env_patch = mock.patch.dict(
"os.environ",
{
"OTEL_PYTHON_URLLIB3_EXCLUDED_URLS": "http://localhost/env_excluded_arg/123,env_excluded_noarg"
},
)
self.env_patch.start()

self.exclude_patch = mock.patch(
"opentelemetry.instrumentation.urllib3._excluded_urls_from_env",
get_excluded_urls("URLLIB3"),
)
self.exclude_patch.start()

URLLib3Instrumentor().instrument()

httpretty.enable(allow_net_connect=False)
@@ -158,6 +174,36 @@ def test_url_open_explicit_arg_parameters(self):

self.assert_success_span(response, url)

def test_excluded_urls_explicit(self):
url_201 = "http://httpbin.org/status/201"
httpretty.register_uri(
httpretty.GET,
url_201,
status=201,
)

URLLib3Instrumentor().uninstrument()
URLLib3Instrumentor().instrument(excluded_urls=".*/201")
self.perform_request(self.HTTP_URL)
self.perform_request(url_201)

self.assert_span(num_spans=1)

def test_excluded_urls_from_env(self):
url = "http://localhost/env_excluded_arg/123"
httpretty.register_uri(
httpretty.GET,
url,
status=200,
)

URLLib3Instrumentor().uninstrument()
URLLib3Instrumentor().instrument()
self.perform_request(self.HTTP_URL)
self.perform_request(url)

self.assert_span(num_spans=1)

def test_uninstrument(self):
URLLib3Instrumentor().uninstrument()