From 2b1eb041ebb8d56c10fe443420ad6205c7986c34 Mon Sep 17 00:00:00 2001 From: Zacharias Zacharodimos Date: Thu, 12 Oct 2023 16:27:15 +0200 Subject: [PATCH 1/2] global: remove support for raven --- invenio_logging/config.py | 29 +---- invenio_logging/sentry.py | 72 +---------- invenio_logging/sentry6.py | 35 ------ setup.cfg | 6 +- tests/test_sentry.py | 237 ++----------------------------------- 5 files changed, 13 insertions(+), 366 deletions(-) delete mode 100644 invenio_logging/sentry6.py diff --git a/invenio_logging/config.py b/invenio_logging/config.py index 5253ae4..9d13bf8 100644 --- a/invenio_logging/config.py +++ b/invenio_logging/config.py @@ -11,7 +11,7 @@ Sentry can, in addition to the configuration variables listed, be further configured with the folllowing configuration variables (see -`Raven `_ +`Sentry `_ for further details): - ``SENTRY_AUTO_LOG_STACKS`` @@ -20,21 +20,10 @@ - ``SENTRY_MAX_LENGTH_LIST`` - ``SENTRY_MAX_LENGTH_STRING`` - ``SENTRY_NAME`` -- ``SENTRY_PROCESSORS`` - ``SENTRY_RELEASE`` - ``SENTRY_SITE_NAME`` - ``SENTRY_TAGS`` -- ``SENTRY_TRANSPORT`` - -.. note:: - - Celery does not deal well with the threaded Sentry transport, so you should - make sure that your **Celery workers** are configured with: - - .. code-block:: python - - SENTRY_TRANSPORT = 'raven.transport.http.HTTPTransport' """ # ------- @@ -85,9 +74,6 @@ # SENTRY # ------ -SENTRY_SDK = True -"""Use of sentry-python SDK, if false raven will be used. """ - LOGGING_SENTRY_LEVEL = "WARNING" """Sentry logging level. @@ -118,16 +104,3 @@ SENTRY_DSN = None """Set SENTRY_DSN environment variable.""" - -SENTRY_PROCESSORS = ( - "raven.processors.SanitizePasswordsProcessor", - "invenio_logging.sentry.RequestIdProcessor", -) -"""Default Sentry event processors.""" - -SENTRY_TRANSPORT = "raven.transport.http.HTTPTransport" -"""Default Sentry transport. - -Explicitly set due to Celery incompatibility with threaded transport -(see note above). -""" diff --git a/invenio_logging/sentry.py b/invenio_logging/sentry.py index 77e1b22..0c6f372 100644 --- a/invenio_logging/sentry.py +++ b/invenio_logging/sentry.py @@ -11,28 +11,12 @@ from __future__ import absolute_import, print_function import logging -import warnings -import pkg_resources -import six from flask import g -from werkzeug.utils import import_string from . import config from .ext import InvenioLoggingBase -try: - pkg_resources.get_distribution("raven") - from raven.processors import Processor -except pkg_resources.DistributionNotFound: - - class Processor(object): - """Dummy class in case Sentry is not installed..""" - - def __init__(self, *args, **kwargs): - """Do nothing.""" - pass - class InvenioLoggingSentry(InvenioLoggingBase): """Invenio-Logging extension for Sentry.""" @@ -61,7 +45,6 @@ def install_handler(self, app): logging_exclusions = None if not app.config["LOGGING_SENTRY_PYWARNINGS"]: logging_exclusions = ( - "raven", "gunicorn", "south", "sentry.errors", @@ -69,10 +52,8 @@ def install_handler(self, app): "dill", "py.warnings", ) - if app.config["SENTRY_SDK"]: - self.install_sentry_sdk_handler(app, logging_exclusions, level) - else: - self.install_raven_handler(app, logging_exclusions, level) + + self.install_sentry_sdk_handler(app, logging_exclusions, level) # Werkzeug only adds a stream handler if there's no other handlers # defined, so when Sentry adds a log handler no output is @@ -113,42 +94,6 @@ def install_sentry_sdk_handler(self, app, logging_exclusions, level): with configure_scope() as scope: scope.level = level - def install_raven_handler(self, app, logging_exclusions, level): - """Install raven log handler.""" - warnings.warn( - "The Raven library will be depricated.", PendingDeprecationWarning - ) - from raven.contrib.celery import register_logger_signal, register_signal - from raven.contrib.flask import Sentry - from raven.handlers.logging import SentryHandler - - cls = app.config["LOGGING_SENTRY_CLASS"] - if cls: - if isinstance(cls, six.string_types): - cls = import_string(cls) - else: - cls = Sentry - sentry = cls( - app, - logging=True, - level=level, - logging_exclusions=logging_exclusions, - ) - app.logger.addHandler(SentryHandler(client=sentry.client, level=level)) - - # Capture warnings from warnings module - if app.config["LOGGING_SENTRY_PYWARNINGS"]: - self.capture_pywarnings(SentryHandler(sentry.client)) - - # Setup Celery logging to Sentry - if app.config["LOGGING_SENTRY_CELERY"]: - try: - register_logger_signal(sentry.client, loglevel=level) - except TypeError: - # Compatibility mode for Raven<=5.1.0 - register_logger_signal(sentry.client) - register_signal(sentry.client) - def add_request_id_sentry_python(self, event, hint): """Add the request id as a tag.""" if g and hasattr(g, "request_id"): @@ -156,16 +101,3 @@ def add_request_id_sentry_python(self, event, hint): tags.append(["request_id", g.request_id]) event["tags"] = tags return event - - -class RequestIdProcessor(Processor): - """Sentry event request processor for adding the request id as a tag.""" - - def process(self, data, **kwargs): - """Process event data.""" - data = super(RequestIdProcessor, self).process(data, **kwargs) - if g and hasattr(g, "request_id"): - tags = data.get("tags", {}) - tags["request_id"] = g.request_id - data["tags"] = tags - return data diff --git a/invenio_logging/sentry6.py b/invenio_logging/sentry6.py deleted file mode 100644 index b58d564..0000000 --- a/invenio_logging/sentry6.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is part of Invenio. -# Copyright (C) 2015-2018 CERN. -# -# Invenio is free software; you can redistribute it and/or modify it -# under the terms of the MIT License; see LICENSE file for more details. - -"""Compatibility layer for dealing with Sentry 6.x.""" - -from __future__ import absolute_import, print_function - -from flask import current_app -from flask_login import current_user -from raven.contrib.flask import Sentry - - -class Sentry6(Sentry): - """Compatibility layer for Sentry.""" - - def get_user_info(self, request): - """Implement custom getter.""" - if not current_user.is_authenticated: - return {} - - user_info = { - "id": current_user.get_id(), - } - - if "SENTRY_USER_ATTRS" in current_app.config: - for attr in current_app.config["SENTRY_USER_ATTRS"]: - if hasattr(current_user, attr): - user_info[attr] = getattr(current_user, attr) - - return user_info diff --git a/setup.cfg b/setup.cfg index 2887737..0a9addb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -39,13 +39,9 @@ tests = pytest-invenio>=1.4.2 iniconfig>=1.1.1 sphinx>=4.5 - raven[flask]>=6 sentry-sdk[flask]>=1.0.0 # Kept for backwards compatibility -docs = -sentry = - raven[flask]>=6 -sentry-sdk = +sentry_sdk = sentry-sdk[flask]>=1.0.0 [options.entry_points] diff --git a/tests/test_sentry.py b/tests/test_sentry.py index 4341ced..9f5f885 100644 --- a/tests/test_sentry.py +++ b/tests/test_sentry.py @@ -10,16 +10,11 @@ from __future__ import absolute_import, print_function -import logging -import warnings -import httpretty -import pytest -from celery.utils.log import get_task_logger -from flask import Flask, g, request -from flask_celeryext import create_celery_app -from flask_login import LoginManager, UserMixin, login_user -from mock import Mock, patch +from flask import Flask +from sentry_sdk.hub import Hub +from sentry_sdk.integrations.celery import CeleryIntegration +from sentry_sdk.integrations.flask import FlaskIntegration def test_init(): @@ -34,223 +29,9 @@ def test_init(): app.config["SENTRY_DSN"] = "http://user:pw@localhost/0" InvenioLoggingSentry(app) assert "invenio-logging-sentry" in app.extensions - if app.config["SENTRY_SDK"] is False: - assert "sentry" in app.extensions - else: - from sentry_sdk.hub import Hub - from sentry_sdk.integrations.celery import CeleryIntegration - from sentry_sdk.integrations.flask import FlaskIntegration - - assert Hub.current and Hub.client - if app.config["LOGGING_SENTRY_CELERY"]: - assert Hub.current.get_integration(CeleryIntegration) - else: - assert Hub.current.get_integration(FlaskIntegration) - - -def test_custom_class(pywarnlogger): - """Test stream handler.""" - from invenio_logging.sentry import InvenioLoggingSentry - - app = Flask("testapp") - app.config["SENTRY_DSN"] = "http://user:pw@localhost/0" - app.config["LOGGING_SENTRY_CLASS"] = "invenio_logging.sentry6:Sentry6" - app.config["SENTRY_SDK"] = False - InvenioLoggingSentry(app) - from invenio_logging.sentry6 import Sentry6 - - assert isinstance(app.extensions["sentry"], Sentry6) - - -def test_stream_handler_in_debug(pywarnlogger): - """Test stream handler.""" - from invenio_logging.sentry import InvenioLoggingSentry - - app = Flask("testapp") - app.debug = True - app.config["SENTRY_DSN"] = "http://user:pw@localhost/0" - app.config["SENTRY_SDK"] = False - InvenioLoggingSentry(app) - logger = logging.getLogger("werkzeug") - assert logging.StreamHandler in [x.__class__ for x in logger.handlers] - -def test_sentry_handler_attached_to_app_logger(): - from raven.handlers.logging import SentryHandler - - from invenio_logging.sentry import InvenioLoggingSentry - - app = Flask("testapp") - app.config["SENTRY_DSN"] = "http://user:pw@localhost/0" - app.config["SENTRY_SDK"] = False - InvenioLoggingSentry(app) - assert any([isinstance(logger, SentryHandler) for logger in app.logger.handlers]) - - -def test_pywarnings(pywarnlogger): - """Test pywarnings.""" - from invenio_logging.sentry import InvenioLoggingSentry - - app = Flask("testapp") - app.config.update( - dict( - SENTRY_DSN="http://user:pw@localhost/0", - SENTRY_SDK=False, - LOGGING_SENTRY_PYWARNINGS=True, - ) - ) - assert len(pywarnlogger.handlers) == 0 - InvenioLoggingSentry(app) - assert len(pywarnlogger.handlers) == 1 - - -def test_pywarnings_are_logged_once(pywarnlogger, sentry_emit): - """Test pywarnings with logging capturing warnings.""" - from raven.handlers.logging import SentryHandler - - from invenio_logging.sentry import InvenioLoggingSentry - - logging.captureWarnings(True) - app = Flask("testapp") - app.config.update( - dict( - SENTRY_DSN="http://user:pw@localhost/0", - LOGGING_SENTRY_PYWARNINGS=True, - SENTRY_SDK=False, - ) - ) - assert sentry_emit.call_count == 0 - InvenioLoggingSentry(app) - warnings.warn("sentry test warning") - assert sentry_emit.call_count == 1 - - -def test_pywarnings_disabled_are_not_logged(pywarnlogger, sentry_emit): - """Test pywarnings disabled with logging capturing warnings.""" - from raven.handlers.logging import SentryHandler - - from invenio_logging.sentry import InvenioLoggingSentry - - logging.captureWarnings(True) - app = Flask("testapp") - app.config.update( - dict( - SENTRY_DSN="http://user:pw@localhost/0", - LOGGING_SENTRY_PYWARNINGS=False, - SENTRY_SDK=False, - ) - ) - assert sentry_emit.call_count == 0 - InvenioLoggingSentry(app) - warnings.warn("sentry test warning") - assert sentry_emit.call_count == 0 - - -def test_import(): - """Test that raven is not required.""" - with patch("raven.contrib.flask.Sentry") as mod: - mod.side_effect = ImportError - # We can import and install extension with out hitting the import - # unless SENTRY_DSN is set. - from invenio_logging.sentry import InvenioLoggingSentry - - app = Flask("testapp") - InvenioLoggingSentry(app) - - app = Flask("testapp") - app.config["SENTRY_DSN"] = "...." - app.config["SENTRY_SDK"] = False - pytest.raises(ImportError, InvenioLoggingSentry, app) - - -@httpretty.activate -def test_celery(): - """Test celery.""" - from invenio_logging.sentry import InvenioLoggingSentry - - app = Flask("testapp") - app.config.update( - dict( - SENTRY_DSN="http://user:pw@localhost/0", - SENTRY_TRANSPORT="raven.transport.http.HTTPTransport", - SENTRY_SDK=False, - LOGGING_SENTRY_CELERY=True, - CELERY_ALWAYS_EAGER=True, - CELERY_RESULT_BACKEND="cache", - CELERY_CACHE_BACKEND="memory", - CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - ) - ) - InvenioLoggingSentry(app) - celery = create_celery_app(app) - - @celery.task - def test_task(): - logger = get_task_logger(__name__) - logger.error("CELERYTEST") - - httpretty.register_uri( - httpretty.POST, - "http://localhost/api/0/store/", - body='[{"title": "Test"}]', - content_type="application/json", - status=200, - ) - test_task.delay() - # Give sentry time to send request - from time import sleep - - sleep(1) - assert httpretty.last_request().path == "/api/0/store/" - - -def test_sentry6(): - """Test Sentry 6.""" - from invenio_logging.sentry import InvenioLoggingSentry - - app = Flask("testapp") - app.config.update( - dict( - SENTRY_DSN="http://user:pw@localhost/0", - SENTRY_SDK=False, - LOGGING_SENTRY_CLASS="invenio_logging.sentry6:Sentry6", - SENTRY_USER_ATTRS=["name"], - SECRET_KEY="CHANGEME", - ) - ) - InvenioLoggingSentry(app) - login_manager = LoginManager(app) - login_manager.user_loader(lambda a: {}) - - class User(UserMixin): - def __init__(self, user_id, name): - self.id = user_id - self.name = name - - with app.test_request_context("/"): - assert app.extensions["sentry"].get_user_info(request) == {} - - with app.test_request_context("/"): - login_user(User(1, "viggo")) - assert app.extensions["sentry"].get_user_info(request) == { - "id": "1", - "name": "viggo", - } - - -def test_request_id_processors(): - """Test the processor which adds request id to the tags context.""" - from invenio_logging.sentry import RequestIdProcessor - - proc = RequestIdProcessor(Mock()) - # Test without request context - assert proc.process({}) == {} - assert proc.process({"tags": {}}) == ({"tags": {}}) - assert proc.process({"tags": {"myvar": "val"}}) == {"tags": {"myvar": "val"}} - - # Test with request context - app = Flask("testpp") - with app.test_request_context("/"): - assert proc.process({"tags": {}}) == ({"tags": {}}) - g.request_id = "12" - assert proc.process({"tags": {}}) == ({"tags": {"request_id": "12"}}) + assert Hub.current and Hub.client + if app.config["LOGGING_SENTRY_CELERY"]: + assert Hub.current.get_integration(CeleryIntegration) + else: + assert Hub.current.get_integration(FlaskIntegration) From 6e8b97abbf72be83d305484d91cca9fbbd414651 Mon Sep 17 00:00:00 2001 From: Zacharias Zacharodimos Date: Thu, 12 Oct 2023 16:29:29 +0200 Subject: [PATCH 2/2] release: v2.0.0 --- .github/workflows/tests.yml | 10 +++++----- CHANGES.rst | 8 ++------ docs/api.rst | 3 --- docs/conf.py | 2 +- invenio_logging/__init__.py | 2 +- invenio_logging/config.py | 4 +--- setup.cfg | 3 +-- tests/test_sentry.py | 1 - 8 files changed, 11 insertions(+), 22 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5d4b224..acc3b74 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,21 +16,21 @@ on: branches: master schedule: # * is a special character in YAML so you have to quote this string - - cron: '0 3 * * 6' + - cron: "0 3 * * 6" workflow_dispatch: inputs: reason: - description: 'Reason' + description: "Reason" required: false - default: 'Manual trigger' + default: "Manual trigger" jobs: Tests: runs-on: ubuntu-20.04 strategy: matrix: - python-version: [3.7, 3.8, 3.9] - requirements-level: [pypi] + python-version: [3.8, 3.9] + requirements-level: [pypi] env: EXTRAS: tests diff --git a/CHANGES.rst b/CHANGES.rst index 5430467..5c105b3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,13 +8,9 @@ Changes ======= -Version 1.3.4 (released 2023-10-12) +Version 2.0.0 (released 2023-10-12) -- add back in setup sentry and sentry-sdk extras - -Version 1.3.3 (released 2023-10-12) - -- Adds `LOGGING_SENTRY_INIT_KWARGS` to allow extra config on sentry initialization +- removes support for raven Version 1.3.2 (released 2022-02-28) diff --git a/docs/api.rst b/docs/api.rst index 6ff2592..9a48757 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -23,8 +23,5 @@ File System Sentry ------ -.. automodule:: invenio_logging.sentry6 - :members: - .. automodule:: invenio_logging.sentry :members: diff --git a/docs/conf.py b/docs/conf.py index 1deba6f..128b861 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -315,7 +315,7 @@ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { - "https://docs.python.org/": None, + "python": ("https://docs.python.org/", None), "flask": ("https://flask.palletsprojects.com/en/latest/", None), "celery": ("https://docs.celeryproject.org/en/stable/", None), } diff --git a/invenio_logging/__init__.py b/invenio_logging/__init__.py index 01c9ce2..6813b40 100644 --- a/invenio_logging/__init__.py +++ b/invenio_logging/__init__.py @@ -87,6 +87,6 @@ from __future__ import absolute_import, print_function -__version__ = "1.3.4" +__version__ = "2.0.0" __all__ = ("__version__",) diff --git a/invenio_logging/config.py b/invenio_logging/config.py index 9d13bf8..b2d7dd2 100644 --- a/invenio_logging/config.py +++ b/invenio_logging/config.py @@ -95,9 +95,7 @@ LOGGING_SENTRY_CLASS = None """Import path of sentry Flask extension class. -This allows you to customize the Sentry extension class. In particular if you -are logging to Sentry v6, you can set this to -:class:`invenio_logging.sentry6.Sentry6`.""" +This allows you to customize the Sentry extension class.""" LOGGING_SENTRY_INIT_KWARGS = None """Pass extra options when initializing Sentry instance.""" diff --git a/setup.cfg b/setup.cfg index 0a9addb..1f5f97a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -24,7 +24,7 @@ classifiers = [options] include_package_data = True packages = find: -python_requires = >=3.7 +python_requires = >=3.8 zip_safe = False install_requires = invenio-celery>=1.2.4 @@ -40,7 +40,6 @@ tests = iniconfig>=1.1.1 sphinx>=4.5 sentry-sdk[flask]>=1.0.0 -# Kept for backwards compatibility sentry_sdk = sentry-sdk[flask]>=1.0.0 diff --git a/tests/test_sentry.py b/tests/test_sentry.py index 9f5f885..85bd1bc 100644 --- a/tests/test_sentry.py +++ b/tests/test_sentry.py @@ -10,7 +10,6 @@ from __future__ import absolute_import, print_function - from flask import Flask from sentry_sdk.hub import Hub from sentry_sdk.integrations.celery import CeleryIntegration