From 406a137eb22407f10c917c0ab41ab8366929ddda Mon Sep 17 00:00:00 2001 From: Mark Pittaway Date: Fri, 6 Dec 2024 10:52:14 +1100 Subject: [PATCH] fix(sentry): Replace raven with sentry-sdk --- content_api/app/__init__.py | 3 --- prod_api/app/__init__.py | 3 --- setup.py | 2 +- superdesk/factory/app.py | 20 +++++++++++++++++--- superdesk/factory/sentry.py | 29 ----------------------------- tests/sentry_tests.py | 12 ------------ 6 files changed, 18 insertions(+), 51 deletions(-) delete mode 100644 superdesk/factory/sentry.py delete mode 100644 tests/sentry_tests.py diff --git a/content_api/app/__init__.py b/content_api/app/__init__.py index 04a211089c..ed28f7f102 100644 --- a/content_api/app/__init__.py +++ b/content_api/app/__init__.py @@ -29,7 +29,6 @@ from superdesk.factory.elastic_apm import setup_apm from superdesk.validator import SuperdeskValidator from superdesk.factory.app import SuperdeskEve, set_error_handlers, get_media_storage_class -from superdesk.factory.sentry import SuperdeskSentry def get_app(config=None): @@ -82,8 +81,6 @@ def get_app(config=None): except AttributeError: pass - app.sentry = SuperdeskSentry(app) - return app diff --git a/prod_api/app/__init__.py b/prod_api/app/__init__.py index eca71c7590..a204c976f5 100644 --- a/prod_api/app/__init__.py +++ b/prod_api/app/__init__.py @@ -26,7 +26,6 @@ from superdesk.factory.elastic_apm import setup_apm from superdesk.validator import SuperdeskValidator from superdesk.factory.app import SuperdeskEve, set_error_handlers, get_media_storage_class -from superdesk.factory.sentry import SuperdeskSentry from prod_api.auth import JWTAuth @@ -90,8 +89,6 @@ def get_app(config=None): else: init_app(app) - app.sentry = SuperdeskSentry(app) - return app diff --git a/setup.py b/setup.py index 9464341a27..de8ff0c1d2 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ "ldap3>=2.2.4,<2.10", "pytz>=2021.3", "tzlocal>=5.2", - "raven[flask]>=5.10,<7.0", + "sentry-sdk[quart]>=1.5.7,<2.0", "requests>=2.7.0,<3.0", "boto3>=1.26,<2.0", "websockets>=12.0,<13.2", diff --git a/superdesk/factory/app.py b/superdesk/factory/app.py index 9f3de92b84..12e8ebdd65 100644 --- a/superdesk/factory/app.py +++ b/superdesk/factory/app.py @@ -19,6 +19,9 @@ import superdesk import logging +import sentry_sdk +from sentry_sdk.integrations.asyncio import AsyncioIntegration +from sentry_sdk.integrations.quart import QuartIntegration from flask_mail import Mail from eve.auth import TokenAuth from eve.io.mongo.mongo import _create_index as create_index @@ -42,7 +45,6 @@ from superdesk.celery_app import init_celery from superdesk.datalayer import SuperdeskDataLayer # noqa from superdesk.errors import SuperdeskError, SuperdeskApiError, DocumentError -from superdesk.factory.sentry import SuperdeskSentry from superdesk.logging import configure_logging from superdesk.storage import ProxyMediaStorage from superdesk.validator import SuperdeskValidator @@ -225,8 +227,8 @@ def __init__(self, **kwargs): self._endpoint_groups = [] self._endpoint_lookup = {} super().__init__(**kwargs) + self.setup_sentry() self.async_app = SuperdeskAsyncApp(self) - self.teardown_request(self._after_each_request) def __getattr__(self, name): @@ -399,6 +401,19 @@ def get_current_request(self, req=None) -> HttpFlaskRequest | None: new_request.user = self.async_app.auth.get_current_user(new_request) return new_request + def setup_sentry(self): + if not self.config.get("SENTRY_DSN"): + return + + # Given how quart_flask_patch patches things, it makes Sentry SDK to think that flask is installed + # mistakenly enabling FlaskIntegration, which breaks QuartIntegration, and preventing sentry from working properly. + # https://github.com/pgjones/quart-flask-patch/blob/0.3.0/src/quart_flask_patch/_patch.py#L110 + # This prevents flask integration from being enabled at all until we're can use a newer version of sentry-sdk where + # specific integrations can be disabled https://github.com/getsentry/sentry-python/releases/tag/2.11.0 + sentry_sdk.integrations._processed_integrations.add("flask") + + sentry_sdk.init(dsn=self.config["SENTRY_DSN"], integrations=[QuartIntegration(), AsyncioIntegration()]) + def get_media_storage_class(app_config: Dict[str, Any], use_provider_config: bool = True) -> Type[MediaStorage]: if use_provider_config and app_config.get("MEDIA_STORAGE_PROVIDER"): @@ -471,7 +486,6 @@ def get_app(config=None, media_storage=None, config_object=None, init_elastic=No app.jinja_loader = custom_loader app.mail = Mail(app) - app.sentry = SuperdeskSentry(app) cache_backend.init_app(app) setup_apm(app) diff --git a/superdesk/factory/sentry.py b/superdesk/factory/sentry.py deleted file mode 100644 index 6187cc7ef2..0000000000 --- a/superdesk/factory/sentry.py +++ /dev/null @@ -1,29 +0,0 @@ -import logging -from raven.contrib.flask import Sentry -from raven.contrib.celery import register_signal, register_logger_signal - - -SENTRY_DSN = "SENTRY_DSN" - - -class SuperdeskSentry: - """Sentry proxy that will do nothing in case sentry is not configured.""" - - def __init__(self, app): - if app.config.get(SENTRY_DSN): - if "verify_ssl" not in app.config[SENTRY_DSN]: - app.config[SENTRY_DSN] += "?verify_ssl=0" - app.config.setdefault("SENTRY_NAME", app.config.get("SERVER_DOMAIN")) - self.sentry = Sentry(app, register_signal=False, wrap_wsgi=False, logging=True, level=logging.WARNING) - register_logger_signal(self.sentry.client) - register_signal(self.sentry.client) - else: - self.sentry = None - - def captureException(self, exc_info=None, **kwargs): - if self.sentry: - self.sentry.captureException(exc_info, **kwargs) - - def captureMessage(self, message, **kwargs): - if self.sentry: - self.sentry.captureMessage(message, **kwargs) diff --git a/tests/sentry_tests.py b/tests/sentry_tests.py deleted file mode 100644 index c5b0cb8a05..0000000000 --- a/tests/sentry_tests.py +++ /dev/null @@ -1,12 +0,0 @@ -import unittest - -from superdesk.flask import Flask -from superdesk.factory.sentry import SuperdeskSentry - - -class SentryTestCase(unittest.TestCase): - def test_sentry_not_configured(self): - app = Flask(__name__) - sentry = SuperdeskSentry(app) - self.assertIsNone(sentry.captureMessage("test")) - self.assertIsNone(sentry.captureException())