solarwinds_apm 5.1.3 → 5.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
[](https://badge.fury.io/rb/solarwinds_apm)
|
|
13
13
|
|
|
14
|
-
[](https://github.com/solarwindscloud/solarwinds-apm-ruby/actions/workflows/test_on_4_linux.yml)
|
|
15
|
+
|
|
15
16
|
[](https://github.com/appoptics/appoptics-apm-ruby/actions/workflows/run_cpluplus_tests.yml)
|
|
16
17
|
|
|
17
18
|
[comment]: <> ([](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
|