-
Notifications
You must be signed in to change notification settings - Fork 395
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
[django] support for Django Rest Framework #389
Conversation
6aba73d
to
8a229bb
Compare
'rest_framework.permissions.IsAdminUser', | ||
], | ||
|
||
'EXCEPTION_HANDLER': 'restframework.exceptions.custom_exception_handler' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I define the custom exception handler.
sp = spans[0] | ||
eq_(sp.name, 'django.request') | ||
eq_(sp.resource, 'restframework.views.UserViewSet') | ||
eq_(sp.error, 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test would break without the rest_framework integration, and that's what we want.
tox.ini
Outdated
setenv = | ||
DATADOG_ENV = test | ||
DJANGO_SETTINGS_MODULE = restframework.settings | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use a different app than for other django unit tests because using rest_framework would break most of our django unit tests.
This way we can only test in a specific app that we properly trace exceptions raised during rest_framework view processing.
7197223
to
b93528d
Compare
ddtrace/contrib/django/apps.py
Outdated
|
||
# project | ||
from .db import patch_db | ||
from .conf import settings | ||
from .cache import patch_cache | ||
from .templates import patch_template | ||
from .rest_framework import patch_rest_framework |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure we want to import it all the time, because it would trigger this exception log https://github.com/DataDog/dd-trace-py/pull/389/files#diff-6f7dbfbaaae5d3b33ba40f455fe6c12fR7
We should probably test the existence or usage of REST framework before trying to import then patch it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally agree!
42d81db
to
e2a3b80
Compare
def unpatch_rest_framework(): | ||
if hasattr(APIView, ORIGINAL_HANDLE_EXCEPTION): | ||
original_handle_exception = getattr(APIView, ORIGINAL_HANDLE_EXCEPTION) | ||
setattr(APIView, 'handle_exception', original_handle_exception) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use the wrapt
library to wrap the method as it's done in most other integrations? It would be safer, at least it has safegards in case the number of parameters mismatch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, gonna use it.
@@ -97,10 +97,21 @@ | |||
'django.contrib.auth', | |||
'django.contrib.contenttypes', | |||
'django.contrib.sessions', | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it not needed to have the if django.VERSION >= (1, 10):
check here too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep !
self.unpatch_rest_framework() | ||
ok_(not hasattr(self.APIView, self.ORIGINAL_HANDLE_EXCEPTION)) | ||
|
||
def test_tracer(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the purpose of that test ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Testing if the tracer is enabled. Do you think I should remove it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did your code change the logic for that? Because otherwise I think it's already checked elsewhere in the tests.
ok_(apps.is_installed('rest_framework')) | ||
ok_(hasattr(self.APIView, self.ORIGINAL_HANDLE_EXCEPTION)) | ||
|
||
def test_unpatch(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you check that no span is generated if you raise an exception after it is unpatched?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A span will be generated, but the error won't get caught.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right sorry. Can you check that behavior then? It would validate that your instrumentation changes the existing behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also update our documentation if we have a support limit for DRF: http://pypi.datadoghq.com/trace/docs/#supported-versions
ddtrace/contrib/django/apps.py
Outdated
|
||
# Instrument rest_framework app to trace custom exception handling. | ||
# rest_framework only supports django 1.10, 1.11 and 2.0. | ||
if apps.is_installed('rest_framework') and django.VERSION >= (1, 10): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we don't need the and django.VERSION >= (1, 10)
clause, if Django 1.8 is used with an incompatible version of DRF, neither the user app will work so we can assume here that it's properly configured. Let's have these check only if we do a "special" import.
.circleci/config.yml
Outdated
@@ -296,8 +296,8 @@ jobs: | |||
- restore_cache: | |||
keys: | |||
- tox-cache-django-{{ checksum "tox.ini" }} | |||
- run: tox -e '{py27,py34,py35,py36}-django{18,19,110,111}-djangopylibmc06-djangoredis45-pylibmc-redis{210}-memcached' --result-json /tmp/django.1.results | |||
- run: tox -e '{py27,py34,py35,py36}-django-autopatch{18,19,110,111}-djangopylibmc06-djangoredis45-pylibmc-redis{210}-memcached' --result-json /tmp/django.2.results | |||
- run: tox -e '{py27,py34,py35,py36}-django{18,19,110,111}-djangopylibmc06-djangoredis45-pylibmc-redis{210}-memcached-restframework' --result-json /tmp/django.1.results |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we don't need all of this. Can you rollback this change and just create two new envs?
tox -e '{py27,py34,py35,py36}-django{18,19,110,111}-restframework{37,36,...}
tox -e '{py27,py34,py35,py36}-django-autopatch{18,19,110,111}-restframework{37,36,...}
To test rest framework we don't need memcache (make the required changes in the settings.py
if needed, or create simply a new config file just for DRF).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also let's test the latest 3 or 4 versions of DRF, though having a best-effort support since 3.0 would be great.
d573ca2
to
ff99a5e
Compare
a38c9d1
to
cd33bd1
Compare
5a71fd7
to
dccea31
Compare
This PR is an integration for the rest_framework django app.
Note: Only DRF >= 3.4 is supported.
When the user was using a custom exception handler, the exception was handled before it comes to our
TraceExceptionMiddleware
.Now we patch
handle_exception
used in rest_framework to trace those exceptions.Before
After