13
13
)
14
14
15
15
if TYPE_CHECKING :
16
- from typing import Any , Dict , Sequence
16
+ from typing import Any , Dict , Sequence , Callable
17
17
from sentry_sdk .tracing import Span
18
18
19
19
_SINGLE_KEY_COMMANDS = frozenset (
@@ -83,8 +83,7 @@ def _set_pipeline_data(
83
83
):
84
84
# type: (Span, bool, Any, bool, Sequence[Any]) -> None
85
85
span .set_tag ("redis.is_cluster" , is_cluster )
86
- transaction = is_transaction if not is_cluster else False
87
- span .set_tag ("redis.transaction" , transaction )
86
+ span .set_tag ("redis.transaction" , is_transaction )
88
87
89
88
commands = []
90
89
for i , arg in enumerate (command_stack ):
@@ -118,7 +117,7 @@ def _set_client_data(span, is_cluster, name, *args):
118
117
span .set_tag ("redis.key" , args [0 ])
119
118
120
119
121
- def _set_db_data (span , connection_params ):
120
+ def _set_db_data_on_span (span , connection_params ):
122
121
# type: (Span, Dict[str, Any]) -> None
123
122
span .set_data (SPANDATA .DB_SYSTEM , "redis" )
124
123
@@ -135,8 +134,34 @@ def _set_db_data(span, connection_params):
135
134
span .set_data (SPANDATA .SERVER_PORT , port )
136
135
137
136
138
- def patch_redis_pipeline (pipeline_cls , is_cluster , get_command_args_fn ):
139
- # type: (Any, bool, Any) -> None
137
+ def _set_db_data (span , redis_instance ):
138
+ # type: (Span, Any) -> None
139
+ _set_db_data_on_span (span , redis_instance .connection_pool .connection_kwargs )
140
+
141
+
142
+ def _set_cluster_db_data (span , redis_cluster_instance ):
143
+ # type: (Span, Any) -> None
144
+ default_node = redis_cluster_instance .get_default_node ()
145
+ if default_node :
146
+ _set_db_data_on_span (
147
+ span , {"host" : default_node .host , "port" : default_node .port }
148
+ )
149
+
150
+
151
+ def _set_async_cluster_db_data (span , async_redis_cluster_instance ):
152
+ # type: (Span, Any) -> None
153
+ default_node = async_redis_cluster_instance .get_default_node ()
154
+ if default_node and default_node .connection_kwargs :
155
+ _set_db_data_on_span (span , default_node .connection_kwargs )
156
+
157
+
158
+ def _set_async_cluster_pipeline_db_data (span , async_redis_cluster_pipeline_instance ):
159
+ # type: (Span, Any) -> None
160
+ _set_async_cluster_db_data (span , async_redis_cluster_pipeline_instance ._client )
161
+
162
+
163
+ def patch_redis_pipeline (pipeline_cls , is_cluster , get_command_args_fn , set_db_data_fn ):
164
+ # type: (Any, bool, Any, Callable[[Span, Any], None]) -> None
140
165
old_execute = pipeline_cls .execute
141
166
142
167
def sentry_patched_execute (self , * args , ** kwargs ):
@@ -150,12 +175,12 @@ def sentry_patched_execute(self, *args, **kwargs):
150
175
op = OP .DB_REDIS , description = "redis.pipeline.execute"
151
176
) as span :
152
177
with capture_internal_exceptions ():
153
- _set_db_data (span , self . connection_pool . connection_kwargs )
178
+ set_db_data_fn (span , self )
154
179
_set_pipeline_data (
155
180
span ,
156
181
is_cluster ,
157
182
get_command_args_fn ,
158
- self .transaction ,
183
+ False if is_cluster else self .transaction ,
159
184
self .command_stack ,
160
185
)
161
186
@@ -164,8 +189,8 @@ def sentry_patched_execute(self, *args, **kwargs):
164
189
pipeline_cls .execute = sentry_patched_execute
165
190
166
191
167
- def patch_redis_client (cls , is_cluster ):
168
- # type: (Any, bool) -> None
192
+ def patch_redis_client (cls , is_cluster , set_db_data_fn ):
193
+ # type: (Any, bool, Callable[[Span, Any], None] ) -> None
169
194
"""
170
195
This function can be used to instrument custom redis client classes or
171
196
subclasses.
@@ -189,7 +214,7 @@ def sentry_patched_execute_command(self, name, *args, **kwargs):
189
214
description = description [: integration .max_data_size - len ("..." )] + "..."
190
215
191
216
with hub .start_span (op = OP .DB_REDIS , description = description ) as span :
192
- _set_db_data (span , self . connection_pool . connection_kwargs )
217
+ set_db_data_fn (span , self )
193
218
_set_client_data (span , is_cluster , name , * args )
194
219
195
220
return old_execute_command (self , name , * args , ** kwargs )
@@ -199,14 +224,16 @@ def sentry_patched_execute_command(self, name, *args, **kwargs):
199
224
200
225
def _patch_redis (StrictRedis , client ): # noqa: N803
201
226
# type: (Any, Any) -> None
202
- patch_redis_client (StrictRedis , is_cluster = False )
203
- patch_redis_pipeline (client .Pipeline , False , _get_redis_command_args )
227
+ patch_redis_client (StrictRedis , is_cluster = False , set_db_data_fn = _set_db_data )
228
+ patch_redis_pipeline (client .Pipeline , False , _get_redis_command_args , _set_db_data )
204
229
try :
205
230
strict_pipeline = client .StrictPipeline
206
231
except AttributeError :
207
232
pass
208
233
else :
209
- patch_redis_pipeline (strict_pipeline , False , _get_redis_command_args )
234
+ patch_redis_pipeline (
235
+ strict_pipeline , False , _get_redis_command_args , _set_db_data
236
+ )
210
237
211
238
try :
212
239
import redis .asyncio
@@ -218,8 +245,56 @@ def _patch_redis(StrictRedis, client): # noqa: N803
218
245
patch_redis_async_pipeline ,
219
246
)
220
247
221
- patch_redis_async_client (redis .asyncio .client .StrictRedis )
222
- patch_redis_async_pipeline (redis .asyncio .client .Pipeline )
248
+ patch_redis_async_client (
249
+ redis .asyncio .client .StrictRedis ,
250
+ is_cluster = False ,
251
+ set_db_data_fn = _set_db_data ,
252
+ )
253
+ patch_redis_async_pipeline (
254
+ redis .asyncio .client .Pipeline ,
255
+ False ,
256
+ _get_redis_command_args ,
257
+ set_db_data_fn = _set_db_data ,
258
+ )
259
+
260
+
261
+ def _patch_redis_cluster ():
262
+ # type: () -> None
263
+ """Patches the cluster module on redis SDK (as opposed to rediscluster library)"""
264
+ try :
265
+ from redis import RedisCluster , cluster
266
+ except ImportError :
267
+ pass
268
+ else :
269
+ patch_redis_client (RedisCluster , True , _set_cluster_db_data )
270
+ patch_redis_pipeline (
271
+ cluster .ClusterPipeline ,
272
+ True ,
273
+ _parse_rediscluster_command ,
274
+ _set_cluster_db_data ,
275
+ )
276
+
277
+ try :
278
+ from redis .asyncio import cluster as async_cluster
279
+ except ImportError :
280
+ pass
281
+ else :
282
+ from sentry_sdk .integrations .redis .asyncio import (
283
+ patch_redis_async_client ,
284
+ patch_redis_async_pipeline ,
285
+ )
286
+
287
+ patch_redis_async_client (
288
+ async_cluster .RedisCluster ,
289
+ is_cluster = True ,
290
+ set_db_data_fn = _set_async_cluster_db_data ,
291
+ )
292
+ patch_redis_async_pipeline (
293
+ async_cluster .ClusterPipeline ,
294
+ True ,
295
+ _parse_rediscluster_command ,
296
+ set_db_data_fn = _set_async_cluster_pipeline_db_data ,
297
+ )
223
298
224
299
225
300
def _patch_rb ():
@@ -229,9 +304,15 @@ def _patch_rb():
229
304
except ImportError :
230
305
pass
231
306
else :
232
- patch_redis_client (rb .clients .FanoutClient , is_cluster = False )
233
- patch_redis_client (rb .clients .MappingClient , is_cluster = False )
234
- patch_redis_client (rb .clients .RoutingClient , is_cluster = False )
307
+ patch_redis_client (
308
+ rb .clients .FanoutClient , is_cluster = False , set_db_data_fn = _set_db_data
309
+ )
310
+ patch_redis_client (
311
+ rb .clients .MappingClient , is_cluster = False , set_db_data_fn = _set_db_data
312
+ )
313
+ patch_redis_client (
314
+ rb .clients .RoutingClient , is_cluster = False , set_db_data_fn = _set_db_data
315
+ )
235
316
236
317
237
318
def _patch_rediscluster ():
@@ -241,7 +322,9 @@ def _patch_rediscluster():
241
322
except ImportError :
242
323
return
243
324
244
- patch_redis_client (rediscluster .RedisCluster , is_cluster = True )
325
+ patch_redis_client (
326
+ rediscluster .RedisCluster , is_cluster = True , set_db_data_fn = _set_db_data
327
+ )
245
328
246
329
# up to v1.3.6, __version__ attribute is a tuple
247
330
# from v2.0.0, __version__ is a string and VERSION a tuple
@@ -251,11 +334,17 @@ def _patch_rediscluster():
251
334
# https://github.com/Grokzen/redis-py-cluster/blob/master/docs/release-notes.rst
252
335
if (0 , 2 , 0 ) < version < (2 , 0 , 0 ):
253
336
pipeline_cls = rediscluster .pipeline .StrictClusterPipeline
254
- patch_redis_client (rediscluster .StrictRedisCluster , is_cluster = True )
337
+ patch_redis_client (
338
+ rediscluster .StrictRedisCluster ,
339
+ is_cluster = True ,
340
+ set_db_data_fn = _set_db_data ,
341
+ )
255
342
else :
256
343
pipeline_cls = rediscluster .pipeline .ClusterPipeline
257
344
258
- patch_redis_pipeline (pipeline_cls , True , _parse_rediscluster_command )
345
+ patch_redis_pipeline (
346
+ pipeline_cls , True , _parse_rediscluster_command , set_db_data_fn = _set_db_data
347
+ )
259
348
260
349
261
350
class RedisIntegration (Integration ):
@@ -274,6 +363,7 @@ def setup_once():
274
363
raise DidNotEnable ("Redis client not installed" )
275
364
276
365
_patch_redis (StrictRedis , client )
366
+ _patch_redis_cluster ()
277
367
_patch_rb ()
278
368
279
369
try :
0 commit comments