From bed0a0ceeb6593414220ff3ca20bd0f5d4eeaf8c Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Tue, 5 Feb 2019 11:25:39 +0100 Subject: [PATCH] Switch from raven to sentry_sdk --- src/pretix/base/templates/500.html | 4 +- src/pretix/base/views/errors.py | 4 +- src/pretix/sentry.py | 124 ++++++++++++++++++++--------- src/pretix/settings.py | 23 +++--- src/requirements/production.txt | 2 +- src/setup.py | 2 +- 6 files changed, 106 insertions(+), 53 deletions(-) diff --git a/src/pretix/base/templates/500.html b/src/pretix/base/templates/500.html index 7093f1156e..c99d6ce459 100644 --- a/src/pretix/base/templates/500.html +++ b/src/pretix/base/templates/500.html @@ -8,13 +8,13 @@

{% trans "Internal Server Error" %}

{% trans "We had trouble processing your request." %}

{% trans "If this problem persists, please contact us." %}

- {% if request.sentry.id %} + {% if sentry_event_id %}

{% blocktrans trimmed %} If you contact us, please send us the following code: {% endblocktrans %}
- {{ request.sentry.id }} + {{ sentry_event_id }}

{% endif %}

{{ exception }}

diff --git a/src/pretix/base/views/errors.py b/src/pretix/base/views/errors.py index 7cedf04bc8..aaa5979ae8 100644 --- a/src/pretix/base/views/errors.py +++ b/src/pretix/base/views/errors.py @@ -7,6 +7,7 @@ from django.template.loader import get_template from django.utils.functional import Promise from django.utils.translation import ugettext as _ from django.views.decorators.csrf import requires_csrf_token +from sentry_sdk import last_event_id def csrf_failure(request, reason=""): @@ -65,5 +66,6 @@ def server_error(request): except TemplateDoesNotExist: return HttpResponseServerError('

Server Error (500)

', content_type='text/html') return HttpResponseServerError(template.render({ - 'request': request + 'request': request, + 'sentry_event_id': last_event_id(), })) diff --git a/src/pretix/sentry.py b/src/pretix/sentry.py index 89d5d17021..daeb0362d9 100644 --- a/src/pretix/sentry.py +++ b/src/pretix/sentry.py @@ -1,50 +1,96 @@ -from threading import Lock +import re +import weakref +from collections import OrderedDict -from raven.contrib.celery import SentryCeleryHandler -from raven.contrib.django.apps import RavenConfig -from raven.contrib.django.models import ( - SentryDjangoHandler, client, get_client, install_middleware, - register_serializers, -) +from sentry_sdk import Hub +from sentry_sdk.integrations.django import DjangoIntegration, _set_user_info +from sentry_sdk.utils import capture_internal_exceptions -_setup_lock = Lock() - -_initialized = False +MASK = '*' * 8 +KEYS = frozenset([ + 'password', + 'secret', + 'passwd', + 'authorization', + 'api_key', + 'apikey', + 'sentry_dsn', + 'access_token', + 'session', +]) +VALUES_RE = re.compile(r'^(?:\d[ -]*?){13,16}$') -class CustomSentryDjangoHandler(SentryDjangoHandler): - def install_celery(self): - self.celery_handler = SentryCeleryHandler(client, ignore_expected=True).install() +def scrub_data(data): + if isinstance(data, dict): + for k, v in data.items(): + if isinstance(k, bytes): + key = k.decode('utf-8', 'replace') + else: + key = k + key = key.lower() + data[k] = scrub_data(v) + for blk in KEYS: + if blk in key: + data[k] = MASK + elif isinstance(data, list): + for i, l in enumerate(list(data)): + data[i] = scrub_data(l) + elif isinstance(data, str): + if '=' in data: + # at this point we've assumed it's a standard HTTP query + # or cookie + if '&' in data: + delimiter = '&' + else: + delimiter = ';' + + qd = scrub_data(OrderedDict(e.split('=', 1) if '=' in e else (e, None) for e in data.split(delimiter))) + return delimiter.join((k + '=' + v if v is not None else k) for k, v in qd.items()) + if VALUES_RE.match(data): + return MASK + return data -def initialize(): - global _initialized +def _make_event_processor(weak_request, integration): + def event_processor(event, hint): + request = weak_request() + if request is None: + return event - with _setup_lock: - if _initialized: - return + with capture_internal_exceptions(): + _set_user_info(request, event) + request_info = event.setdefault("request", {}) + request_info["cookies"] = dict(request.COOKIES) - _initialized = True + scrub_data(event.get("request", {})) + if 'exception' in event: + exc = event.get("exception", {}) + for val in exc.get('values', []): + stack = val.get('stacktrace', {}) + for frame in stack.get('frames', []): + scrub_data(frame['vars']) + return event - try: - register_serializers() - install_middleware( - 'raven.contrib.django.middleware.SentryMiddleware', - ( - 'raven.contrib.django.middleware.SentryMiddleware', - 'raven.contrib.django.middleware.SentryLogMiddleware')) - install_middleware( - 'raven.contrib.django.middleware.DjangoRestFrameworkCompatMiddleware') - - handler = CustomSentryDjangoHandler() - handler.install() - - # instantiate client so hooks get registered - get_client() # NOQA - except Exception: - _initialized = False + return event_processor -class App(RavenConfig): - def ready(self): - initialize() +class PretixSentryIntegration(DjangoIntegration): + @staticmethod + def setup_once(): + DjangoIntegration.setup_once() + from django.core.handlers.base import BaseHandler + + old_get_response = BaseHandler.get_response + + def sentry_patched_get_response(self, request): + hub = Hub.current + integration = hub.get_integration(DjangoIntegration) + if integration is not None: + with hub.configure_scope() as scope: + scope.add_event_processor( + _make_event_processor(weakref.ref(request), integration) + ) + return old_get_response(self, request) + + BaseHandler.get_response = sentry_patched_get_response diff --git a/src/pretix/settings.py b/src/pretix/settings.py index 60f10f28b5..ae67290f0e 100644 --- a/src/pretix/settings.py +++ b/src/pretix/settings.py @@ -1,6 +1,7 @@ import configparser import os import sys + from urllib.parse import urlparse from kombu import Queue @@ -564,15 +565,19 @@ LOGGING = { } if config.has_option('sentry', 'dsn'): - INSTALLED_APPS += [ - 'pretix.sentry.App', - ] - RAVEN_CONFIG = { - 'dsn': config.get('sentry', 'dsn'), - 'transport': 'raven.transport.threaded_requests.ThreadedRequestsHTTPTransport', - 'release': __version__, - 'environment': SITE_URL, - } + import sentry_sdk + from sentry_sdk.integrations.celery import CeleryIntegration + from sentry_sdk.integrations.logging import ignore_logger + from .sentry import PretixSentryIntegration + + sentry_sdk.init( + dsn=config.get('sentry', 'dsn'), + integrations=[PretixSentryIntegration(), CeleryIntegration()], + environment=SITE_URL, + release=__version__, + send_default_pii=False + ) + ignore_logger('pretix.base.tasks') CELERY_TASK_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = 'json' diff --git a/src/requirements/production.txt b/src/requirements/production.txt index 617acff845..b78fd60993 100644 --- a/src/requirements/production.txt +++ b/src/requirements/production.txt @@ -29,7 +29,7 @@ csscompressor django-markup markdown<=2.2 bleach==2.* -raven +sentry-sdk==0.7.* babel django-i18nfield>=1.4.0 django-hijack>=2.1.10,<2.2.0 diff --git a/src/setup.py b/src/setup.py index 1359fb8f80..9244c53c9e 100644 --- a/src/setup.py +++ b/src/setup.py @@ -117,7 +117,7 @@ setup( 'django-markup', 'markdown<=2.2', 'bleach==2.*', - 'raven', + 'sentry-sdk==0.7.*', 'babel', 'paypalrestsdk==1.13.*', 'pycparser==2.13',