10
10
require_relative '../../../../../lib/opentelemetry/instrumentation/redis/patches/redis_v4_client'
11
11
12
12
describe OpenTelemetry ::Instrumentation ::Redis ::Patches ::RedisV4Client do
13
+ # NOTE: These tests should be run for redis v4 and redis v5, even though the patches won't be installed on v5.
14
+ # Perhaps these tests should live in a different file?
13
15
let ( :instrumentation ) { OpenTelemetry ::Instrumentation ::Redis ::Instrumentation . instance }
14
16
let ( :exporter ) { EXPORTER }
15
17
let ( :password ) { 'passw0rd' }
21
23
# will generate one extra span on connect because the Redis client will
22
24
# send an AUTH command before doing anything else.
23
25
def redis_with_auth ( redis_options = { } )
24
- redis_options [ :password ] = password
25
- redis_options [ :host ] = redis_host
26
- redis_options [ :port ] = redis_port
26
+ redis_options [ :password ] || = password
27
+ redis_options [ :host ] || = redis_host
28
+ redis_options [ :port ] || = redis_port
27
29
Redis . new ( redis_options )
28
30
end
29
31
32
+ def redis_version
33
+ Gem . loaded_specs [ 'redis' ] &.version
34
+ end
35
+
36
+ def redis_version_major
37
+ redis_version &.segments &.first
38
+ end
39
+
40
+ def redis_gte_5?
41
+ redis_version_major &.>=( 5 )
42
+ end
43
+
30
44
before do
31
45
# ensure obfuscation is off if it was previously set in a different test
32
46
config = { db_statement : :include }
@@ -95,19 +109,49 @@ def redis_with_auth(redis_options = {})
95
109
end
96
110
97
111
it 'reflects db index' do
112
+ skip if redis_gte_5?
113
+
98
114
redis = redis_with_auth ( db : 1 )
99
115
redis . get ( 'K' )
100
116
101
117
_ ( exporter . finished_spans . size ) . must_equal 3
102
118
103
119
select_span = exporter . finished_spans [ 1 ]
104
120
_ ( select_span . name ) . must_equal 'SELECT'
105
- _ ( select_span . attributes [ 'db.system' ] ) . must_equal 'redis'
106
121
_ ( select_span . attributes [ 'db.statement' ] ) . must_equal ( 'SELECT 1' )
122
+
123
+ get_span = exporter . finished_spans . last
124
+
125
+ _ ( select_span . attributes [ 'db.system' ] ) . must_equal 'redis'
107
126
_ ( select_span . attributes [ 'net.peer.name' ] ) . must_equal redis_host
108
127
_ ( select_span . attributes [ 'net.peer.port' ] ) . must_equal redis_port
128
+ _ ( select_span . attributes [ 'db.redis.database_index' ] ) . must_equal 1
109
129
130
+ _ ( get_span . name ) . must_equal 'GET'
131
+ _ ( get_span . attributes [ 'db.system' ] ) . must_equal 'redis'
132
+ _ ( get_span . attributes [ 'db.statement' ] ) . must_equal ( 'GET K' )
133
+ _ ( get_span . attributes [ 'db.redis.database_index' ] ) . must_equal 1
134
+ _ ( get_span . attributes [ 'net.peer.name' ] ) . must_equal redis_host
135
+ _ ( get_span . attributes [ 'net.peer.port' ] ) . must_equal redis_port
136
+ end
137
+
138
+ it 'reflects db index v5' do
139
+ skip unless redis_gte_5?
140
+
141
+ redis = redis_with_auth ( db : 1 )
142
+ redis . get ( 'K' )
143
+
144
+ _ ( exporter . finished_spans . size ) . must_equal 2
145
+ select_span = exporter . finished_spans . first
110
146
get_span = exporter . finished_spans . last
147
+ _ ( select_span . name ) . must_equal 'PIPELINED'
148
+ _ ( select_span . attributes [ 'db.statement' ] ) . must_equal ( "AUTH ?\n SELECT 1" )
149
+
150
+ _ ( select_span . attributes [ 'db.system' ] ) . must_equal 'redis'
151
+ _ ( select_span . attributes [ 'net.peer.name' ] ) . must_equal redis_host
152
+ _ ( select_span . attributes [ 'net.peer.port' ] ) . must_equal redis_port
153
+ _ ( select_span . attributes [ 'db.redis.database_index' ] ) . must_equal 1
154
+
111
155
_ ( get_span . name ) . must_equal 'GET'
112
156
_ ( get_span . attributes [ 'db.system' ] ) . must_equal 'redis'
113
157
_ ( get_span . attributes [ 'db.statement' ] ) . must_equal ( 'GET K' )
@@ -134,6 +178,8 @@ def redis_with_auth(redis_options = {})
134
178
end
135
179
136
180
it 'records exceptions' do
181
+ skip if redis_gte_5?
182
+
137
183
expect do
138
184
redis = redis_with_auth
139
185
redis . call 'THIS_IS_NOT_A_REDIS_FUNC' , 'THIS_IS_NOT_A_VALID_ARG'
@@ -150,21 +196,68 @@ def redis_with_auth(redis_options = {})
150
196
_ ( last_span . status . code ) . must_equal (
151
197
OpenTelemetry ::Trace ::Status ::ERROR
152
198
)
199
+
153
200
_ ( last_span . status . description . tr ( '`' , "'" ) ) . must_include (
154
201
"ERR unknown command 'THIS_IS_NOT_A_REDIS_FUNC', with args beginning with: 'THIS_IS_NOT_A_VALID_ARG"
155
202
)
156
203
end
157
204
158
- it 'records net.peer.name and net.peer.port attributes' do
205
+ it 'records exceptions v5' do
206
+ skip unless redis_gte_5?
207
+
159
208
expect do
160
- Redis . new ( host : 'example.com' , port : 8321 , timeout : 0.01 ) . auth ( password )
161
- end . must_raise Redis ::CannotConnectError
209
+ redis = redis_with_auth
210
+ redis . call 'THIS_IS_NOT_A_REDIS_FUNC' , 'THIS_IS_NOT_A_VALID_ARG'
211
+ end . must_raise Redis ::CommandError
162
212
163
- _ ( last_span . name ) . must_equal 'AUTH'
213
+ _ ( exporter . finished_spans . size ) . must_equal 2
214
+ _ ( last_span . name ) . must_equal 'THIS_IS_NOT_A_REDIS_FUNC'
164
215
_ ( last_span . attributes [ 'db.system' ] ) . must_equal 'redis'
165
- _ ( last_span . attributes [ 'db.statement' ] ) . must_equal 'AUTH ?'
166
- _ ( last_span . attributes [ 'net.peer.name' ] ) . must_equal 'example.com'
167
- _ ( last_span . attributes [ 'net.peer.port' ] ) . must_equal 8321
216
+ _ ( last_span . attributes [ 'db.statement' ] ) . must_equal (
217
+ 'THIS_IS_NOT_A_REDIS_FUNC THIS_IS_NOT_A_VALID_ARG'
218
+ )
219
+ _ ( last_span . attributes [ 'net.peer.name' ] ) . must_equal redis_host
220
+ _ ( last_span . attributes [ 'net.peer.port' ] ) . must_equal redis_port
221
+ _ ( last_span . status . code ) . must_equal (
222
+ OpenTelemetry ::Trace ::Status ::ERROR
223
+ )
224
+
225
+ _ ( last_span . status . description . tr ( '`' , "'" ) ) . must_include (
226
+ 'Unhandled exception of type: RedisClient::CommandError'
227
+ )
228
+ end
229
+
230
+ it 'records net.peer.name and net.peer.port attributes' do
231
+ skip if redis_gte_5?
232
+
233
+ client = Redis . new ( host : 'example.com' , port : 8321 , timeout : 0.01 )
234
+ expect { client . auth ( password ) } . must_raise Redis ::CannotConnectError
235
+
236
+ if redis_gte_5?
237
+ skip (
238
+ 'Redis 5 is a wrapper around RedisClient, which calls' \
239
+ '`ensure_connected` before any of the middlewares are invoked.' \
240
+ 'This is more appropriately instrumented via a `#connect` hook in the middleware.'
241
+ )
242
+ else
243
+ _ ( last_span . name ) . must_equal 'AUTH'
244
+ _ ( last_span . attributes [ 'db.system' ] ) . must_equal 'redis'
245
+ _ ( last_span . attributes [ 'db.statement' ] ) . must_equal 'AUTH ?'
246
+ _ ( last_span . attributes [ 'net.peer.name' ] ) . must_equal 'example.com'
247
+ _ ( last_span . attributes [ 'net.peer.port' ] ) . must_equal 8321
248
+ end
249
+ end
250
+
251
+ it 'records net.peer.name and net.peer.port attributes v5' do
252
+ skip unless redis_gte_5?
253
+
254
+ client = Redis . new ( host : 'example.com' , port : 8321 , timeout : 0.01 )
255
+ expect { client . auth ( password ) } . must_raise Redis ::CannotConnectError
256
+
257
+ # NOTE: Redis 5 is a wrapper around RedisClient, which calls
258
+ # ensure_connected` before any of the middlewares are invoked, so we don't
259
+ # have a span here. This is more appropriately instrumented via a `#connect`
260
+ # hook in the middleware.
168
261
end
169
262
170
263
it 'traces pipelined commands' do
@@ -185,10 +278,11 @@ def redis_with_auth(redis_options = {})
185
278
186
279
it 'traces pipelined commands on commit' do
187
280
redis = redis_with_auth
188
- redis . queue ( [ :set , 'v1' , '0' ] )
189
- redis . queue ( [ :incr , 'v1' ] )
190
- redis . queue ( [ :get , 'v1' ] )
191
- redis . commit
281
+ redis . pipelined do |pipeline |
282
+ pipeline . set ( 'v1' , '0' )
283
+ pipeline . incr ( 'v1' )
284
+ pipeline . get ( 'v1' )
285
+ end
192
286
193
287
_ ( exporter . finished_spans . size ) . must_equal 2
194
288
_ ( last_span . name ) . must_equal 'PIPELINED'
@@ -225,16 +319,17 @@ def redis_with_auth(redis_options = {})
225
319
it 'truncates long db.statements' do
226
320
redis = redis_with_auth
227
321
the_long_value = 'y' * 100
228
- redis . queue ( [ :set , 'v1' , the_long_value ] )
229
- redis . queue ( [ :set , 'v1' , the_long_value ] )
230
- redis . queue ( [ :set , 'v1' , the_long_value ] )
231
- redis . queue ( [ :set , 'v1' , the_long_value ] )
232
- redis . queue ( [ :set , 'v1' , the_long_value ] )
233
- redis . queue ( [ :set , 'v1' , the_long_value ] )
234
- redis . queue ( [ :set , 'v1' , the_long_value ] )
235
- redis . queue ( [ :set , 'v1' , the_long_value ] )
236
- redis . queue ( [ :set , 'v1' , the_long_value ] )
237
- redis . commit
322
+ redis . pipelined do |pipeline |
323
+ pipeline . set ( 'v1' , the_long_value )
324
+ pipeline . set ( 'v1' , the_long_value )
325
+ pipeline . set ( 'v1' , the_long_value )
326
+ pipeline . set ( 'v1' , the_long_value )
327
+ pipeline . set ( 'v1' , the_long_value )
328
+ pipeline . set ( 'v1' , the_long_value )
329
+ pipeline . set ( 'v1' , the_long_value )
330
+ pipeline . set ( 'v1' , the_long_value )
331
+ pipeline . set ( 'v1' , the_long_value )
332
+ end
238
333
239
334
expected_db_statement = <<~HEREDOC . chomp
240
335
SET v1 yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
@@ -291,13 +386,39 @@ def redis_with_auth(redis_options = {})
291
386
end
292
387
293
388
it 'omits db.statement attribute' do
389
+ skip if redis_gte_5?
390
+
391
+ redis = redis_with_auth
392
+ _ ( redis . set ( 'K' , 'xyz' ) ) . must_equal 'OK'
393
+ _ ( redis . get ( 'K' ) ) . must_equal 'xyz'
394
+ _ ( exporter . finished_spans . size ) . must_equal 3
395
+
396
+ set_span = exporter . finished_spans [ 0 ]
397
+ _ ( set_span . name ) . must_equal ( 'AUTH' )
398
+ _ ( set_span . attributes [ 'db.system' ] ) . must_equal 'redis'
399
+ _ ( set_span . attributes ) . wont_include ( 'db.statement' )
400
+
401
+ set_span = exporter . finished_spans [ 1 ]
402
+ _ ( set_span . name ) . must_equal 'SET'
403
+ _ ( set_span . attributes [ 'db.system' ] ) . must_equal 'redis'
404
+ _ ( set_span . attributes ) . wont_include ( 'db.statement' )
405
+
406
+ set_span = exporter . finished_spans [ 2 ]
407
+ _ ( set_span . name ) . must_equal 'GET'
408
+ _ ( set_span . attributes [ 'db.system' ] ) . must_equal 'redis'
409
+ _ ( set_span . attributes ) . wont_include ( 'db.statement' )
410
+ end
411
+
412
+ it 'omits db.statement attribute v5' do
413
+ skip unless redis_gte_5?
414
+
294
415
redis = redis_with_auth
295
416
_ ( redis . set ( 'K' , 'xyz' ) ) . must_equal 'OK'
296
417
_ ( redis . get ( 'K' ) ) . must_equal 'xyz'
297
418
_ ( exporter . finished_spans . size ) . must_equal 3
298
419
299
420
set_span = exporter . finished_spans [ 0 ]
300
- _ ( set_span . name ) . must_equal 'AUTH'
421
+ _ ( set_span . name ) . must_equal ( 'PIPELINED' )
301
422
_ ( set_span . attributes [ 'db.system' ] ) . must_equal 'redis'
302
423
_ ( set_span . attributes ) . wont_include ( 'db.statement' )
303
424
@@ -320,13 +441,45 @@ def redis_with_auth(redis_options = {})
320
441
end
321
442
322
443
it 'obfuscates arguments in db.statement' do
444
+ skip if redis_gte_5?
445
+
446
+ redis = redis_with_auth
447
+ _ ( redis . set ( 'K' , 'xyz' ) ) . must_equal 'OK'
448
+ _ ( redis . get ( 'K' ) ) . must_equal 'xyz'
449
+ _ ( exporter . finished_spans . size ) . must_equal 3
450
+
451
+ set_span = exporter . finished_spans [ 0 ]
452
+ _ ( set_span . name ) . must_equal ( 'AUTH' )
453
+ _ ( set_span . attributes [ 'db.system' ] ) . must_equal 'redis'
454
+ _ ( set_span . attributes [ 'db.statement' ] ) . must_equal (
455
+ 'AUTH ?'
456
+ )
457
+
458
+ set_span = exporter . finished_spans [ 1 ]
459
+ _ ( set_span . name ) . must_equal 'SET'
460
+ _ ( set_span . attributes [ 'db.system' ] ) . must_equal 'redis'
461
+ _ ( set_span . attributes [ 'db.statement' ] ) . must_equal (
462
+ 'SET ? ?'
463
+ )
464
+
465
+ set_span = exporter . finished_spans [ 2 ]
466
+ _ ( set_span . name ) . must_equal 'GET'
467
+ _ ( set_span . attributes [ 'db.system' ] ) . must_equal 'redis'
468
+ _ ( set_span . attributes [ 'db.statement' ] ) . must_equal (
469
+ 'GET ?'
470
+ )
471
+ end
472
+
473
+ it 'obfuscates arguments in db.statement v5' do
474
+ skip unless redis_gte_5?
475
+
323
476
redis = redis_with_auth
324
477
_ ( redis . set ( 'K' , 'xyz' ) ) . must_equal 'OK'
325
478
_ ( redis . get ( 'K' ) ) . must_equal 'xyz'
326
479
_ ( exporter . finished_spans . size ) . must_equal 3
327
480
328
481
set_span = exporter . finished_spans [ 0 ]
329
- _ ( set_span . name ) . must_equal 'AUTH'
482
+ _ ( set_span . name ) . must_equal ( 'PIPELINED' )
330
483
_ ( set_span . attributes [ 'db.system' ] ) . must_equal 'redis'
331
484
_ ( set_span . attributes [ 'db.statement' ] ) . must_equal (
332
485
'AUTH ?'
0 commit comments