diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 573a72c9..cb0d7538 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -34,4 +34,4 @@ jobs: - name: Lint with ruff run: | # stop the build if there are Python syntax errors. - ruff check --show-source rq tests + ruff check --output-format=full rq tests diff --git a/docs/patterns/sentry.md b/docs/patterns/sentry.md index 7dab4dc9..8aebe9b9 100644 --- a/docs/patterns/sentry.md +++ b/docs/patterns/sentry.md @@ -1,49 +1,8 @@ --- -title: "RQ: Sending exceptions to Sentry" +title: "RQ: Sending Exceptions to Sentry" layout: patterns --- ## Sending Exceptions to Sentry -[Sentry](https://www.getsentry.com/) is a popular exception gathering service. -RQ allows you to very easily send job exceptions to Sentry. To do this, you'll -need to have [sentry-sdk](https://pypi.org/project/sentry-sdk/) installed. - -There are a few ways to start sending job exceptions to Sentry. - - -### Configuring Sentry Through CLI - -Simply invoke the `rqworker` script using the ``--sentry-dsn`` argument. - -```console -rq worker --sentry-dsn https://my-dsn@sentry.io/123 -``` - - -### Configuring Sentry Through a Config File - -Declare `SENTRY_DSN` in RQ's config file like this: - -```python -SENTRY_DSN = 'https://my-dsn@sentry.io/123' -``` - -And run RQ's worker with your config file: - -```console -rq worker -c my_settings -``` - -Visit [this page](https://python-rq.org/docs/workers/#using-a-config-file) -to read more about running RQ using a config file. - - -### Configuring Sentry Through Environment Variable - -Simple set `RQ_SENTRY_DSN` in your environment variable and RQ will -automatically start Sentry integration for you. - -```console -RQ_SENTRY_DSN="https://my-dsn@sentry.io/123" rq worker -``` +Please visit Sentry's [RQ integration page](https://docs.sentry.io/platforms/python/integrations/rq/). \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 22eb03a7..aee524fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,7 +85,6 @@ dependencies = [ "pytest", "pytest-cov", "ruff", - "sentry-sdk<2", "tox", ] [tool.hatch.envs.test.scripts] @@ -99,7 +98,7 @@ skip-string-normalization = true [tool.ruff] # Set what ruff should check for. # See https://beta.ruff.rs/docs/rules/ for a list of rules. -select = [ +lint.select = [ "E", # pycodestyle errors "F", # pyflakes errors "I", # import sorting @@ -108,7 +107,7 @@ select = [ line-length = 120 # To match black. target-version = "py38" -[tool.ruff.isort] +[tool.ruff.lint.isort] known-first-party = ["rq"] section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"] diff --git a/rq/cli/cli.py b/rq/cli/cli.py index 6b32a571..d9fcf56e 100755 --- a/rq/cli/cli.py +++ b/rq/cli/cli.py @@ -182,9 +182,6 @@ def info(cli_config, interval, raw, only_queues, only_workers, by_queue, queues, @click.option('--disable-job-desc-logging', is_flag=True, help='Turn off description logging.') @click.option('--verbose', '-v', is_flag=True, help='Show more output') @click.option('--quiet', '-q', is_flag=True, help='Show less output') -@click.option('--sentry-ca-certs', envvar='RQ_SENTRY_CA_CERTS', help='Path to CRT file for Sentry DSN') -@click.option('--sentry-debug', envvar='RQ_SENTRY_DEBUG', help='Enable debug') -@click.option('--sentry-dsn', envvar='RQ_SENTRY_DSN', help='Report exceptions to this Sentry DSN') @click.option('--exception-handler', help='Exception handler(s) to use', multiple=True) @click.option('--pid', help='Write the process ID number to a file at the specified path') @click.option('--disable-default-exception-handler', '-d', is_flag=True, help='Disable RQ\'s default exception handler') @@ -209,9 +206,6 @@ def worker( disable_job_desc_logging, verbose, quiet, - sentry_ca_certs, - sentry_debug, - sentry_dsn, exception_handler, pid, disable_default_exception_handler, @@ -229,9 +223,6 @@ def worker( settings = read_config_file(cli_config.config) if cli_config.config else {} # Worker specific default arguments queues = queues or settings.get('QUEUES', ['default']) - sentry_ca_certs = sentry_ca_certs or settings.get('SENTRY_CA_CERTS') - sentry_debug = sentry_debug or settings.get('SENTRY_DEBUG') - sentry_dsn = sentry_dsn or settings.get('SENTRY_DSN') name = name or settings.get('NAME') dict_config = settings.get('DICT_CONFIG') @@ -288,13 +279,6 @@ def worker( serializer=serializer, ) - # Should we configure Sentry? - if sentry_dsn: - sentry_opts = {"ca_certs": sentry_ca_certs, "debug": sentry_debug} - from rq.contrib.sentry import register_sentry - - register_sentry(sentry_dsn, **sentry_opts) - # if --verbose or --quiet, override --logging_level if verbose or quiet: logging_level = None @@ -445,9 +429,6 @@ def enqueue( @main.command() @click.option('--burst', '-b', is_flag=True, help='Run in burst mode (quit after all work is done)') @click.option('--logging-level', '-l', type=str, default="INFO", help='Set logging level') -@click.option('--sentry-ca-certs', envvar='RQ_SENTRY_CA_CERTS', help='Path to CRT file for Sentry DSN') -@click.option('--sentry-debug', envvar='RQ_SENTRY_DEBUG', help='Enable debug') -@click.option('--sentry-dsn', envvar='RQ_SENTRY_DSN', help='Report exceptions to this Sentry DSN') @click.option('--verbose', '-v', is_flag=True, help='Show more output') @click.option('--quiet', '-q', is_flag=True, help='Show less output') @click.option('--log-format', type=str, default=DEFAULT_LOGGING_FORMAT, help='Set the format of the logs') @@ -462,9 +443,6 @@ def worker_pool( logging_level, queues, serializer, - sentry_ca_certs, - sentry_debug, - sentry_dsn, verbose, quiet, log_format, @@ -478,9 +456,6 @@ def worker_pool( settings = read_config_file(cli_config.config) if cli_config.config else {} # Worker specific default arguments queue_names: List[str] = queues or settings.get('QUEUES', ['default']) - sentry_ca_certs = sentry_ca_certs or settings.get('SENTRY_CA_CERTS') - sentry_debug = sentry_debug or settings.get('SENTRY_DEBUG') - sentry_dsn = sentry_dsn or settings.get('SENTRY_DSN') setup_loghandlers_from_args(verbose, quiet, date_format, log_format) @@ -513,13 +488,6 @@ def worker_pool( ) pool.start(burst=burst, logging_level=logging_level) - # Should we configure Sentry? - if sentry_dsn: - sentry_opts = {"ca_certs": sentry_ca_certs, "debug": sentry_debug} - from rq.contrib.sentry import register_sentry - - register_sentry(sentry_dsn, **sentry_opts) - if __name__ == '__main__': main() diff --git a/rq/contrib/legacy.py b/rq/contrib/legacy.py deleted file mode 100644 index 9362d697..00000000 --- a/rq/contrib/legacy.py +++ /dev/null @@ -1,23 +0,0 @@ -import logging - -from rq import Worker, get_current_connection - -logger = logging.getLogger(__name__) - - -def cleanup_ghosts(conn=None, worker_class=Worker): - """ - RQ versions < 0.3.6 suffered from a race condition where workers, when - abruptly terminated, did not have a chance to clean up their worker - registration, leading to reports of ghosted workers in `rqinfo`. Since - 0.3.6, new worker registrations automatically expire, and the worker will - make sure to refresh the registrations as long as it's alive. - - This function will clean up any of such legacy ghosted workers. - """ - conn = conn if conn else get_current_connection() - for worker in worker_class.all(connection=conn): - if conn.ttl(worker.key) == -1: - ttl = worker.worker_ttl - conn.expire(worker.key, ttl) - logger.info('Marked ghosted worker {0} to expire in {1} seconds.'.format(worker.name, ttl)) diff --git a/rq/contrib/sentry.py b/rq/contrib/sentry.py deleted file mode 100644 index efb55b2f..00000000 --- a/rq/contrib/sentry.py +++ /dev/null @@ -1,8 +0,0 @@ -def register_sentry(sentry_dsn, **opts): - """Given a Raven client and an RQ worker, registers exception handlers - with the worker so exceptions are logged to Sentry. - """ - import sentry_sdk - from sentry_sdk.integrations.rq import RqIntegration - - sentry_sdk.init(sentry_dsn, integrations=[RqIntegration()], **opts) diff --git a/rq/maintenance.py b/rq/maintenance.py index 52ed4749..a77aac3a 100644 --- a/rq/maintenance.py +++ b/rq/maintenance.py @@ -1,5 +1,4 @@ import warnings - from typing import TYPE_CHECKING from .intermediate_queue import IntermediateQueue diff --git a/tests/config_files/sentry.py b/tests/config_files/sentry.py deleted file mode 100644 index 163d3055..00000000 --- a/tests/config_files/sentry.py +++ /dev/null @@ -1,2 +0,0 @@ -REDIS_HOST = "testhost.example.com" -SENTRY_DSN = 'https://123@sentry.io/123' diff --git a/tests/test_sentry.py b/tests/test_sentry.py deleted file mode 100644 index 834a0157..00000000 --- a/tests/test_sentry.py +++ /dev/null @@ -1,54 +0,0 @@ -from unittest import mock - -from click.testing import CliRunner - -from rq import Queue -from rq.cli import main -from rq.cli.helpers import read_config_file -from rq.contrib.sentry import register_sentry -from rq.worker import SimpleWorker -from tests import RQTestCase -from tests.fixtures import div_by_zero - - -class FakeSentry: - servers = [] - - def captureException(self, *args, **kwds): # noqa - pass # we cannot check this, because worker forks - - -class TestSentry(RQTestCase): - def setUp(self): - super().setUp() - db_num = self.connection.connection_pool.connection_kwargs['db'] - self.redis_url = 'redis://127.0.0.1:6379/%d' % db_num - - def test_reading_dsn_from_file(self): - settings = read_config_file('tests.config_files.sentry') - self.assertIn('SENTRY_DSN', settings) - self.assertEqual(settings['SENTRY_DSN'], 'https://123@sentry.io/123') - - @mock.patch('rq.contrib.sentry.register_sentry') - def test_cli_flag(self, mocked): - """rq worker -u -b --exception-handler """ - # connection = Redis.from_url(self.redis_url) - runner = CliRunner() - runner.invoke(main, ['worker', '-u', self.redis_url, '-b', '--sentry-dsn', 'https://1@sentry.io/1']) - self.assertEqual(mocked.call_count, 1) - - runner.invoke(main, ['worker-pool', '-u', self.redis_url, '-b', '--sentry-dsn', 'https://1@sentry.io/1']) - self.assertEqual(mocked.call_count, 2) - - def test_failure_capture(self): - """Test failure is captured by Sentry SDK""" - from sentry_sdk import Hub - - hub = Hub.current - self.assertIsNone(hub.last_event_id()) - queue = Queue(connection=self.connection) - queue.enqueue(div_by_zero) - worker = SimpleWorker(queues=[queue], connection=self.connection) - register_sentry('https://123@sentry.io/123') - worker.work(burst=True) - self.assertIsNotNone(hub.last_event_id()) diff --git a/tox.ini b/tox.ini index f6ce6f94..66fbbabe 100644 --- a/tox.ini +++ b/tox.ini @@ -8,7 +8,6 @@ deps= psutil pytest pytest-cov - sentry-sdk passenv= RUN_SSL_TESTS @@ -51,7 +50,6 @@ skipdist = True basepython = python3.10 deps= pytest - sentry-sdk psutil passenv= RUN_SSL_TESTS