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