14
14
15
15
import re
16
16
17
- from newrelic .api .datastore_trace import DatastoreTrace
17
+ from newrelic .api .datastore_trace import DatastoreTrace , DatastoreTraceWrapper , wrap_datastore_trace
18
18
from newrelic .api .time_trace import current_trace
19
19
from newrelic .api .transaction import current_transaction
20
- from newrelic .common .object_wrapper import function_wrapper , wrap_function_wrapper
20
+ from newrelic .common .object_wrapper import wrap_function_wrapper
21
+ from newrelic .common .async_wrapper import coroutine_wrapper , async_generator_wrapper , generator_wrapper
21
22
22
23
_redis_client_sync_methods = {
23
24
"acl_dryrun" ,
136
137
"client_no_evict" ,
137
138
"client_pause" ,
138
139
"client_reply" ,
140
+ "client_setinfo" ,
139
141
"client_setname" ,
140
142
"client_tracking" ,
141
143
"client_trackinginfo" ,
162
164
"cluster_reset" ,
163
165
"cluster_save_config" ,
164
166
"cluster_set_config_epoch" ,
165
- "client_setinfo" ,
166
167
"cluster_setslot" ,
167
168
"cluster_slaves" ,
168
169
"cluster_slots" ,
248
249
"hmset_dict" ,
249
250
"hmset" ,
250
251
"hrandfield" ,
251
- "hscan_inter " ,
252
+ "hscan_iter " ,
252
253
"hscan" ,
253
254
"hset" ,
254
255
"hsetnx" ,
399
400
"syndump" ,
400
401
"synupdate" ,
401
402
"tagvals" ,
402
- "tfcall" ,
403
403
"tfcall_async" ,
404
+ "tfcall" ,
404
405
"tfunction_delete" ,
405
406
"tfunction_list" ,
406
407
"tfunction_load" ,
473
474
"zunionstore" ,
474
475
}
475
476
477
+ _redis_client_gen_methods = {
478
+ "scan_iter" ,
479
+ "hscan_iter" ,
480
+ "sscan_iter" ,
481
+ "zscan_iter" ,
482
+ }
483
+
476
484
_redis_client_methods = _redis_client_sync_methods .union (_redis_client_async_methods )
477
485
478
486
_redis_multipart_commands = set (["client" , "cluster" , "command" , "config" , "debug" , "sentinel" , "slowlog" , "script" ])
@@ -498,50 +506,31 @@ def _instance_info(kwargs):
498
506
499
507
500
508
def _wrap_Redis_method_wrapper_ (module , instance_class_name , operation ):
501
- def _nr_wrapper_Redis_method_ (wrapped , instance , args , kwargs ):
502
- transaction = current_transaction ()
503
-
504
- if transaction is None :
505
- return wrapped (* args , ** kwargs )
506
-
507
- dt = DatastoreTrace (product = "Redis" , target = None , operation = operation , source = wrapped )
508
-
509
- transaction ._nr_datastore_instance_info = (None , None , None )
510
-
511
- with dt :
512
- result = wrapped (* args , ** kwargs )
513
-
514
- host , port_path_or_id , db = transaction ._nr_datastore_instance_info
515
- dt .host = host
516
- dt .port_path_or_id = port_path_or_id
517
- dt .database_name = db
518
-
519
- return result
520
-
521
509
name = "%s.%s" % (instance_class_name , operation )
522
- wrap_function_wrapper (module , name , _nr_wrapper_Redis_method_ )
510
+ if operation in _redis_client_gen_methods :
511
+ async_wrapper = generator_wrapper
512
+ else :
513
+ async_wrapper = None
523
514
515
+ wrap_datastore_trace (module , name , product = "Redis" , target = None , operation = operation , async_wrapper = async_wrapper )
524
516
525
- def _wrap_asyncio_Redis_method_wrapper (module , instance_class_name , operation ):
526
- @function_wrapper
527
- async def _nr_wrapper_asyncio_Redis_async_method_ (wrapped , instance , args , kwargs ):
528
- transaction = current_transaction ()
529
- if transaction is None :
530
- return await wrapped (* args , ** kwargs )
531
-
532
- with DatastoreTrace (product = "Redis" , target = None , operation = operation ):
533
- return await wrapped (* args , ** kwargs )
534
517
518
+ def _wrap_asyncio_Redis_method_wrapper (module , instance_class_name , operation ):
535
519
def _nr_wrapper_asyncio_Redis_method_ (wrapped , instance , args , kwargs ):
536
520
from redis .asyncio .client import Pipeline
537
521
538
522
if isinstance (instance , Pipeline ):
539
523
return wrapped (* args , ** kwargs )
540
524
541
- # Method should be run when awaited, therefore we wrap in an async wrapper.
542
- return _nr_wrapper_asyncio_Redis_async_method_ (wrapped )(* args , ** kwargs )
525
+ # Method should be run when awaited or iterated , therefore we wrap in an async wrapper.
526
+ return DatastoreTraceWrapper (wrapped , product = "Redis" , target = None , operation = operation , async_wrapper = async_wrapper )(* args , ** kwargs )
543
527
544
528
name = "%s.%s" % (instance_class_name , operation )
529
+ if operation in _redis_client_gen_methods :
530
+ async_wrapper = async_generator_wrapper
531
+ else :
532
+ async_wrapper = coroutine_wrapper
533
+
545
534
wrap_function_wrapper (module , name , _nr_wrapper_asyncio_Redis_method_ )
546
535
547
536
@@ -614,7 +603,15 @@ def _nr_Connection_send_command_wrapper_(wrapped, instance, args, kwargs):
614
603
except :
615
604
pass
616
605
617
- transaction ._nr_datastore_instance_info = (host , port_path_or_id , db )
606
+ # Find DatastoreTrace no matter how many other traces are inbetween
607
+ trace = current_trace ()
608
+ while trace is not None and not isinstance (trace , DatastoreTrace ):
609
+ trace = getattr (trace , "parent" , None )
610
+
611
+ if trace is not None :
612
+ trace .host = host
613
+ trace .port_path_or_id = port_path_or_id
614
+ trace .database_name = db
618
615
619
616
# Older Redis clients would when sending multi part commands pass
620
617
# them in as separate arguments to send_command(). Need to therefore
@@ -666,7 +663,6 @@ def instrument_asyncio_redis_client(module):
666
663
if hasattr (class_ , operation ):
667
664
_wrap_asyncio_Redis_method_wrapper (module , "Redis" , operation )
668
665
669
-
670
666
def instrument_redis_commands_core (module ):
671
667
_instrument_redis_commands_module (module , "CoreCommands" )
672
668
0 commit comments