solarwinds_apm 5.1.3 → 5.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +9 -2
- data/CHANGELOG.md +14 -0
- data/README.md +4 -2
- data/ext/oboe_metal/extconf.rb +18 -5
- data/ext/oboe_metal/lib/liboboe-1.0-aarch64.so.0.0.0.sha256 +1 -0
- data/ext/oboe_metal/lib/liboboe-1.0-alpine-aarch64.so.0.0.0.sha256 +1 -0
- data/ext/oboe_metal/lib/liboboe-1.0-alpine-x86_64.so.0.0.0.sha256 +1 -1
- data/ext/oboe_metal/lib/liboboe-1.0-x86_64.so.0.0.0.sha256 +1 -1
- data/ext/oboe_metal/src/VERSION +1 -1
- data/lib/solarwinds_apm/inst/redis.rb +36 -29
- data/lib/solarwinds_apm/inst/redis_v4.rb +273 -0
- data/lib/solarwinds_apm/util.rb +116 -42
- data/lib/solarwinds_apm/version.rb +1 -1
- data/solarwinds_apm.gemspec +4 -2
- metadata +5 -13
- data/.github/CODEOWNERS +0 -1
- data/.github/ISSUE_TEMPLATE/bug-or-feature-request.md +0 -16
- data/.github/workflows/build_and_release_gem.yml +0 -108
- data/.github/workflows/build_for_packagecloud.yml +0 -59
- data/.github/workflows/docker-images.yml +0 -53
- data/.github/workflows/run_cpluplus_tests.yml +0 -73
- data/.github/workflows/run_tests.yml +0 -154
- data/.github/workflows/scripts/test_install.rb +0 -28
- data/.github/workflows/swig/swig-v4.0.2.tar.gz +0 -0
- data/.github/workflows/test_on_4_linux.yml +0 -109
- data/.github/workflows/test_on_ubuntu.yml +0 -107
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b56699604e8448a606d69ed23ba957622c4b70d8d2cbcf070526105f2dcff87
|
4
|
+
data.tar.gz: 5b0f55643f2ea9072418c66c62a6f9ea4ca87fea74fbcec66574a341e0510c82
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f77f2c0e8315832f92204bc0cdd29d6588f83cbf2751f8dfac2fb0402c65091c96238f0192da4473b99a0cf5197b44a66839f7d1c58fb5521bd11ce1a96defc6
|
7
|
+
data.tar.gz: 53c8e3740a5361fa4f9e403cf863f2044d821a58a7c42ac8708c859781c363b168af78e39da50b2ceeda783b752205cf0c0cb5bdf86afa7ffa4e63e80ae4dffe
|
data/.gitignore
CHANGED
@@ -41,11 +41,18 @@ gemfiles/vendor*
|
|
41
41
|
lib/libsolarwinds_apm.so
|
42
42
|
vendor/
|
43
43
|
|
44
|
-
# test
|
44
|
+
# test & test script
|
45
45
|
tmp.rb
|
46
46
|
apm.collector.st-ssp.solarwinds.com
|
47
47
|
test/run_tests/.ruby_version_ubuntu
|
48
|
+
test/run_tests/.ruby_version_alpine
|
49
|
+
test/run_tests/.ruby_version_centos
|
50
|
+
test/run_tests/.ruby_version_ubuntu
|
51
|
+
|
52
|
+
redis-test.*
|
48
53
|
|
54
|
+
# compiling
|
55
|
+
ext/oboe_metal/verify
|
49
56
|
|
50
57
|
# mac DS_Store
|
51
|
-
.DS_Store
|
58
|
+
.DS_Store
|
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,20 @@ https://github.com/solarwindscloud/solarwinds-apm-ruby/releases
|
|
3
3
|
|
4
4
|
Dates in this file are in the format MM/DD/YYYY.
|
5
5
|
|
6
|
+
# solarwinds_apm 5.1.4 (11/23/2022)
|
7
|
+
|
8
|
+
This release includes the following features:
|
9
|
+
|
10
|
+
* Update to the latest redis-rb gem (> 5.x)
|
11
|
+
* Update latest liboboe library (11.1.0)
|
12
|
+
* Start to support solarwinds-apm-ruby arm64/aarch64
|
13
|
+
* Init message update for swo/nh backends
|
14
|
+
|
15
|
+
Pushed to Rubygems:
|
16
|
+
|
17
|
+
https://rubygems.org/gems/solarwinds_apm/versions/5.1.4
|
18
|
+
|
19
|
+
|
6
20
|
# solarwinds_apm 5.1.0 (09/15/2022)
|
7
21
|
|
8
22
|
This release includes the following features:
|
data/README.md
CHANGED
@@ -11,7 +11,8 @@ It requires an [Solarwinds] account to view metrics. Get yours,
|
|
11
11
|
|
12
12
|
[![Gem Version](https://badge.fury.io/rb/solarwinds_apm.svg)](https://badge.fury.io/rb/solarwinds_apm)
|
13
13
|
|
14
|
-
[![Run all Tests](https://github.com/
|
14
|
+
[![Run all Tests](https://github.com/solarwindscloud/solarwinds-apm-ruby/actions/workflows/test_on_4_linux.yml/badge.svg)](https://github.com/solarwindscloud/solarwinds-apm-ruby/actions/workflows/test_on_4_linux.yml)
|
15
|
+
|
15
16
|
[![C++ Tests](https://github.com/appoptics/appoptics-apm-ruby/actions/workflows/run_cpluplus_tests.yml/badge.svg)](https://github.com/appoptics/appoptics-apm-ruby/actions/workflows/run_cpluplus_tests.yml)
|
16
17
|
|
17
18
|
[comment]: <> ([![Maintainability](https://api.codeclimate.com/v1/badges/ac7f36241a23a3a82fc5/maintainability)](https://codeclimate.com/github/appoptics/appoptics-apm-ruby/maintainability))
|
@@ -363,7 +364,7 @@ To make this simpler, we've included a few rake tasks to automate this process:
|
|
363
364
|
|
364
365
|
```bash
|
365
366
|
rake clean # make sure no old stuff is around
|
366
|
-
rake
|
367
|
+
rake fetch_oboe_file["stg"] # download c-files from staging
|
367
368
|
rake compile # Build the gem's c extension
|
368
369
|
```
|
369
370
|
|
@@ -381,3 +382,4 @@ See the README in the test directory.
|
|
381
382
|
Copyright (c) 2018 SolarWinds, LLC
|
382
383
|
|
383
384
|
Released under the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
385
|
+
|
data/ext/oboe_metal/extconf.rb
CHANGED
@@ -27,26 +27,39 @@ ao_include = File.join(ext_dir, 'src')
|
|
27
27
|
|
28
28
|
# Download the appropriate liboboe from Staging or Production
|
29
29
|
version = File.read(File.join(ao_include, 'VERSION')).strip
|
30
|
-
if ENV['
|
30
|
+
if ENV['OBOE_DEV'].to_s.downcase == 'true'
|
31
|
+
ao_path = "https://solarwinds-apm-staging.s3.us-west-2.amazonaws.com/apm/c-lib/nightly"
|
32
|
+
puts 'Fetching c-lib from DEVELOPMENT Build'
|
33
|
+
elsif ENV['OBOE_STAGING'].to_s.downcase == 'true'
|
31
34
|
ao_path = File.join('https://agent-binaries.global.st-ssp.solarwinds.com/apm/c-lib/', version)
|
32
35
|
puts 'Fetching c-lib from STAGING'
|
33
36
|
else
|
34
37
|
ao_path = File.join('https://agent-binaries.cloud.solarwinds.com/apm/c-lib/', version)
|
35
38
|
end
|
36
39
|
|
37
|
-
ao_arch =
|
40
|
+
ao_arch = "x86_64"
|
41
|
+
system_arch = `uname -m` # for mac, the command is `uname` # "Darwin\n"; try `uname -a`
|
42
|
+
case system_arch.gsub("\n","")
|
43
|
+
when "x86_64"
|
44
|
+
ao_arch = "x86_64"
|
45
|
+
when "aarch64"
|
46
|
+
ao_arch = "aarch64"
|
47
|
+
end
|
48
|
+
|
38
49
|
if File.exist?('/etc/alpine-release')
|
39
50
|
version = File.read('/etc/alpine-release').strip
|
40
51
|
|
52
|
+
tmp_ao_arch = ao_arch.clone
|
41
53
|
ao_arch =
|
42
54
|
if Gem::Version.new(version) < Gem::Version.new('3.9')
|
43
|
-
|
55
|
+
"alpine-libressl-#{tmp_ao_arch}"
|
44
56
|
else # openssl
|
45
|
-
|
57
|
+
"alpine-#{tmp_ao_arch}"
|
46
58
|
end
|
47
59
|
end
|
48
60
|
|
49
61
|
ao_clib = "liboboe-1.0-#{ao_arch}.so.0.0.0"
|
62
|
+
ao_clib = "liboboe-1.0-#{ao_arch}.so" if ENV['OBOE_DEV'].to_s.downcase == 'true' # for dev build only
|
50
63
|
ao_item = File.join(ao_path, ao_clib)
|
51
64
|
ao_checksum_file = File.join(ao_lib_dir, "#{ao_clib}.sha256")
|
52
65
|
clib = File.join(ao_lib_dir, ao_clib)
|
@@ -138,4 +151,4 @@ if success
|
|
138
151
|
$stderr.puts '=================================================================='
|
139
152
|
create_makefile('oboe_noop', 'noop')
|
140
153
|
end
|
141
|
-
end
|
154
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
ae491260c3b2dfbb2ada97de0c94e78424b2eb5bf19b2b752c25301057270c30
|
@@ -0,0 +1 @@
|
|
1
|
+
a1f5406931ce82b0297bbb1b2b22b151c544e47babc43f0116274f95002ce85e
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
ede3fcc827faba4d6cf09d2dd59a41325c462c9093994c2c2df202ae57416733
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
079abfef35a869c3469908a62b97ad6f0f98f85a6f79e3c60265b251dc07dac3
|
data/ext/oboe_metal/src/VERSION
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
11.
|
1
|
+
11.1.0
|
2
2
|
|
@@ -57,10 +57,9 @@ module SolarWindsAPM
|
|
57
57
|
# KVOp (no KVKey)
|
58
58
|
|
59
59
|
def self.included(klass)
|
60
|
-
#
|
61
|
-
|
62
|
-
SolarWindsAPM::Util.method_alias(klass, :
|
63
|
-
SolarWindsAPM::Util.method_alias(klass, :call_pipeline, ::Redis::Client)
|
60
|
+
# call_pipelined is alias of call in redisclient middlewares
|
61
|
+
SolarWindsAPM::Util.method_alias(klass, :call, ::RedisClient)
|
62
|
+
SolarWindsAPM::Util.method_alias(klass, :call_pipelined, ::RedisClient)
|
64
63
|
end
|
65
64
|
|
66
65
|
# Given any Redis operation command array, this method
|
@@ -69,12 +68,14 @@ module SolarWindsAPM
|
|
69
68
|
# @param command [Array] the Redis operation array
|
70
69
|
# @param r [Return] the return value from the operation
|
71
70
|
# @return [Hash] the Key/Values to report
|
72
|
-
def extract_trace_details(command, r)
|
71
|
+
def extract_trace_details(command, r, config)
|
72
|
+
SolarWindsAPM.logger.debug "extract_trace_details command => #{command.inspect}"
|
73
73
|
kvs = {}
|
74
74
|
op = command.first
|
75
|
+
op = op.to_sym
|
75
76
|
|
76
77
|
kvs[:KVOp] = command[0]
|
77
|
-
kvs[:RemoteHost] =
|
78
|
+
kvs[:RemoteHost] = config.host
|
78
79
|
unless NO_KEY_OPS.include?(op) || op == :del && command[1..-1].flatten.count > 1
|
79
80
|
if command[1].is_a?(Array)
|
80
81
|
kvs[:KVKey] = command[1].first
|
@@ -115,7 +116,7 @@ module SolarWindsAPM
|
|
115
116
|
kvs[:KVHit] = r.nil? ? 0 : 1
|
116
117
|
|
117
118
|
when :hdel, :hexists, :hget, :hset, :hsetnx
|
118
|
-
kvs[:field] = command[2] unless command[2].is_a?(Array)
|
119
|
+
kvs[:field] = command[2] unless (command[2] && command[3]) # replace the idiom of command[2].is_a?(Array)
|
119
120
|
if op == :hget
|
120
121
|
kvs[:KVHit] = r.nil? ? 0 : 1
|
121
122
|
end
|
@@ -136,7 +137,7 @@ module SolarWindsAPM
|
|
136
137
|
else
|
137
138
|
kvs[:Script] = command[2]
|
138
139
|
end
|
139
|
-
elsif command[1] ==
|
140
|
+
elsif command[1] == "exists"
|
140
141
|
if command[2].is_a?(Array)
|
141
142
|
kvs[:KVKey] = command[2].inspect
|
142
143
|
else
|
@@ -166,6 +167,10 @@ module SolarWindsAPM
|
|
166
167
|
end
|
167
168
|
end # case op
|
168
169
|
end # if KV_COLLECT_MAP[op]
|
170
|
+
# turn every string into number
|
171
|
+
# kvs.each do |k,v|
|
172
|
+
# kvs[k] = v.to_i if v.is_a(String)
|
173
|
+
# end
|
169
174
|
rescue StandardError => e
|
170
175
|
SolarWindsAPM.logger.debug "[solarwinds_apm/redis] Error collecting redis KVs: #{e.message}"
|
171
176
|
SolarWindsAPM.logger.debug e.backtrace.join('\n')
|
@@ -178,16 +183,17 @@ module SolarWindsAPM
|
|
178
183
|
#
|
179
184
|
# @param pipeline [Redis::Pipeline] the Redis pipeline instance
|
180
185
|
# @return [Hash] the Key/Values to report
|
181
|
-
def extract_pipeline_details(
|
186
|
+
def extract_pipeline_details(commands, config)
|
187
|
+
SolarWindsAPM.logger.debug "extract_pipeline_details command => #{commands.inspect}"
|
182
188
|
kvs = {}
|
183
189
|
|
184
|
-
kvs[:RemoteHost] =
|
190
|
+
kvs[:RemoteHost] = config.host
|
185
191
|
kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:redis][:collect_backtraces]
|
186
192
|
|
187
|
-
command_count =
|
193
|
+
command_count = commands.count
|
188
194
|
kvs[:KVOpCount] = command_count
|
189
195
|
|
190
|
-
kvs[:KVOp] = if
|
196
|
+
kvs[:KVOp] = if commands.first == :multi
|
191
197
|
:multi
|
192
198
|
else
|
193
199
|
:pipeline
|
@@ -197,7 +203,7 @@ module SolarWindsAPM
|
|
197
203
|
# of ops is reasonable
|
198
204
|
if command_count < 12
|
199
205
|
ops = []
|
200
|
-
|
206
|
+
commands.each do |c|
|
201
207
|
ops << c.first
|
202
208
|
end
|
203
209
|
kvs[:KVOps] = ops.join(', ')
|
@@ -210,17 +216,19 @@ module SolarWindsAPM
|
|
210
216
|
end
|
211
217
|
|
212
218
|
#
|
213
|
-
# The wrapper method for
|
219
|
+
# The wrapper method for RedisClient.call. Here
|
214
220
|
# (when tracing) we capture KVs to report and pass
|
215
221
|
# the call along
|
216
222
|
#
|
217
|
-
def call_with_sw_apm(command, &block)
|
223
|
+
def call_with_sw_apm(command, config, &block)
|
224
|
+
SolarWindsAPM.logger.debug "call_with_sw_apm command => #{command.inspect}"
|
218
225
|
if SolarWindsAPM.tracing?
|
219
226
|
SolarWindsAPM::API.log_entry(:redis, {})
|
220
227
|
|
221
228
|
begin
|
222
|
-
r = call_without_sw_apm(command, &block)
|
223
|
-
report_kvs = extract_trace_details(command, r)
|
229
|
+
r = call_without_sw_apm(command, config, &block)
|
230
|
+
report_kvs = extract_trace_details(command, r, config)
|
231
|
+
SolarWindsAPM.logger.debug "call_with_sw_apm command => #{report_kvs.inspect}"
|
224
232
|
r
|
225
233
|
rescue StandardError => e
|
226
234
|
SolarWindsAPM::API.log_exception(:redis, e)
|
@@ -230,26 +238,25 @@ module SolarWindsAPM
|
|
230
238
|
end
|
231
239
|
|
232
240
|
else
|
233
|
-
call_without_sw_apm(command, &block)
|
241
|
+
call_without_sw_apm(command, config, &block)
|
234
242
|
end
|
235
243
|
end
|
236
244
|
|
237
245
|
#
|
238
|
-
# The wrapper method for
|
246
|
+
# The wrapper method for RedisClient.call_pipeline. Here
|
239
247
|
# (when tracing) we capture KVs to report and pass the call along
|
240
|
-
#
|
241
|
-
def
|
248
|
+
# 5.0.0 + removed the deprecated pipelined and multi signature. Commands now MUST be called on the block argument, not the original redis instance
|
249
|
+
def call_pipelined_with_sw_apm(commands, config, &block)
|
250
|
+
SolarWindsAPM.logger.debug "call_pipelined_with_sw_apm command => #{commands.inspect}"
|
242
251
|
if SolarWindsAPM.tracing?
|
243
252
|
# Fall back to the raw tracing API so we can pass KVs
|
244
253
|
# back on exit (a limitation of the SolarWindsAPM::API.trace
|
245
254
|
# block method) This removes the need for an info
|
246
255
|
# event to send additonal KVs
|
247
256
|
SolarWindsAPM::API.log_entry(:redis, {})
|
248
|
-
|
249
|
-
report_kvs = extract_pipeline_details(pipeline)
|
250
|
-
|
257
|
+
report_kvs = extract_pipeline_details(commands, config)
|
251
258
|
begin
|
252
|
-
|
259
|
+
call_pipelined_without_sw_apm(commands, config, &block)
|
253
260
|
rescue StandardError => e
|
254
261
|
SolarWindsAPM::API.log_exception(:redis, e)
|
255
262
|
raise
|
@@ -257,7 +264,7 @@ module SolarWindsAPM
|
|
257
264
|
SolarWindsAPM::API.log_exit(:redis, report_kvs)
|
258
265
|
end
|
259
266
|
else
|
260
|
-
|
267
|
+
call_pipelined_without_sw_apm(commands, config, &block)
|
261
268
|
end
|
262
269
|
end
|
263
270
|
end
|
@@ -266,8 +273,8 @@ module SolarWindsAPM
|
|
266
273
|
end
|
267
274
|
|
268
275
|
if SolarWindsAPM::Config[:redis][:enabled]
|
269
|
-
if defined?(
|
270
|
-
SolarWindsAPM.logger.info
|
271
|
-
|
276
|
+
if defined?(::RedisClient)
|
277
|
+
SolarWindsAPM.logger.info "[solarwinds_apm/loading] Instrumenting redis #{Redis::VERSION}" if SolarWindsAPM::Config[:verbose]
|
278
|
+
::RedisClient.register(SolarWindsAPM::Inst::Redis::Client)
|
272
279
|
end
|
273
280
|
end
|
@@ -0,0 +1,273 @@
|
|
1
|
+
# Copyright (c) 2016 SolarWinds, LLC.
|
2
|
+
# All rights reserved.
|
3
|
+
|
4
|
+
module SolarWindsAPM
|
5
|
+
module Inst
|
6
|
+
module RedisV4
|
7
|
+
module Client
|
8
|
+
# The operations listed in this constant skip collecting KVKey
|
9
|
+
NO_KEY_OPS = [:auth, :keys, :randomkey, :scan, :sdiff, :sdiffstore, :sinter,
|
10
|
+
:sinterstore, :smove, :sunion, :sunionstore, :zinterstore,
|
11
|
+
:zunionstore, :publish, :select, :eval, :evalsha, :script].freeze
|
12
|
+
|
13
|
+
# Instead of a giant switch statement, we use a hash constant to map out what
|
14
|
+
# KVs need to be collected for each of the many many Redis operations
|
15
|
+
# Hash formatting by undiagnosed OCD
|
16
|
+
KV_COLLECT_MAP = {
|
17
|
+
:brpoplpush => { :destination => 2 }, :rpoplpush => { :destination => 2 },
|
18
|
+
:sdiffstore => { :destination => 1 }, :sinterstore => { :destination => 1 },
|
19
|
+
:sunionstore => { :destination => 1 }, :zinterstore => { :destination => 1 },
|
20
|
+
:zunionstore => { :destination => 1 }, :publish => { :channel => 1 },
|
21
|
+
:incrby => { :increment => 2 }, :incrbyfloat => { :increment => 2 },
|
22
|
+
:pexpire => { :milliseconds => 2 }, :pexpireat => { :milliseconds => 2 },
|
23
|
+
:expireat => { :timestamp => 2 }, :decrby => { :decrement => 2 },
|
24
|
+
:psetex => { :ttl => 2 }, :restore => { :ttl => 2 },
|
25
|
+
:setex => { :ttl => 2 }, :setnx => { :ttl => 2 },
|
26
|
+
:move => { :db => 2 }, :select => { :db => 1 },
|
27
|
+
:lindex => { :index => 2 }, :getset => { :value => 2 },
|
28
|
+
:keys => { :pattern => 1 }, :expire => { :seconds => 2 },
|
29
|
+
:rename => { :newkey => 2 }, :renamenx => { :newkey => 2 },
|
30
|
+
:getbit => { :offset => 2 }, :setbit => { :offset => 2 },
|
31
|
+
:setrange => { :offset => 2 }, :evalsha => { :sha => 1 },
|
32
|
+
:getrange => { :start => 2, :end => 3 },
|
33
|
+
:zrange => { :start => 2, :end => 3 },
|
34
|
+
:bitcount => { :start => 2, :stop => 3 },
|
35
|
+
:lrange => { :start => 2, :stop => 3 },
|
36
|
+
:zrevrange => { :start => 2, :stop => 3 },
|
37
|
+
:hincrby => { :field => 2, :increment => 3 },
|
38
|
+
:smove => { :source => 1, :destination => 2 },
|
39
|
+
:bitop => { :operation => 1, :destkey => 2 },
|
40
|
+
:hincrbyfloat => { :field => 2, :increment => 3 },
|
41
|
+
:zremrangebyrank => { :start => 2, :stop => 3 }
|
42
|
+
}.freeze
|
43
|
+
|
44
|
+
# The following operations don't require any special handling. For these,
|
45
|
+
# we only collect KVKey and KVOp
|
46
|
+
#
|
47
|
+
# :append, :blpop, :brpop, :decr, :del, :dump, :exists,
|
48
|
+
# :hgetall, :hkeys, :hlen, :hvals, :hmset, :incr, :linsert,
|
49
|
+
# :llen, :lpop, :lpush, :lpushx, :lrem, :lset, :ltrim,
|
50
|
+
# :persist, :pttl, :hscan, :rpop, :rpush, :rpushx, :sadd,
|
51
|
+
# :scard, :sismember, :smembers, :strlen, :sort, :spop,
|
52
|
+
# :srandmember, :srem, :sscan, :ttl, :type, :zadd, :zcard,
|
53
|
+
# :zcount, :zincrby, :zrangebyscore, :zrank, :zrem,
|
54
|
+
# :zremrangebyscore, :zrevrank, :zrevrangebyscore, :zscore
|
55
|
+
#
|
56
|
+
# For the operations in NO_KEY_OPS (above) we only collect
|
57
|
+
# KVOp (no KVKey)
|
58
|
+
|
59
|
+
def self.included(klass)
|
60
|
+
# We wrap two of the Redis methods to instrument
|
61
|
+
# operations
|
62
|
+
SolarWindsAPM::Util.method_alias(klass, :call, ::Redis::Client)
|
63
|
+
SolarWindsAPM::Util.method_alias(klass, :call_pipeline, ::Redis::Client)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Given any Redis operation command array, this method
|
67
|
+
# extracts the Key/Values to report to the SolarWinds # dashboard.
|
68
|
+
#
|
69
|
+
# @param command [Array] the Redis operation array
|
70
|
+
# @param r [Return] the return value from the operation
|
71
|
+
# @return [Hash] the Key/Values to report
|
72
|
+
def extract_trace_details(command, r)
|
73
|
+
kvs = {}
|
74
|
+
op = command.first
|
75
|
+
|
76
|
+
kvs[:KVOp] = command[0]
|
77
|
+
kvs[:RemoteHost] = @options[:host]
|
78
|
+
unless NO_KEY_OPS.include?(op) || op == :del && command[1..-1].flatten.count > 1
|
79
|
+
if command[1].is_a?(Array)
|
80
|
+
kvs[:KVKey] = command[1].first
|
81
|
+
else
|
82
|
+
kvs[:KVKey] = command[1]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
if KV_COLLECT_MAP[op]
|
87
|
+
# Extract KVs from command for this op
|
88
|
+
KV_COLLECT_MAP[op].each { |k, v| kvs[k] = command[v] }
|
89
|
+
else
|
90
|
+
# This case statement handle special cases not handled
|
91
|
+
# by KV_COLLECT_MAP
|
92
|
+
case op
|
93
|
+
when :set
|
94
|
+
if command.count > 3
|
95
|
+
if command[3].is_a?(Hash)
|
96
|
+
options = command[3]
|
97
|
+
kvs[:ex] = options[:ex] if options.key?(:ex)
|
98
|
+
kvs[:px] = options[:px] if options.key?(:px)
|
99
|
+
kvs[:nx] = options[:nx] if options.key?(:nx)
|
100
|
+
kvs[:xx] = options[:xx] if options.key?(:xx)
|
101
|
+
else
|
102
|
+
options = command[3..-1]
|
103
|
+
until (opts = options.shift(2)).empty?
|
104
|
+
case opts[0]
|
105
|
+
when 'EX' then; kvs[:ex] = opts[1]
|
106
|
+
when 'PX' then; kvs[:px] = opts[1]
|
107
|
+
when 'NX' then; kvs[:nx] = opts[1]
|
108
|
+
when 'XX' then; kvs[:xx] = opts[1]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
when :get
|
115
|
+
kvs[:KVHit] = r.nil? ? 0 : 1
|
116
|
+
|
117
|
+
when :hdel, :hexists, :hget, :hset, :hsetnx
|
118
|
+
kvs[:field] = command[2] unless command[2].is_a?(Array)
|
119
|
+
if op == :hget
|
120
|
+
kvs[:KVHit] = r.nil? ? 0 : 1
|
121
|
+
end
|
122
|
+
|
123
|
+
when :eval
|
124
|
+
if command[1].length > 1024
|
125
|
+
kvs[:Script] = command[1][0..1023] + '(...snip...)'
|
126
|
+
else
|
127
|
+
kvs[:Script] = command[1]
|
128
|
+
end
|
129
|
+
|
130
|
+
when :script
|
131
|
+
kvs[:subcommand] = command[1]
|
132
|
+
kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:redis][:collect_backtraces]
|
133
|
+
if command[1] == 'load'
|
134
|
+
if command[1].length > 1024
|
135
|
+
kvs[:Script] = command[2][0..1023] + '(...snip...)'
|
136
|
+
else
|
137
|
+
kvs[:Script] = command[2]
|
138
|
+
end
|
139
|
+
elsif command[1] == :exists
|
140
|
+
if command[2].is_a?(Array)
|
141
|
+
kvs[:KVKey] = command[2].inspect
|
142
|
+
else
|
143
|
+
kvs[:KVKey] = command[2]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
when :mget
|
148
|
+
if command[1].is_a?(Array)
|
149
|
+
kvs[:KVKeyCount] = command[1].count
|
150
|
+
else
|
151
|
+
kvs[:KVKeyCount] = command.count - 1
|
152
|
+
end
|
153
|
+
values = r.select { |i| i }
|
154
|
+
kvs[:KVHitCount] = values.count
|
155
|
+
|
156
|
+
when :hmget
|
157
|
+
kvs[:KVKeyCount] = command.count - 2
|
158
|
+
values = r.select { |i| i }
|
159
|
+
kvs[:KVHitCount] = values.count
|
160
|
+
|
161
|
+
when :mset, :msetnx
|
162
|
+
if command[1].is_a?(Array)
|
163
|
+
kvs[:KVKeyCount] = command[1].count / 2
|
164
|
+
else
|
165
|
+
kvs[:KVKeyCount] = (command.count - 1) / 2
|
166
|
+
end
|
167
|
+
end # case op
|
168
|
+
end # if KV_COLLECT_MAP[op]
|
169
|
+
rescue StandardError => e
|
170
|
+
SolarWindsAPM.logger.debug "[solarwinds_apm/redis] Error collecting redis KVs: #{e.message}"
|
171
|
+
SolarWindsAPM.logger.debug e.backtrace.join('\n')
|
172
|
+
ensure
|
173
|
+
return kvs
|
174
|
+
end
|
175
|
+
|
176
|
+
# Extracts the Key/Values to report from a pipelined
|
177
|
+
# call to the SolarWinds dashboard.
|
178
|
+
#
|
179
|
+
# @param pipeline [Redis::Pipeline] the Redis pipeline instance
|
180
|
+
# @return [Hash] the Key/Values to report
|
181
|
+
def extract_pipeline_details(pipeline)
|
182
|
+
kvs = {}
|
183
|
+
|
184
|
+
kvs[:RemoteHost] = @options[:host]
|
185
|
+
kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:redis][:collect_backtraces]
|
186
|
+
|
187
|
+
command_count = pipeline.commands.count
|
188
|
+
kvs[:KVOpCount] = command_count
|
189
|
+
|
190
|
+
kvs[:KVOp] = if pipeline.commands.first == :multi
|
191
|
+
:multi
|
192
|
+
else
|
193
|
+
:pipeline
|
194
|
+
end
|
195
|
+
|
196
|
+
# Report pipelined operations if the number
|
197
|
+
# of ops is reasonable
|
198
|
+
if command_count < 12
|
199
|
+
ops = []
|
200
|
+
pipeline.commands.each do |c|
|
201
|
+
ops << c.first
|
202
|
+
end
|
203
|
+
kvs[:KVOps] = ops.join(', ')
|
204
|
+
end
|
205
|
+
rescue StandardError => e
|
206
|
+
SolarWindsAPM.logger.debug "[solarwinds_apm/debug] Error extracting pipelined commands: #{e.message}"
|
207
|
+
SolarWindsAPM.logger.debug e.backtrace
|
208
|
+
ensure
|
209
|
+
return kvs
|
210
|
+
end
|
211
|
+
|
212
|
+
#
|
213
|
+
# The wrapper method for Redis::Client.call. Here
|
214
|
+
# (when tracing) we capture KVs to report and pass
|
215
|
+
# the call along
|
216
|
+
#
|
217
|
+
def call_with_sw_apm(command, &block)
|
218
|
+
if SolarWindsAPM.tracing?
|
219
|
+
SolarWindsAPM::API.log_entry(:redis, {})
|
220
|
+
|
221
|
+
begin
|
222
|
+
r = call_without_sw_apm(command, &block)
|
223
|
+
report_kvs = extract_trace_details(command, r)
|
224
|
+
r
|
225
|
+
rescue StandardError => e
|
226
|
+
SolarWindsAPM::API.log_exception(:redis, e)
|
227
|
+
raise
|
228
|
+
ensure
|
229
|
+
SolarWindsAPM::API.log_exit(:redis, report_kvs)
|
230
|
+
end
|
231
|
+
|
232
|
+
else
|
233
|
+
call_without_sw_apm(command, &block)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
#
|
238
|
+
# The wrapper method for Redis::Client.call_pipeline. Here
|
239
|
+
# (when tracing) we capture KVs to report and pass the call along
|
240
|
+
#
|
241
|
+
def call_pipeline_with_sw_apm(pipeline)
|
242
|
+
if SolarWindsAPM.tracing?
|
243
|
+
# Fall back to the raw tracing API so we can pass KVs
|
244
|
+
# back on exit (a limitation of the SolarWindsAPM::API.trace
|
245
|
+
# block method) This removes the need for an info
|
246
|
+
# event to send additonal KVs
|
247
|
+
SolarWindsAPM::API.log_entry(:redis, {})
|
248
|
+
|
249
|
+
report_kvs = extract_pipeline_details(pipeline)
|
250
|
+
|
251
|
+
begin
|
252
|
+
call_pipeline_without_sw_apm(pipeline)
|
253
|
+
rescue StandardError => e
|
254
|
+
SolarWindsAPM::API.log_exception(:redis, e)
|
255
|
+
raise
|
256
|
+
ensure
|
257
|
+
SolarWindsAPM::API.log_exit(:redis, report_kvs)
|
258
|
+
end
|
259
|
+
else
|
260
|
+
call_pipeline_without_sw_apm(pipeline)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
if SolarWindsAPM::Config[:redis][:enabled]
|
269
|
+
if defined?(::Redis) && ::Redis::VERSION < '5' && ::Redis::VERSION > '3'
|
270
|
+
SolarWindsAPM.logger.info '[solarwinds_apm/loading] Instrumenting redis' if SolarWindsAPM::Config[:verbose]
|
271
|
+
SolarWindsAPM::Util.send_include(::Redis::Client, SolarWindsAPM::Inst::RedisV4::Client)
|
272
|
+
end
|
273
|
+
end
|