From 9bc0f731c23bb6fc45765bb312aabf94e697de3c Mon Sep 17 00:00:00 2001 From: maudetes Date: Mon, 14 Jun 2021 11:58:24 +0200 Subject: [PATCH] Migrate from raven to sentry-sdk (#2620) --- CHANGELOG.md | 1 + requirements/sentry.pip | 2 +- setup.py | 2 +- udata/sentry.py | 68 +++++++++++++++++++++++------------------ 4 files changed, 41 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63f453df6..ba05e784d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Current (in progress) +- Migrate from raven to sentry-sdk [#2620](https://github.com/opendatateam/udata/pull/2620) - Add a UdataCleaner class to use udata's markdown configuration on SafeMarkup as well [#2619](https://github.com/opendatateam/udata/pull/2619) - Fix schema name display in resource modal [#2617](https://github.com/opendatateam/udata/pull/2617) diff --git a/requirements/sentry.pip b/requirements/sentry.pip index 5ea9528a7..47cd9ec92 100644 --- a/requirements/sentry.pip +++ b/requirements/sentry.pip @@ -1 +1 @@ -raven[flask] >= 5.8.0 +sentry-sdk[flask] >= 1.1.0 diff --git a/setup.py b/setup.py index 662cc64be..07b123170 100644 --- a/setup.py +++ b/setup.py @@ -69,7 +69,7 @@ def pip(filename): tests_require=tests_require, extras_require={ 'test': tests_require, - 'sentry': ['raven[flask]>=6.1.0'], + 'sentry': ['sentry-sdk[flask] >= 1.1.0'], }, entry_points={ 'console_scripts': [ diff --git a/udata/sentry.py b/udata/sentry.py index 51bc26454..8bf2dd7e5 100644 --- a/udata/sentry.py +++ b/udata/sentry.py @@ -1,6 +1,7 @@ import logging import pkg_resources import re +import warnings from werkzeug.exceptions import HTTPException from udata import entrypoints @@ -11,18 +12,28 @@ log = logging.getLogger(__name__) RE_DSN = re.compile( - r'(?Phttps?)://(?P[0-9a-f]+):[0-9a-f]+' - '@(?P.+)/(?P\d+)') + r'(?Phttps?)://(?P[0-9a-f]+)(?::(?P[0-9a-f]+))?' + r'@(?P.+)/(?P\d+)') + +SECRET_DSN_DEPRECATED_MSG = 'DSN with secret is deprecated, use a public DSN instead' +ERROR_PARSE_DSN_MSG = 'Unable to parse Sentry DSN' # Controlled exceptions that Sentry should ignore IGNORED_EXCEPTIONS = HTTPException, PermissionDenied, UploadProgress - def public_dsn(dsn): - '''Transform a standard Sentry DSN into a public one''' + '''Check if DSN is public or raise a warning and turn it into a public one''' m = RE_DSN.match(dsn) if not m: - log.error('Unable to parse Sentry DSN') + log.error(ERROR_PARSE_DSN_MSG) + raise ValueError(ERROR_PARSE_DSN_MSG) + + if not m["secret"]: + return dsn + + log.warning(SECRET_DSN_DEPRECATED_MSG) + warnings.warn(SECRET_DSN_DEPRECATED_MSG, category=DeprecationWarning) + public = '{scheme}://{client_id}@{domain}/{site_id}'.format( **m.groupdict()) return public @@ -31,42 +42,39 @@ def public_dsn(dsn): def init_app(app): if app.config['SENTRY_DSN']: try: - from raven.contrib.celery import ( - register_signal, register_logger_signal - ) - from raven.contrib.flask import Sentry + import sentry_sdk + from sentry_sdk.integrations.flask import FlaskIntegration + from sentry_sdk.integrations.celery import CeleryIntegration except ImportError: - log.error('raven is required to use Sentry') + log.error('sentry-sdk is required to use Sentry') return - sentry = Sentry() - tags = app.config['SENTRY_TAGS'] - log_level_name = app.config['SENTRY_LOGGING'] - if log_level_name: - log_level = getattr(logging, log_level_name.upper()) - if log_level: - sentry.logging = True - sentry.level = log_level + sentry_public_dsn = public_dsn(app.config['SENTRY_DSN']) # Do not send HTTPExceptions exceptions = set(app.config['SENTRY_IGNORE_EXCEPTIONS']) for exception in IGNORED_EXCEPTIONS: exceptions.add(exception) - app.config['SENTRY_IGNORE_EXCEPTIONS'] = list(exceptions) - app.config['SENTRY_PUBLIC_DSN'] = public_dsn(app.config['SENTRY_DSN']) + sentry_sdk.init( + dsn=sentry_public_dsn, + integrations=[FlaskIntegration(), CeleryIntegration()], + ignore_errors=list(exceptions) + ) + # Set log level + log_level_name = app.config['SENTRY_LOGGING'] + if log_level_name: + log_level = getattr(logging, log_level_name.upper()) + sentry_sdk.set_level(log_level) + + # Set sentry tags + tags = app.config['SENTRY_TAGS'] + for tag_key in tags: + sentry_sdk.set_tag(tag_key, tags[tag_key]) # Versions Management: uData and plugins versions as tags. for dist in entrypoints.get_plugins_dists(app): if dist.version: - tags[dist.project_name] = dist.version + sentry_sdk.set_tag(dist.project_name, dist.version) # Do not forget udata itself - tags['udata'] = pkg_resources.get_distribution('udata').version - - sentry.init_app(app) - - # register a custom filter to filter out duplicate logs - register_logger_signal(sentry.client, loglevel=sentry.level) - - # hook into the Celery error handler - register_signal(sentry.client) + sentry_sdk.set_tag('udata', pkg_resources.get_distribution('udata').version)