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

[django] support for Django Rest Framework #389

Merged
merged 21 commits into from
Jan 19, 2018
Merged

Conversation

gabsn
Copy link

@gabsn gabsn commented Dec 19, 2017

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

image

After

image

'rest_framework.permissions.IsAdminUser',
],

'EXCEPTION_HANDLER': 'restframework.exceptions.custom_exception_handler'
Copy link
Author

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)
Copy link
Author

@gabsn gabsn Dec 19, 2017

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

Copy link
Author

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.

@gabsn gabsn requested a review from LotharSee December 19, 2017 17:49
@gabsn gabsn requested review from bmermet and removed request for LotharSee and palazzem December 21, 2017 10:12

# 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
Copy link
Contributor

@LotharSee LotharSee Dec 21, 2017

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.

Copy link
Author

Choose a reason for hiding this comment

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

Totally agree!

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)
Copy link
Contributor

@bmermet bmermet Dec 21, 2017

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.

Copy link
Author

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

Copy link
Contributor

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?

Copy link
Author

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):
Copy link
Contributor

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 ?

Copy link
Author

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?

Copy link
Contributor

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):
Copy link
Contributor

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?

Copy link
Author

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.

Copy link
Contributor

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.

bmermet
bmermet previously approved these changes Dec 22, 2017
Copy link
Contributor

@bmermet bmermet left a comment

Choose a reason for hiding this comment

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

LGTM 👍

@palazzem palazzem changed the title Trace rest_framework app trace Django Rest Framework Jan 3, 2018
@palazzem palazzem added this to the 0.10.1 milestone Jan 3, 2018
Copy link

@palazzem palazzem left a 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


# 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):
Copy link

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.

@@ -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
Copy link

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

Copy link

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.

@palazzem palazzem merged commit 91fb199 into master Jan 19, 2018
@palazzem palazzem deleted the gabin/rest_framework branch January 19, 2018 22:42
@palazzem palazzem changed the title trace Django Rest Framework [django] support for Django Rest Framework Jan 30, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants