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

Adding tracked_url_callback to RequestsInstrumentor #714

Merged
merged 13 commits into from
Oct 8, 2021
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#686](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/686))
- `opentelemetry-instrumentation-tornado` now sets `http.client_ip` and `tornado.handler` attributes
([#706](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/706))
- `opentelemetry-instrumentation-requests` added `tracked_url_callback`
benjaminsky marked this conversation as resolved.
Show resolved Hide resolved
([#714](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/714))

### Changed
- `opentelemetry-instrumentation-botocore` Make common span attributes compliant with semantic conventions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.trace import SpanKind, get_tracer
from opentelemetry.trace.status import Status
from opentelemetry.util.http import remove_url_credentials
from opentelemetry.util.http import remove_url_credentials, get_excluded_urls, parse_excluded_urls
from opentelemetry.util.http.httplib import set_ip_on_next_http_connection

# A key to a context variable to avoid creating duplicate spans when instrumenting
Expand All @@ -61,10 +61,13 @@
"suppress_http_instrumentation"
)

_excluded_urls_from_env = get_excluded_urls("REQUESTS")

# pylint: disable=unused-argument
# pylint: disable=R0915
def _instrument(tracer, span_callback=None, name_callback=None):
def _instrument(
tracer, span_callback=None, name_callback=None, excluded_urls=None
):
"""Enables tracing of all requests calls that go through
:code:`requests.session.Session.request` (this includes
:code:`requests.get`, etc.)."""
Expand All @@ -81,6 +84,9 @@ def _instrument(tracer, span_callback=None, name_callback=None):

@functools.wraps(wrapped_request)
def instrumented_request(self, method, url, *args, **kwargs):
if excluded_urls and excluded_urls.url_disabled(url):
return wrapped_request(self, method, url, *args, **kwargs)

def get_or_create_headers():
headers = kwargs.get("headers")
if headers is None:
Expand All @@ -98,6 +104,9 @@ def call_wrapped():

@functools.wraps(wrapped_send)
def instrumented_send(self, request, **kwargs):
if excluded_urls and excluded_urls.url_disabled(request.url):
return wrapped_send(self, request, **kwargs)

def get_or_create_headers():
request.headers = (
request.headers
Expand Down Expand Up @@ -223,13 +232,19 @@ def _instrument(self, **kwargs):
``name_callback``: Callback which calculates a generic span name for an
outgoing HTTP request based on the method and url.
Optional: Defaults to get_default_span_name.
``excluded_urls``: A string containing a comma-delimitted
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")
_instrument(
tracer,
span_callback=kwargs.get("span_callback"),
name_callback=kwargs.get("name_callback"),
excluded_urls=_excluded_urls_from_env
if excluded_urls is None
else parse_excluded_urls(excluded_urls)
)

def _uninstrument(self, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from opentelemetry.test.mock_textmap import MockTextMapPropagator
from opentelemetry.test.test_base import TestBase
from opentelemetry.trace import StatusCode
from opentelemetry.util.http import get_excluded_urls


class TransportMock:
Expand Down Expand Up @@ -64,6 +65,21 @@ class RequestsIntegrationTestBase(abc.ABC):
# pylint: disable=invalid-name
def setUp(self):
super().setUp()

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stop the patch in teardown.


self.exclude_patch = mock.patch(
"opentelemetry.instrumentation.requests._excluded_urls_from_env",
get_excluded_urls("REQUESTS"),
)
self.exclude_patch.start()

RequestsInstrumentor().instrument()
httpretty.enable()
httpretty.register_uri(httpretty.GET, self.URL, body="Hello!")
Expand Down Expand Up @@ -125,6 +141,32 @@ def name_callback(method, url):

self.assertEqual(span.name, "GET" + self.URL)

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

RequestsInstrumentor().uninstrument()
RequestsInstrumentor().instrument(excluded_urls=".*/404")
self.perform_request(self.URL)
self.perform_request(url_404)

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,
)

RequestsInstrumentor().uninstrument()
RequestsInstrumentor().instrument()
self.perform_request(self.URL)
self.perform_request(url)

self.assert_span(num_spans=1)

def test_name_callback_default(self):
def name_callback(method, url):
return 123
Expand Down