38
38
client = redis.StrictRedis(host="localhost", port=6379)
39
39
client.get("my-key")
40
40
41
+ The `instrument` method accepts the following keyword args:
42
+
43
+ tracer_provider (TracerProvider) - an optional tracer provider
44
+
45
+ request_hook (Callable) - a function with extra user-defined logic to be performed before performing the request
46
+ this function signature is: def request_hook(span: Span, instance: redis.connection.Connection, args, kwargs) -> None
47
+
48
+ response_hook (Callable) - a function with extra user-defined logic to be performed after performing the request
49
+ this function signature is: def response_hook(span: Span, instance: redis.connection.Connection, response) -> None
50
+
51
+ for example:
52
+
53
+ .. code: python
54
+
55
+ from opentelemetry.instrumentation.redis import RedisInstrumentor
56
+ import redis
57
+
58
+ def request_hook(span, instance, args, kwargs):
59
+ if span and span.is_recording():
60
+ span.set_attribute("custom_user_attribute_from_request_hook", "some-value")
61
+
62
+ def response_hook(span, instance, response):
63
+ if span and span.is_recording():
64
+ span.set_attribute("custom_user_attribute_from_response_hook", "some-value")
65
+
66
+ # Instrument redis with hooks
67
+ RedisInstrumentor().instrument(request_hook=request_hook, response_hook=response_hook)
68
+
69
+ # This will report a span with the default settings and the custom attributes added from the hooks
70
+ client = redis.StrictRedis(host="localhost", port=6379)
71
+ client.get("my-key")
72
+
41
73
API
42
74
---
43
75
"""
44
-
45
- from typing import Collection
76
+ import typing
77
+ from typing import Any , Collection
46
78
47
79
import redis
48
80
from wrapt import wrap_function_wrapper
57
89
from opentelemetry .instrumentation .redis .version import __version__
58
90
from opentelemetry .instrumentation .utils import unwrap
59
91
from opentelemetry .semconv .trace import SpanAttributes
92
+ from opentelemetry .trace import Span
60
93
61
94
_DEFAULT_SERVICE = "redis"
62
95
96
+ _RequestHookT = typing .Optional [
97
+ typing .Callable [
98
+ [Span , redis .connection .Connection , typing .List , typing .Dict ], None
99
+ ]
100
+ ]
101
+ _ResponseHookT = typing .Optional [
102
+ typing .Callable [[Span , redis .connection .Connection , Any ], None ]
103
+ ]
104
+
63
105
64
106
def _set_connection_attributes (span , conn ):
65
107
if not span .is_recording ():
@@ -70,42 +112,68 @@ def _set_connection_attributes(span, conn):
70
112
span .set_attribute (key , value )
71
113
72
114
73
- def _traced_execute_command (func , instance , args , kwargs ):
74
- tracer = getattr (redis , "_opentelemetry_tracer" )
75
- query = _format_command_args (args )
76
- name = ""
77
- if len (args ) > 0 and args [0 ]:
78
- name = args [0 ]
79
- else :
80
- name = instance .connection_pool .connection_kwargs .get ("db" , 0 )
81
- with tracer .start_as_current_span (
82
- name , kind = trace .SpanKind .CLIENT
83
- ) as span :
84
- if span .is_recording ():
85
- span .set_attribute (SpanAttributes .DB_STATEMENT , query )
86
- _set_connection_attributes (span , instance )
87
- span .set_attribute ("db.redis.args_length" , len (args ))
88
- return func (* args , ** kwargs )
89
-
90
-
91
- def _traced_execute_pipeline (func , instance , args , kwargs ):
92
- tracer = getattr (redis , "_opentelemetry_tracer" )
93
-
94
- cmds = [_format_command_args (c ) for c , _ in instance .command_stack ]
95
- resource = "\n " .join (cmds )
96
-
97
- span_name = " " .join ([args [0 ] for args , _ in instance .command_stack ])
98
-
99
- with tracer .start_as_current_span (
100
- span_name , kind = trace .SpanKind .CLIENT
101
- ) as span :
102
- if span .is_recording ():
103
- span .set_attribute (SpanAttributes .DB_STATEMENT , resource )
104
- _set_connection_attributes (span , instance )
105
- span .set_attribute (
106
- "db.redis.pipeline_length" , len (instance .command_stack )
107
- )
108
- return func (* args , ** kwargs )
115
+ def _instrument (
116
+ tracer ,
117
+ request_hook : _RequestHookT = None ,
118
+ response_hook : _ResponseHookT = None ,
119
+ ):
120
+ def _traced_execute_command (func , instance , args , kwargs ):
121
+ query = _format_command_args (args )
122
+ name = ""
123
+ if len (args ) > 0 and args [0 ]:
124
+ name = args [0 ]
125
+ else :
126
+ name = instance .connection_pool .connection_kwargs .get ("db" , 0 )
127
+ with tracer .start_as_current_span (
128
+ name , kind = trace .SpanKind .CLIENT
129
+ ) as span :
130
+ if span .is_recording ():
131
+ span .set_attribute (SpanAttributes .DB_STATEMENT , query )
132
+ _set_connection_attributes (span , instance )
133
+ span .set_attribute ("db.redis.args_length" , len (args ))
134
+ if callable (request_hook ):
135
+ request_hook (span , instance , args , kwargs )
136
+ response = func (* args , ** kwargs )
137
+ if callable (response_hook ):
138
+ response_hook (span , instance , response )
139
+ return response
140
+
141
+ def _traced_execute_pipeline (func , instance , args , kwargs ):
142
+ cmds = [_format_command_args (c ) for c , _ in instance .command_stack ]
143
+ resource = "\n " .join (cmds )
144
+
145
+ span_name = " " .join ([args [0 ] for args , _ in instance .command_stack ])
146
+
147
+ with tracer .start_as_current_span (
148
+ span_name , kind = trace .SpanKind .CLIENT
149
+ ) as span :
150
+ if span .is_recording ():
151
+ span .set_attribute (SpanAttributes .DB_STATEMENT , resource )
152
+ _set_connection_attributes (span , instance )
153
+ span .set_attribute (
154
+ "db.redis.pipeline_length" , len (instance .command_stack )
155
+ )
156
+ response = func (* args , ** kwargs )
157
+ if callable (response_hook ):
158
+ response_hook (span , instance , response )
159
+ return response
160
+
161
+ pipeline_class = (
162
+ "BasePipeline" if redis .VERSION < (3 , 0 , 0 ) else "Pipeline"
163
+ )
164
+ redis_class = "StrictRedis" if redis .VERSION < (3 , 0 , 0 ) else "Redis"
165
+
166
+ wrap_function_wrapper (
167
+ "redis" , f"{ redis_class } .execute_command" , _traced_execute_command
168
+ )
169
+ wrap_function_wrapper (
170
+ "redis.client" , f"{ pipeline_class } .execute" , _traced_execute_pipeline ,
171
+ )
172
+ wrap_function_wrapper (
173
+ "redis.client" ,
174
+ f"{ pipeline_class } .immediate_execute_command" ,
175
+ _traced_execute_command ,
176
+ )
109
177
110
178
111
179
class RedisInstrumentor (BaseInstrumentor ):
@@ -117,41 +185,22 @@ def instrumentation_dependencies(self) -> Collection[str]:
117
185
return _instruments
118
186
119
187
def _instrument (self , ** kwargs ):
188
+ """Instruments the redis module
189
+
190
+ Args:
191
+ **kwargs: Optional arguments
192
+ ``tracer_provider``: a TracerProvider, defaults to global.
193
+ ``response_hook``: An optional callback which is invoked right before the span is finished processing a response.
194
+ """
120
195
tracer_provider = kwargs .get ("tracer_provider" )
121
- setattr (
122
- redis ,
123
- "_opentelemetry_tracer" ,
124
- trace .get_tracer (
125
- __name__ , __version__ , tracer_provider = tracer_provider ,
126
- ),
196
+ tracer = trace .get_tracer (
197
+ __name__ , __version__ , tracer_provider = tracer_provider
198
+ )
199
+ _instrument (
200
+ tracer ,
201
+ request_hook = kwargs .get ("request_hook" ),
202
+ response_hook = kwargs .get ("response_hook" ),
127
203
)
128
-
129
- if redis .VERSION < (3 , 0 , 0 ):
130
- wrap_function_wrapper (
131
- "redis" , "StrictRedis.execute_command" , _traced_execute_command
132
- )
133
- wrap_function_wrapper (
134
- "redis.client" ,
135
- "BasePipeline.execute" ,
136
- _traced_execute_pipeline ,
137
- )
138
- wrap_function_wrapper (
139
- "redis.client" ,
140
- "BasePipeline.immediate_execute_command" ,
141
- _traced_execute_command ,
142
- )
143
- else :
144
- wrap_function_wrapper (
145
- "redis" , "Redis.execute_command" , _traced_execute_command
146
- )
147
- wrap_function_wrapper (
148
- "redis.client" , "Pipeline.execute" , _traced_execute_pipeline
149
- )
150
- wrap_function_wrapper (
151
- "redis.client" ,
152
- "Pipeline.immediate_execute_command" ,
153
- _traced_execute_command ,
154
- )
155
204
156
205
def _uninstrument (self , ** kwargs ):
157
206
if redis .VERSION < (3 , 0 , 0 ):
0 commit comments