diff --git a/sentry_sdk/integrations/redis.py b/sentry_sdk/integrations/redis.py index c947be36da..0df6121a54 100644 --- a/sentry_sdk/integrations/redis.py +++ b/sentry_sdk/integrations/redis.py @@ -1,7 +1,7 @@ from __future__ import absolute_import from sentry_sdk import Hub -from sentry_sdk.utils import capture_internal_exceptions +from sentry_sdk.utils import capture_internal_exceptions, logger from sentry_sdk.integrations import Integration from sentry_sdk._types import MYPY @@ -15,6 +15,25 @@ _MULTI_KEY_COMMANDS = frozenset(["del", "touch", "unlink"]) +def _patch_rediscluster(): + # type: () -> None + try: + import rediscluster # type: ignore + except ImportError: + return + + patch_redis_client(rediscluster.RedisCluster) + + # up to v1.3.6, __version__ attribute is a tuple + # from v2.0.0, __version__ is a string and VERSION a tuple + version = getattr(rediscluster, "VERSION", rediscluster.__version__) + + # StrictRedisCluster was introduced in v0.2.0 and removed in v2.0.0 + # https://github.com/Grokzen/redis-py-cluster/blob/master/docs/release-notes.rst + if (0, 2, 0) < version < (2, 0, 0): + patch_redis_client(rediscluster.StrictRedisCluster) + + class RedisIntegration(Integration): identifier = "redis" @@ -34,6 +53,11 @@ def setup_once(): patch_redis_client(rb.clients.MappingClient) patch_redis_client(rb.clients.RoutingClient) + try: + _patch_rediscluster() + except Exception: + logger.exception("Error occured while patching `rediscluster` library") + def patch_redis_client(cls): # type: (Any) -> None diff --git a/tests/integrations/rediscluster/__init__.py b/tests/integrations/rediscluster/__init__.py new file mode 100644 index 0000000000..b292f63ec8 --- /dev/null +++ b/tests/integrations/rediscluster/__init__.py @@ -0,0 +1,3 @@ +import pytest + +pytest.importorskip("rediscluster") diff --git a/tests/integrations/rediscluster/test_rediscluster.py b/tests/integrations/rediscluster/test_rediscluster.py new file mode 100644 index 0000000000..c3fad38315 --- /dev/null +++ b/tests/integrations/rediscluster/test_rediscluster.py @@ -0,0 +1,37 @@ +import pytest +from sentry_sdk import capture_message +from sentry_sdk.integrations.redis import RedisIntegration + +import rediscluster + +rediscluster_classes = [rediscluster.RedisCluster] + +if hasattr(rediscluster, "StrictRedisCluster"): + rediscluster_classes.append(rediscluster.StrictRedisCluster) + + +@pytest.fixture(scope="module", autouse=True) +def monkeypatch_rediscluster_classes(): + for cls in rediscluster_classes: + cls.execute_command = lambda *_, **__: None + + +@pytest.mark.parametrize("rediscluster_cls", rediscluster_classes) +def test_rediscluster_basic(rediscluster_cls, sentry_init, capture_events): + sentry_init(integrations=[RedisIntegration()]) + events = capture_events() + + rc = rediscluster_cls(connection_pool=True) + rc.get("foobar") + capture_message("hi") + + (event,) = events + (crumb,) = event["breadcrumbs"] + + assert crumb == { + "category": "redis", + "message": "GET 'foobar'", + "data": {"redis.key": "foobar", "redis.command": "GET"}, + "timestamp": crumb["timestamp"], + "type": "redis", + } diff --git a/tox.ini b/tox.ini index ece251d7aa..8e3989499e 100644 --- a/tox.ini +++ b/tox.ini @@ -62,6 +62,7 @@ envlist = {py2.7,py3.8}-requests {py2.7,py3.7,py3.8}-redis + {py2.7,py3.7,py3.8}-rediscluster-{1,2} py{3.7,3.8}-asgi @@ -166,8 +167,9 @@ deps = trytond-4.6: trytond>=4.6,<4.7 redis: fakeredis - # https://github.com/jamesls/fakeredis/issues/245 - redis: redis<3.2.2 + + rediscluster-1: redis-py-cluster>=1.0.0,<2.0.0 + rediscluster-2: redis-py-cluster>=2.0.0,<3.0.0 asgi: starlette asgi: requests @@ -199,6 +201,7 @@ setenv = tornado: TESTPATH=tests/integrations/tornado trytond: TESTPATH=tests/integrations/trytond redis: TESTPATH=tests/integrations/redis + rediscluster: TESTPATH=tests/integrations/rediscluster asgi: TESTPATH=tests/integrations/asgi sqlalchemy: TESTPATH=tests/integrations/sqlalchemy spark: TESTPATH=tests/integrations/spark