Skip to content

Commit 949ff5b

Browse files
authored
Fix breadcrumbs in redis (#3680)
This moves the creation of `redis` breadcrumbs from the `maybe_create_breadcrumbs_from_span` into the integrations. And as well fixes some span related tests by using `render_span_tree`.
1 parent e72e62f commit 949ff5b

File tree

13 files changed

+292
-210
lines changed

13 files changed

+292
-210
lines changed

sentry_sdk/integrations/redis/_async_common.py

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
from sentry_sdk.integrations.redis.consts import SPAN_ORIGIN
44
from sentry_sdk.integrations.redis.modules.caches import (
55
_compile_cache_span_properties,
6-
_set_cache_data,
6+
_get_cache_data,
77
)
88
from sentry_sdk.integrations.redis.modules.queries import _compile_db_span_properties
99
from sentry_sdk.integrations.redis.utils import (
10-
_set_client_data,
11-
_set_pipeline_data,
10+
_create_breadcrumb,
11+
_get_client_data,
12+
_get_pipeline_data,
13+
_update_span,
1214
)
1315
from sentry_sdk.tracing import Span
1416
from sentry_sdk.utils import capture_internal_exceptions
@@ -23,9 +25,9 @@
2325

2426

2527
def patch_redis_async_pipeline(
26-
pipeline_cls, is_cluster, get_command_args_fn, set_db_data_fn
28+
pipeline_cls, is_cluster, get_command_args_fn, get_db_data_fn
2729
):
28-
# type: (Union[type[Pipeline[Any]], type[ClusterPipeline[Any]]], bool, Any, Callable[[Span, Any], None]) -> None
30+
# type: (Union[type[Pipeline[Any]], type[ClusterPipeline[Any]]], bool, Any, Callable[[Any], dict[str, Any]]) -> None
2931
old_execute = pipeline_cls.execute
3032

3133
from sentry_sdk.integrations.redis import RedisIntegration
@@ -41,22 +43,25 @@ async def _sentry_execute(self, *args, **kwargs):
4143
origin=SPAN_ORIGIN,
4244
) as span:
4345
with capture_internal_exceptions():
44-
set_db_data_fn(span, self)
45-
_set_pipeline_data(
46-
span,
47-
is_cluster,
48-
get_command_args_fn,
49-
False if is_cluster else self.is_transaction,
50-
self._command_stack if is_cluster else self.command_stack,
46+
span_data = get_db_data_fn(self)
47+
pipeline_data = _get_pipeline_data(
48+
is_cluster=is_cluster,
49+
get_command_args_fn=get_command_args_fn,
50+
is_transaction=False if is_cluster else self.is_transaction,
51+
command_stack=(
52+
self._command_stack if is_cluster else self.command_stack
53+
),
5154
)
55+
_update_span(span, span_data, pipeline_data)
56+
_create_breadcrumb("redis.pipeline.execute", span_data, pipeline_data)
5257

5358
return await old_execute(self, *args, **kwargs)
5459

5560
pipeline_cls.execute = _sentry_execute # type: ignore
5661

5762

58-
def patch_redis_async_client(cls, is_cluster, set_db_data_fn):
59-
# type: (Union[type[StrictRedis[Any]], type[RedisCluster[Any]]], bool, Callable[[Span, Any], None]) -> None
63+
def patch_redis_async_client(cls, is_cluster, get_db_data_fn):
64+
# type: (Union[type[StrictRedis[Any]], type[RedisCluster[Any]]], bool, Callable[[Any], dict[str, Any]]) -> None
6065
old_execute_command = cls.execute_command
6166

6267
from sentry_sdk.integrations.redis import RedisIntegration
@@ -92,15 +97,20 @@ async def _sentry_execute_command(self, name, *args, **kwargs):
9297
)
9398
db_span.__enter__()
9499

95-
set_db_data_fn(db_span, self)
96-
_set_client_data(db_span, is_cluster, name, *args)
100+
db_span_data = get_db_data_fn(self)
101+
db_client_span_data = _get_client_data(is_cluster, name, *args)
102+
_update_span(db_span, db_span_data, db_client_span_data)
103+
_create_breadcrumb(
104+
db_properties["description"], db_span_data, db_client_span_data
105+
)
97106

98107
value = await old_execute_command(self, name, *args, **kwargs)
99108

100109
db_span.__exit__(None, None, None)
101110

102111
if cache_span:
103-
_set_cache_data(cache_span, self, cache_properties, value)
112+
cache_span_data = _get_cache_data(self, cache_properties, value)
113+
_update_span(cache_span, cache_span_data)
104114
cache_span.__exit__(None, None, None)
105115

106116
return value

sentry_sdk/integrations/redis/_sync_common.py

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
from sentry_sdk.integrations.redis.consts import SPAN_ORIGIN
44
from sentry_sdk.integrations.redis.modules.caches import (
55
_compile_cache_span_properties,
6-
_set_cache_data,
6+
_get_cache_data,
77
)
88
from sentry_sdk.integrations.redis.modules.queries import _compile_db_span_properties
99
from sentry_sdk.integrations.redis.utils import (
10-
_set_client_data,
11-
_set_pipeline_data,
10+
_create_breadcrumb,
11+
_get_client_data,
12+
_get_pipeline_data,
13+
_update_span,
1214
)
1315
from sentry_sdk.tracing import Span
1416
from sentry_sdk.utils import capture_internal_exceptions
@@ -24,9 +26,9 @@ def patch_redis_pipeline(
2426
pipeline_cls,
2527
is_cluster,
2628
get_command_args_fn,
27-
set_db_data_fn,
29+
get_db_data_fn,
2830
):
29-
# type: (Any, bool, Any, Callable[[Span, Any], None]) -> None
31+
# type: (Any, bool, Any, Callable[[Any], dict[str, Any]]) -> None
3032
old_execute = pipeline_cls.execute
3133

3234
from sentry_sdk.integrations.redis import RedisIntegration
@@ -42,22 +44,23 @@ def sentry_patched_execute(self, *args, **kwargs):
4244
origin=SPAN_ORIGIN,
4345
) as span:
4446
with capture_internal_exceptions():
45-
set_db_data_fn(span, self)
46-
_set_pipeline_data(
47-
span,
48-
is_cluster,
49-
get_command_args_fn,
50-
False if is_cluster else self.transaction,
51-
self.command_stack,
47+
span_data = get_db_data_fn(self)
48+
pipeline_data = _get_pipeline_data(
49+
is_cluster=is_cluster,
50+
get_command_args_fn=get_command_args_fn,
51+
is_transaction=False if is_cluster else self.transaction,
52+
command_stack=self.command_stack,
5253
)
54+
_update_span(span, span_data, pipeline_data)
55+
_create_breadcrumb("redis.pipeline.execute", span_data, pipeline_data)
5356

5457
return old_execute(self, *args, **kwargs)
5558

5659
pipeline_cls.execute = sentry_patched_execute
5760

5861

59-
def patch_redis_client(cls, is_cluster, set_db_data_fn):
60-
# type: (Any, bool, Callable[[Span, Any], None]) -> None
62+
def patch_redis_client(cls, is_cluster, get_db_data_fn):
63+
# type: (Any, bool, Callable[[Any], dict[str, Any]]) -> None
6164
"""
6265
This function can be used to instrument custom redis client classes or
6366
subclasses.
@@ -97,15 +100,20 @@ def sentry_patched_execute_command(self, name, *args, **kwargs):
97100
)
98101
db_span.__enter__()
99102

100-
set_db_data_fn(db_span, self)
101-
_set_client_data(db_span, is_cluster, name, *args)
103+
db_span_data = get_db_data_fn(self)
104+
db_client_span_data = _get_client_data(is_cluster, name, *args)
105+
_update_span(db_span, db_span_data, db_client_span_data)
106+
_create_breadcrumb(
107+
db_properties["description"], db_span_data, db_client_span_data
108+
)
102109

103110
value = old_execute_command(self, name, *args, **kwargs)
104111

105112
db_span.__exit__(None, None, None)
106113

107114
if cache_span:
108-
_set_cache_data(cache_span, self, cache_properties, value)
115+
cache_span_data = _get_cache_data(self, cache_properties, value)
116+
_update_span(cache_span, cache_span_data)
109117
cache_span.__exit__(None, None, None)
110118

111119
return value

sentry_sdk/integrations/redis/modules/caches.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -75,22 +75,24 @@ def _get_cache_span_description(redis_command, args, kwargs, integration):
7575
return description
7676

7777

78-
def _set_cache_data(span, redis_client, properties, return_value):
79-
# type: (Span, Any, dict[str, Any], Optional[Any]) -> None
78+
def _get_cache_data(redis_client, properties, return_value):
79+
# type: (Any, dict[str, Any], Optional[Any]) -> dict[str, Any]
80+
data = {}
81+
8082
with capture_internal_exceptions():
81-
span.set_data(SPANDATA.CACHE_KEY, properties["key"])
83+
data[SPANDATA.CACHE_KEY] = properties["key"]
8284

8385
if properties["redis_command"] in GET_COMMANDS:
8486
if return_value is not None:
85-
span.set_data(SPANDATA.CACHE_HIT, True)
87+
data[SPANDATA.CACHE_HIT] = True
8688
size = (
8789
len(str(return_value).encode("utf-8"))
8890
if not isinstance(return_value, bytes)
8991
else len(return_value)
9092
)
91-
span.set_data(SPANDATA.CACHE_ITEM_SIZE, size)
93+
data[SPANDATA.CACHE_ITEM_SIZE] = size
9294
else:
93-
span.set_data(SPANDATA.CACHE_HIT, False)
95+
data[SPANDATA.CACHE_HIT] = False
9496

9597
elif properties["redis_command"] in SET_COMMANDS:
9698
if properties["value"] is not None:
@@ -99,7 +101,7 @@ def _set_cache_data(span, redis_client, properties, return_value):
99101
if not isinstance(properties["value"], bytes)
100102
else len(properties["value"])
101103
)
102-
span.set_data(SPANDATA.CACHE_ITEM_SIZE, size)
104+
data[SPANDATA.CACHE_ITEM_SIZE] = size
103105

104106
try:
105107
connection_params = redis_client.connection_pool.connection_kwargs
@@ -114,8 +116,10 @@ def _set_cache_data(span, redis_client, properties, return_value):
114116

115117
host = connection_params.get("host")
116118
if host is not None:
117-
span.set_data(SPANDATA.NETWORK_PEER_ADDRESS, host)
119+
data[SPANDATA.NETWORK_PEER_ADDRESS] = host
118120

119121
port = connection_params.get("port")
120122
if port is not None:
121-
span.set_data(SPANDATA.NETWORK_PEER_PORT, port)
123+
data[SPANDATA.NETWORK_PEER_PORT] = port
124+
125+
return data

sentry_sdk/integrations/redis/modules/queries.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,26 +43,30 @@ def _get_db_span_description(integration, command_name, args):
4343
return description
4444

4545

46-
def _set_db_data_on_span(span, connection_params):
47-
# type: (Span, dict[str, Any]) -> None
48-
span.set_data(SPANDATA.DB_SYSTEM, "redis")
46+
def _get_connection_data(connection_params):
47+
# type: (dict[str, Any]) -> dict[str, Any]
48+
data = {
49+
SPANDATA.DB_SYSTEM: "redis",
50+
}
4951

5052
db = connection_params.get("db")
5153
if db is not None:
52-
span.set_data(SPANDATA.DB_NAME, str(db))
54+
data[SPANDATA.DB_NAME] = str(db)
5355

5456
host = connection_params.get("host")
5557
if host is not None:
56-
span.set_data(SPANDATA.SERVER_ADDRESS, host)
58+
data[SPANDATA.SERVER_ADDRESS] = host
5759

5860
port = connection_params.get("port")
5961
if port is not None:
60-
span.set_data(SPANDATA.SERVER_PORT, port)
62+
data[SPANDATA.SERVER_PORT] = port
63+
64+
return data
6165

6266

63-
def _set_db_data(span, redis_instance):
64-
# type: (Span, Redis[Any]) -> None
67+
def _get_db_data(redis_instance):
68+
# type: (Redis[Any]) -> dict[str, Any]
6569
try:
66-
_set_db_data_on_span(span, redis_instance.connection_pool.connection_kwargs)
70+
return _get_connection_data(redis_instance.connection_pool.connection_kwargs)
6771
except AttributeError:
68-
pass # connections_kwargs may be missing in some cases
72+
return {} # connections_kwargs may be missing in some cases

sentry_sdk/integrations/redis/rb.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"""
66

77
from sentry_sdk.integrations.redis._sync_common import patch_redis_client
8-
from sentry_sdk.integrations.redis.modules.queries import _set_db_data
8+
from sentry_sdk.integrations.redis.modules.queries import _get_db_data
99

1010

1111
def _patch_rb():
@@ -18,15 +18,15 @@ def _patch_rb():
1818
patch_redis_client(
1919
rb.clients.FanoutClient,
2020
is_cluster=False,
21-
set_db_data_fn=_set_db_data,
21+
get_db_data_fn=_get_db_data,
2222
)
2323
patch_redis_client(
2424
rb.clients.MappingClient,
2525
is_cluster=False,
26-
set_db_data_fn=_set_db_data,
26+
get_db_data_fn=_get_db_data,
2727
)
2828
patch_redis_client(
2929
rb.clients.RoutingClient,
3030
is_cluster=False,
31-
set_db_data_fn=_set_db_data,
31+
get_db_data_fn=_get_db_data,
3232
)

sentry_sdk/integrations/redis/redis.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
patch_redis_client,
99
patch_redis_pipeline,
1010
)
11-
from sentry_sdk.integrations.redis.modules.queries import _set_db_data
11+
from sentry_sdk.integrations.redis.modules.queries import _get_db_data
1212

1313
from typing import TYPE_CHECKING
1414

@@ -26,13 +26,13 @@ def _patch_redis(StrictRedis, client): # noqa: N803
2626
patch_redis_client(
2727
StrictRedis,
2828
is_cluster=False,
29-
set_db_data_fn=_set_db_data,
29+
get_db_data_fn=_get_db_data,
3030
)
3131
patch_redis_pipeline(
3232
client.Pipeline,
3333
is_cluster=False,
3434
get_command_args_fn=_get_redis_command_args,
35-
set_db_data_fn=_set_db_data,
35+
get_db_data_fn=_get_db_data,
3636
)
3737
try:
3838
strict_pipeline = client.StrictPipeline
@@ -43,7 +43,7 @@ def _patch_redis(StrictRedis, client): # noqa: N803
4343
strict_pipeline,
4444
is_cluster=False,
4545
get_command_args_fn=_get_redis_command_args,
46-
set_db_data_fn=_set_db_data,
46+
get_db_data_fn=_get_db_data,
4747
)
4848

4949
try:
@@ -59,11 +59,11 @@ def _patch_redis(StrictRedis, client): # noqa: N803
5959
patch_redis_async_client(
6060
redis.asyncio.client.StrictRedis,
6161
is_cluster=False,
62-
set_db_data_fn=_set_db_data,
62+
get_db_data_fn=_get_db_data,
6363
)
6464
patch_redis_async_pipeline(
6565
redis.asyncio.client.Pipeline,
6666
False,
6767
_get_redis_command_args,
68-
set_db_data_fn=_set_db_data,
68+
get_db_data_fn=_get_db_data,
6969
)

0 commit comments

Comments
 (0)