Skip to content

Commit

Permalink
WIP: Add Django ASGI support
Browse files Browse the repository at this point in the history
Still a WIP, this diff adds `asgi` as an extra, and uses its methods if
the current request is an `ASGIRequest`.

I still need to dig deeper in the current test suite, to find a way to
duplicate the tests in
`instrumentation/opentelemetry-instrumentation-django/tests/test_middleware.py`,
but using an
[`AsyncClient`](https://docs.djangoproject.com/en/3.1/topics/testing/tools/#testing-asynchronous-code).

Fixes open-telemetry#165, open-telemetry#185, open-telemetry#280, open-telemetry#334.
  • Loading branch information
adamantike committed Mar 31, 2021
1 parent a946d5c commit 57c7dd5
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ install_requires =
opentelemetry-api == 1.0.0

[options.extras_require]
asgi =
opentelemetry-instrumentation-asgi == 0.19.b0
test =
opentelemetry-test == 0.19b0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from opentelemetry.instrumentation.utils import extract_attributes_from_object
from opentelemetry.instrumentation.wsgi import (
add_response_attributes,
collect_request_attributes,
collect_request_attributes as wsgi_collect_request_attributes,
wsgi_getter,
)
from opentelemetry.propagate import extract
Expand All @@ -40,6 +40,25 @@
except ImportError:
MiddlewareMixin = object

try:
from django.core.handlers.asgi import ASGIRequest
except ImportError:
ASGIRequest = None

try:
from opentelemetry.instrumentation.asgi import (
asgi_getter,
collect_request_attributes as asgi_collect_request_attributes,
set_status_code,
)
_is_asgi_supported = True
except ImportError:
asgi_getter = None
asgi_collect_request_attributes = None
set_status_code = None
_is_asgi_supported = False


_logger = getLogger(__name__)
_attributes_by_preference = [
["http.scheme", "http.host", "http.target"],
Expand Down Expand Up @@ -84,6 +103,9 @@ def _get_span_name(request):
except Resolver404:
return "HTTP {}".format(request.method)

def _is_asgi_request(self, request):
return ASGIRequest and isinstance(request, ASGIRequest)

def process_request(self, request):
# request.META is a dictionary containing all available HTTP headers
# Read more about request.META here:
Expand All @@ -92,12 +114,23 @@ def process_request(self, request):
if self._excluded_urls.url_disabled(request.build_absolute_uri("?")):
return

is_asgi_request = self._is_asgi_request(request)
if is_asgi_request and not _is_asgi_supported:
return

# pylint:disable=W0212
request._otel_start_time = time()

request_meta = request.META

token = attach(extract(request_meta, getter=wsgi_getter))
if is_asgi_request:
carrier_getter = asgi_getter
collect_request_attributes = asgi_collect_request_attributes
else:
carrier_getter = wsgi_getter
collect_request_attributes = wsgi_collect_request_attributes

token = attach(extract(request_meta, getter=carrier_getter))

tracer = get_tracer(__name__, __version__)

Expand Down Expand Up @@ -156,15 +189,22 @@ def process_response(self, request, response):
if self._excluded_urls.url_disabled(request.build_absolute_uri("?")):
return response

is_asgi_request = self._is_asgi_request(request)
if is_asgi_request and not _is_asgi_supported:
return

if (
self._environ_activation_key in request.META.keys()
and self._environ_span_key in request.META.keys()
):
add_response_attributes(
request.META[self._environ_span_key],
"{} {}".format(response.status_code, response.reason_phrase),
response,
)
if is_asgi_request:
set_status_code(request.META[self._environ_span_key], response.status_code)
else:
add_response_attributes(
request.META[self._environ_span_key],
"{} {}".format(response.status_code, response.reason_phrase),
response,
)

request.META.pop(self._environ_span_key)

Expand All @@ -182,7 +222,10 @@ def process_response(self, request, response):
request.META.pop(self._environ_activation_key)

if self._environ_token in request.META.keys():
detach(request.environ.get(self._environ_token))
if is_asgi_request:
detach(request.META.get(self._environ_token))
else:
detach(request.environ.get(self._environ_token))
request.META.pop(self._environ_token)

return response
4 changes: 2 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ commands_pre =

falcon,flask,django,pyramid,tornado,starlette,fastapi: pip install {toxinidir}/util/opentelemetry-util-http
wsgi,falcon,flask,django,pyramid: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi
asgi,starlette,fastapi: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi
asgi,django,starlette,fastapi: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi

asyncpg: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg

Expand Down Expand Up @@ -319,7 +319,7 @@ commands =

[testenv:lint]
basepython: python3.8
recreate = False
recreate = False
deps =
-c dev-requirements.txt
flaky
Expand Down

0 comments on commit 57c7dd5

Please sign in to comment.