diff --git a/sentry_sdk/integrations/redis/_async_common.py b/sentry_sdk/integrations/redis/_async_common.py index 310d18eba4..8fc3d0c3a9 100644 --- a/sentry_sdk/integrations/redis/_async_common.py +++ b/sentry_sdk/integrations/redis/_async_common.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING import sentry_sdk -from sentry_sdk.consts import OP +from sentry_sdk.consts import OP, SPANDATA from sentry_sdk.integrations.redis.consts import SPAN_ORIGIN from sentry_sdk.integrations.redis.modules.caches import ( _compile_cache_span_properties, @@ -9,6 +9,7 @@ ) from sentry_sdk.integrations.redis.modules.queries import _compile_db_span_properties from sentry_sdk.integrations.redis.utils import ( + _get_safe_command, _set_client_data, _set_pipeline_data, ) @@ -109,6 +110,12 @@ async def _sentry_execute_command( integration, ) + additional_cache_span_attributes = {} + with capture_internal_exceptions(): + additional_cache_span_attributes[SPANDATA.DB_QUERY_TEXT] = ( + _get_safe_command(name, args) + ) + cache_span: "Optional[Union[Span, StreamedSpan]]" = None if cache_properties["is_cache_key"] and cache_properties["op"] is not None: if span_streaming: @@ -117,6 +124,7 @@ async def _sentry_execute_command( attributes={ "sentry.op": cache_properties["op"], "sentry.origin": SPAN_ORIGIN, + **additional_cache_span_attributes, }, ) else: @@ -129,6 +137,12 @@ async def _sentry_execute_command( db_properties = _compile_db_span_properties(integration, name, args) + additional_db_span_attributes = {} + with capture_internal_exceptions(): + additional_db_span_attributes[SPANDATA.DB_QUERY_TEXT] = _get_safe_command( + name, args + ) + db_span: "Union[Span, StreamedSpan]" if span_streaming: db_span = sentry_sdk.traces.start_span( @@ -136,6 +150,7 @@ async def _sentry_execute_command( attributes={ "sentry.op": db_properties["op"], "sentry.origin": SPAN_ORIGIN, + **additional_db_span_attributes, }, ) else: diff --git a/sentry_sdk/integrations/redis/_sync_common.py b/sentry_sdk/integrations/redis/_sync_common.py index 889b602da3..58d686b099 100644 --- a/sentry_sdk/integrations/redis/_sync_common.py +++ b/sentry_sdk/integrations/redis/_sync_common.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING import sentry_sdk -from sentry_sdk.consts import OP +from sentry_sdk.consts import OP, SPANDATA from sentry_sdk.integrations.redis.consts import SPAN_ORIGIN from sentry_sdk.integrations.redis.modules.caches import ( _compile_cache_span_properties, @@ -9,6 +9,7 @@ ) from sentry_sdk.integrations.redis.modules.queries import _compile_db_span_properties from sentry_sdk.integrations.redis.utils import ( + _get_safe_command, _set_client_data, _set_pipeline_data, ) @@ -108,6 +109,12 @@ def sentry_patched_execute_command( integration, ) + additional_cache_span_attributes = {} + with capture_internal_exceptions(): + additional_cache_span_attributes[SPANDATA.DB_QUERY_TEXT] = ( + _get_safe_command(name, args) + ) + cache_span: "Optional[Union[Span, StreamedSpan]]" = None if cache_properties["is_cache_key"] and cache_properties["op"] is not None: if span_streaming: @@ -116,6 +123,7 @@ def sentry_patched_execute_command( attributes={ "sentry.op": cache_properties["op"], "sentry.origin": SPAN_ORIGIN, + **additional_cache_span_attributes, }, ) else: @@ -128,6 +136,12 @@ def sentry_patched_execute_command( db_properties = _compile_db_span_properties(integration, name, args) + additional_db_span_attributes = {} + with capture_internal_exceptions(): + additional_db_span_attributes[SPANDATA.DB_QUERY_TEXT] = _get_safe_command( + name, args + ) + db_span: "Union[Span, StreamedSpan]" if span_streaming: db_span = sentry_sdk.traces.start_span( @@ -135,6 +149,7 @@ def sentry_patched_execute_command( attributes={ "sentry.op": db_properties["op"], "sentry.origin": SPAN_ORIGIN, + **additional_db_span_attributes, }, ) else: diff --git a/tests/integrations/redis/test_redis.py b/tests/integrations/redis/test_redis.py index b5c83f9412..6d6db159a5 100644 --- a/tests/integrations/redis/test_redis.py +++ b/tests/integrations/redis/test_redis.py @@ -139,6 +139,7 @@ def test_sensitive_data(sentry_init, capture_events, capture_items, span_streami assert parent_span["name"] == "custom parent" assert redis_span["name"] == "GET [Filtered]" + assert redis_span["attributes"][SPANDATA.DB_QUERY_TEXT] == "GET [Filtered]" assert redis_span["attributes"]["sentry.op"] == "db.redis" else: events = capture_events() @@ -177,8 +178,10 @@ def test_pii_data_redacted(sentry_init, capture_events, capture_items, span_stre assert parent["name"] == "custom parent" assert set1["name"] == "SET 'somekey1' [Filtered]" + assert set1["attributes"][SPANDATA.DB_QUERY_TEXT] == "SET 'somekey1' [Filtered]" assert set1["attributes"]["sentry.op"] == "db.redis" assert set2["name"] == "SET 'somekey2' [Filtered]" + assert set2["attributes"][SPANDATA.DB_QUERY_TEXT] == "SET 'somekey2' [Filtered]" assert get["name"] == "GET 'somekey2'" assert delete["name"] == "DEL 'somekey1' [Filtered]" else: @@ -223,8 +226,16 @@ def test_pii_data_sent(sentry_init, capture_events, capture_items, span_streamin assert parent["name"] == "custom parent" assert set1["name"] == "SET 'somekey1' 'my secret string1'" + assert ( + set1["attributes"][SPANDATA.DB_QUERY_TEXT] + == "SET 'somekey1' 'my secret string1'" + ) assert set1["attributes"]["sentry.op"] == "db.redis" assert set2["name"] == "SET 'somekey2' 'my secret string2'" + assert ( + set2["attributes"][SPANDATA.DB_QUERY_TEXT] + == "SET 'somekey2' 'my secret string2'" + ) assert get["name"] == "GET 'somekey2'" assert delete["name"] == "DEL 'somekey1' 'somekey2'" else: @@ -271,8 +282,16 @@ def test_no_data_truncation_by_default( assert parent["name"] == "custom parent" assert set1["name"] == f"SET 'somekey1' '{long_string}'" + assert ( + set1["attributes"][SPANDATA.DB_QUERY_TEXT] + == f"SET 'somekey1' '{long_string}'" + ) assert set1["attributes"]["sentry.op"] == "db.redis" assert set2["name"] == f"SET 'somekey2' '{short_string}'" + assert ( + set2["attributes"][SPANDATA.DB_QUERY_TEXT] + == f"SET 'somekey2' '{short_string}'" + ) else: events = capture_events() with start_transaction(): @@ -317,8 +336,16 @@ def test_data_truncation_custom( assert parent["name"] == "custom parent" assert set1["name"] == expected_long + assert ( + set1["attributes"][SPANDATA.DB_QUERY_TEXT] + == f"SET 'somekey1' '{long_string}'" + ) assert set1["attributes"]["sentry.op"] == "db.redis" assert set2["name"] == expected_short + assert ( + set2["attributes"][SPANDATA.DB_QUERY_TEXT] + == f"SET 'somekey2' '{short_string}'" + ) else: events = capture_events() with start_transaction(): @@ -401,6 +428,7 @@ def test_db_connection_attributes_client( assert redis_span["name"] == "GET 'foobar'" attrs = redis_span["attributes"] assert attrs["sentry.op"] == "db.redis" + assert attrs[SPANDATA.DB_QUERY_TEXT] == "GET 'foobar'" assert attrs[SPANDATA.DB_SYSTEM_NAME] == "redis" assert attrs[SPANDATA.DB_DRIVER_NAME] == "redis-py" assert attrs[SPANDATA.DB_NAMESPACE] == "1" @@ -508,6 +536,9 @@ def test_span_origin(sentry_init, capture_events, capture_items, span_streaming) assert parent_span["name"] == "custom parent" assert parent_span["attributes"]["sentry.origin"] == "manual" assert set_span["attributes"]["sentry.origin"] == "auto.db.redis" + assert ( + set_span["attributes"][SPANDATA.DB_QUERY_TEXT] == "SET 'somekey' [Filtered]" + ) assert pipeline_span["attributes"]["sentry.origin"] == "auto.db.redis" else: events = capture_events() diff --git a/tests/integrations/redis/test_redis_cache_module.py b/tests/integrations/redis/test_redis_cache_module.py index 5036cf7b48..a48393f088 100644 --- a/tests/integrations/redis/test_redis_cache_module.py +++ b/tests/integrations/redis/test_redis_cache_module.py @@ -83,21 +83,34 @@ def test_cache_basic(sentry_init, capture_events, capture_items, span_streaming) assert payloads[1]["attributes"]["sentry.op"] == "db.redis" assert payloads[1]["attributes"][SPANDATA.DB_OPERATION_NAME] == "GET" assert payloads[2]["attributes"]["sentry.op"] == "cache.get" + assert payloads[2]["attributes"][SPANDATA.DB_QUERY_TEXT] == "GET 'mycachekey'" # set: db then cache.put assert payloads[3]["attributes"]["sentry.op"] == "db.redis" assert payloads[3]["attributes"][SPANDATA.DB_OPERATION_NAME] == "SET" assert payloads[4]["attributes"]["sentry.op"] == "cache.put" + assert ( + payloads[4]["attributes"][SPANDATA.DB_QUERY_TEXT] + == "SET 'mycachekey1' [Filtered]" + ) # setex: db then cache.put assert payloads[5]["attributes"]["sentry.op"] == "db.redis" assert payloads[5]["attributes"][SPANDATA.DB_OPERATION_NAME] == "SETEX" assert payloads[6]["attributes"]["sentry.op"] == "cache.put" + assert ( + payloads[6]["attributes"][SPANDATA.DB_QUERY_TEXT] + == "SETEX 'mycachekey2' [Filtered] [Filtered]" + ) # mget: db then cache.get assert payloads[7]["attributes"]["sentry.op"] == "db.redis" assert payloads[7]["attributes"][SPANDATA.DB_OPERATION_NAME] == "MGET" assert payloads[8]["attributes"]["sentry.op"] == "cache.get" + assert ( + payloads[8]["attributes"][SPANDATA.DB_QUERY_TEXT] + == "MGET 'mycachekey1' [Filtered]" + ) assert payloads[9]["name"] == "custom parent" else: @@ -169,12 +182,14 @@ def test_cache_keys(sentry_init, capture_events, capture_items, span_streaming): assert payloads[1]["name"] == "GET 'blub'" assert payloads[2]["attributes"]["sentry.op"] == "cache.get" assert payloads[2]["name"] == "blub" + assert payloads[2]["attributes"][SPANDATA.DB_QUERY_TEXT] == "GET 'blub'" # blubkeything: db then cache.get assert payloads[3]["attributes"]["sentry.op"] == "db.redis" assert payloads[3]["name"] == "GET 'blubkeything'" assert payloads[4]["attributes"]["sentry.op"] == "cache.get" assert payloads[4]["name"] == "blubkeything" + assert payloads[4]["attributes"][SPANDATA.DB_QUERY_TEXT] == "GET 'blubkeything'" # bl: db only (no prefix match) assert payloads[5]["attributes"]["sentry.op"] == "db.redis" diff --git a/tests/integrations/redis/test_redis_cache_module_async.py b/tests/integrations/redis/test_redis_cache_module_async.py index 02cda07c34..9ea86662b3 100644 --- a/tests/integrations/redis/test_redis_cache_module_async.py +++ b/tests/integrations/redis/test_redis_cache_module_async.py @@ -15,6 +15,7 @@ ) import sentry_sdk +from sentry_sdk.consts import SPANDATA from sentry_sdk.integrations.redis import RedisIntegration from sentry_sdk.utils import parse_version @@ -83,6 +84,7 @@ async def test_cache_basic(sentry_init, capture_events, capture_items, span_stre assert parent_span["name"] == "custom parent" assert db_span["attributes"]["sentry.op"] == "db.redis" assert cache_span["attributes"]["sentry.op"] == "cache.get" + assert cache_span["attributes"][SPANDATA.CACHE_KEY] == ["myasynccachekey"] else: events = capture_events() with sentry_sdk.start_transaction(): @@ -132,12 +134,14 @@ async def test_cache_keys(sentry_init, capture_events, capture_items, span_strea assert payloads[1]["name"] == "GET 'ablub'" assert payloads[2]["attributes"]["sentry.op"] == "cache.get" assert payloads[2]["name"] == "ablub" + assert payloads[2]["attributes"][SPANDATA.CACHE_KEY] == ["ablub"] # ablubkeything: db then cache.get assert payloads[3]["attributes"]["sentry.op"] == "db.redis" assert payloads[3]["name"] == "GET 'ablubkeything'" assert payloads[4]["attributes"]["sentry.op"] == "cache.get" assert payloads[4]["name"] == "ablubkeything" + assert payloads[4]["attributes"][SPANDATA.CACHE_KEY] == ["ablubkeything"] # abl: db only (no prefix match) assert payloads[5]["attributes"]["sentry.op"] == "db.redis"