getsentry / raven-python

Raven is the legacy Python client for Sentry (getsentry.com) — replaced by sentry-python
https://sentry.io
BSD 3-Clause "New" or "Revised" License
1.68k stars 657 forks source link

[DOCS] Django Integration for User Feedback #945

Open audiolion opened 7 years ago

audiolion commented 7 years ago

The docs for user feedback integration with Django state that it is as simple as dropping that into the 500.html template. However, this is incorrect.

Django's default server_error() view passes an empty context variable so the template renders and the {% if request.sentry.id %} line always results in False because there is no request object passed back (empty context dict).

The resolution is stated in the docs where it references a custom handler500() view that passes the context. I feel like that needs to be included down in the User Feedback section somewhere, and can submit a PR if you'd like.

The new documentation for User Feedback would look something like:

User Feedback

By default Django will render 500.html, so simply drop the following snippet into your template:

<!-- Sentry JS SDK 2.1.+ required -->
<script src="https://cdn.ravenjs.com/2.3.0/raven.min.js"></script>

{% if request.sentry.id %}
  <script>
  Raven.showReportDialog({
    eventId: '{{ request.sentry.id }}',

    // use the public DSN (dont include your secret!)
    dsn: '___PUBLIC_DSN___'
  });
  </script>
{% endif %}

Then override the default 500 handler to include the context information. In your URL Conf (base urls.py) add:

handler500 = 'myapp.views.sentry_server_error'

Then define that view as:

from django import http
from django.template import Context, RequestContext, TemplateDoesNotExist, loader
from django.views.decorators.csrf import requires_csrf_token

@requires_csrf_token
def sentry_server_error(request, template_name='500.html'):
    try:
        template = loader.get_template(template_name)
    except TemplateDoesNotExist:
        return http.HttpResponseServerError('<h1>Server Error (500)</h1>', content_type='text/html')
    return http.HttpResponseServerError(template.render(RequestContext(request)))

Note

Because Django's default server_error() passes an empty context variable we need to create a custom 500 error handler to pass the request context.

dcramer commented 7 years ago

I suppose we could do provide a built-in replacement to server_error which handles passing in the required context from Sentry. There's good reasons you don't want context in general to be available, but we can guarantee safety around things we provide.

jaaved commented 6 years ago

In django >= 2.0

@requires_csrf_token
def sentry_server_error(request, template_name=ERROR_500_TEMPLATE_NAME):
    """
    500 error handler.

    Templates: :template:`500.html`
    Context: None
    """
    try:
        template = loader.get_template(template_name)
    except TemplateDoesNotExist:
        if template_name != ERROR_500_TEMPLATE_NAME:
            # Reraise if it's a missing custom template.
            raise
        return HttpResponseServerError('<h1>Server Error (500)</h1>', content_type='text/html')
    #return HttpResponseServerError(template.render(RequestContext(request))) # instead of this; to avoid TypeError
    return HttpResponseServerError(template.render(context=None, request=request))
epicserve commented 6 years ago

@dcramer,

FYI, I just ran into this as well. The docs don't mention using a custom 500 view right now either.

epicserve commented 6 years ago

I guess It does say something in the Message Reference section, but I missed it because there wasn't anything in the User Feedback Section.

https://docs.sentry.io/clients/python/integrations/django/#message-references

epicserve commented 6 years ago

Also as an FYI you might want to suggest a server error view like the following so you can dynamically insert the dsn.

from urllib.parse import urlparse

def server_error(request):

    # Use the 500 template for each program if it exists
    url_segment = request.path_info.strip("/").split("/")
    program_500_template = "%s/500.html" % url_segment[0]
    t = loader.select_template([program_500_template, '500.html'])

    sentry_dsn = None
    if hasattr(settings, 'RAVEN_CONFIG') is True and 'dsn' in settings.RAVEN_CONFIG and settings.RAVEN_CONFIG['dsn']:
        # strip out the password
        url_obj = urlparse(settings.RAVEN_CONFIG['dsn'])
        sentry_dsn = '{scheme}://{username}@{hostname}{path}'.format(scheme=url_obj.scheme, username=url_obj.username, hostname=url_obj.hostname, path=url_obj.path)

    context_dict = {'request': request, 'sentry_dsn': sentry_dsn}
    return HttpResponseServerError(t.render(context_dict))
AzyCrw4282 commented 2 years ago

I also came across this error today. I was following the docs here and inconveniently it doesn't mention the step of overriding the 500 handler. If you follow that as shown in the top answer and then the rest of the code in the docs will help solve the problem.