solarwinds_apm 5.1.8 → 6.0.0.preV1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +0 -1
  3. data/ext/oboe_metal/extconf.rb +19 -23
  4. data/ext/oboe_metal/lib/liboboe-1.0-aarch64.so.sha256 +1 -1
  5. data/ext/oboe_metal/lib/liboboe-1.0-alpine-aarch64.so.sha256 +1 -1
  6. data/ext/oboe_metal/lib/liboboe-1.0-alpine-x86_64.so.sha256 +1 -1
  7. data/ext/oboe_metal/lib/liboboe-1.0-x86_64.so.sha256 +1 -1
  8. data/ext/oboe_metal/src/VERSION +1 -1
  9. data/ext/oboe_metal/src/oboe_api.cpp +9 -7
  10. data/ext/oboe_metal/src/oboe_api.h +7 -7
  11. data/ext/oboe_metal/src/oboe_debug.h +2 -0
  12. data/ext/oboe_metal/src/oboe_swig_wrap.cc +19 -18
  13. data/lib/oboe_metal.rb +116 -80
  14. data/lib/rails/generators/solarwinds_apm/install_generator.rb +1 -2
  15. data/lib/rails/generators/solarwinds_apm/templates/solarwinds_apm_initializer.rb +44 -260
  16. data/lib/solarwinds_apm/api/current_trace_info.rb +148 -0
  17. data/lib/solarwinds_apm/api/tracing.rb +30 -0
  18. data/lib/solarwinds_apm/api/transaction_name.rb +57 -0
  19. data/lib/solarwinds_apm/api.rb +8 -15
  20. data/lib/solarwinds_apm/base.rb +4 -131
  21. data/lib/solarwinds_apm/config.rb +128 -175
  22. data/lib/solarwinds_apm/constants.rb +32 -0
  23. data/lib/solarwinds_apm/logger.rb +1 -1
  24. data/lib/solarwinds_apm/noop/context.rb +2 -5
  25. data/lib/solarwinds_apm/noop/metadata.rb +1 -2
  26. data/lib/solarwinds_apm/noop/profiling.rb +3 -7
  27. data/lib/solarwinds_apm/oboe_init_options.rb +71 -33
  28. data/lib/solarwinds_apm/opentelemetry/solarwinds_exporter.rb +204 -0
  29. data/lib/solarwinds_apm/opentelemetry/solarwinds_processor.rb +163 -0
  30. data/lib/solarwinds_apm/opentelemetry/solarwinds_propagator.rb +92 -0
  31. data/lib/solarwinds_apm/opentelemetry/solarwinds_response_propagator.rb +72 -0
  32. data/lib/solarwinds_apm/opentelemetry/solarwinds_sampler.rb +330 -0
  33. data/lib/solarwinds_apm/opentelemetry.rb +8 -0
  34. data/lib/solarwinds_apm/otel_config.rb +161 -0
  35. data/lib/solarwinds_apm/{inst → support}/logger_formatter.rb +5 -6
  36. data/lib/solarwinds_apm/{inst → support}/logging_log_event.rb +3 -6
  37. data/lib/solarwinds_apm/{inst → support}/lumberjack_formatter.rb +1 -4
  38. data/lib/solarwinds_apm/support/oboe_tracing_mode.rb +27 -0
  39. data/lib/solarwinds_apm/support/swomarginalia/LICENSE +20 -0
  40. data/lib/solarwinds_apm/support/swomarginalia/README.md +41 -0
  41. data/lib/solarwinds_apm/support/swomarginalia/comment.rb +205 -0
  42. data/lib/solarwinds_apm/support/swomarginalia/load_swomarginalia.rb +48 -0
  43. data/lib/solarwinds_apm/support/swomarginalia/railtie.rb +22 -0
  44. data/lib/solarwinds_apm/support/swomarginalia/swomarginalia.rb +86 -0
  45. data/lib/solarwinds_apm/support/transaction_cache.rb +24 -0
  46. data/lib/solarwinds_apm/support/transaction_settings.rb +26 -209
  47. data/lib/solarwinds_apm/support/transformer.rb +56 -0
  48. data/lib/solarwinds_apm/support/txn_name_manager.rb +25 -0
  49. data/lib/solarwinds_apm/support/x_trace_options.rb +42 -26
  50. data/lib/solarwinds_apm/support.rb +33 -10
  51. data/lib/solarwinds_apm/support_report.rb +10 -32
  52. data/lib/solarwinds_apm/thread_local.rb +1 -1
  53. data/lib/solarwinds_apm/version.rb +4 -4
  54. data/lib/solarwinds_apm.rb +31 -26
  55. metadata +76 -121
  56. data/.dockerignore +0 -5
  57. data/.gitignore +0 -58
  58. data/.rubocop.yml +0 -29
  59. data/.whitesource +0 -22
  60. data/.yardopts +0 -7
  61. data/CHANGELOG-appoptics.md +0 -766
  62. data/CHANGELOG.md +0 -72
  63. data/CONFIG.md +0 -31
  64. data/Gemfile +0 -15
  65. data/README.md +0 -385
  66. data/bin/solarwinds_apm_config +0 -15
  67. data/examples/prepend.rb +0 -13
  68. data/examples/sdk_examples.rb +0 -158
  69. data/ext/oboe_metal/README.md +0 -69
  70. data/ext/oboe_metal/extconf_local.rb +0 -75
  71. data/ext/oboe_metal/lib/.keep +0 -0
  72. data/ext/oboe_metal/noop/noop.c +0 -8
  73. data/ext/oboe_metal/src/README.md +0 -6
  74. data/ext/oboe_metal/src/frames.cc +0 -247
  75. data/ext/oboe_metal/src/frames.h +0 -40
  76. data/ext/oboe_metal/src/logging.cc +0 -97
  77. data/ext/oboe_metal/src/logging.h +0 -34
  78. data/ext/oboe_metal/src/profiling.cc +0 -435
  79. data/ext/oboe_metal/src/profiling.h +0 -78
  80. data/ext/oboe_metal/test/CMakeLists.txt +0 -53
  81. data/ext/oboe_metal/test/FindGMock.cmake +0 -43
  82. data/ext/oboe_metal/test/README.md +0 -56
  83. data/ext/oboe_metal/test/frames_test.cc +0 -164
  84. data/ext/oboe_metal/test/profiling_test.cc +0 -93
  85. data/ext/oboe_metal/test/ruby_inc_dir.rb +0 -8
  86. data/ext/oboe_metal/test/ruby_prefix.rb +0 -8
  87. data/ext/oboe_metal/test/ruby_test_helper.rb +0 -67
  88. data/ext/oboe_metal/test/test.h +0 -11
  89. data/ext/oboe_metal/test/test_main.cc +0 -32
  90. data/init.rb +0 -4
  91. data/lib/solarwinds_apm/api/layerinit.rb +0 -41
  92. data/lib/solarwinds_apm/api/logging.rb +0 -356
  93. data/lib/solarwinds_apm/api/memcache.rb +0 -37
  94. data/lib/solarwinds_apm/api/metrics.rb +0 -63
  95. data/lib/solarwinds_apm/api/util.rb +0 -98
  96. data/lib/solarwinds_apm/frameworks/grape.rb +0 -96
  97. data/lib/solarwinds_apm/frameworks/padrino.rb +0 -78
  98. data/lib/solarwinds_apm/frameworks/rails/inst/action_controller.rb +0 -100
  99. data/lib/solarwinds_apm/frameworks/rails/inst/action_controller5.rb +0 -50
  100. data/lib/solarwinds_apm/frameworks/rails/inst/action_controller_api.rb +0 -50
  101. data/lib/solarwinds_apm/frameworks/rails/inst/action_view.rb +0 -88
  102. data/lib/solarwinds_apm/frameworks/rails/inst/active_record.rb +0 -26
  103. data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/mysql2.rb +0 -29
  104. data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/postgresql.rb +0 -22
  105. data/lib/solarwinds_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +0 -103
  106. data/lib/solarwinds_apm/frameworks/rails/inst/logger_formatters.rb +0 -14
  107. data/lib/solarwinds_apm/frameworks/rails.rb +0 -100
  108. data/lib/solarwinds_apm/frameworks/sinatra.rb +0 -96
  109. data/lib/solarwinds_apm/inst/bunny-client.rb +0 -157
  110. data/lib/solarwinds_apm/inst/bunny-consumer.rb +0 -102
  111. data/lib/solarwinds_apm/inst/curb.rb +0 -289
  112. data/lib/solarwinds_apm/inst/dalli.rb +0 -89
  113. data/lib/solarwinds_apm/inst/delayed_job.rb +0 -100
  114. data/lib/solarwinds_apm/inst/excon.rb +0 -113
  115. data/lib/solarwinds_apm/inst/faraday.rb +0 -96
  116. data/lib/solarwinds_apm/inst/graphql.rb +0 -206
  117. data/lib/solarwinds_apm/inst/grpc_client.rb +0 -147
  118. data/lib/solarwinds_apm/inst/grpc_server.rb +0 -119
  119. data/lib/solarwinds_apm/inst/httpclient.rb +0 -182
  120. data/lib/solarwinds_apm/inst/memcached.rb +0 -86
  121. data/lib/solarwinds_apm/inst/mongo.rb +0 -246
  122. data/lib/solarwinds_apm/inst/mongo2.rb +0 -225
  123. data/lib/solarwinds_apm/inst/moped.rb +0 -466
  124. data/lib/solarwinds_apm/inst/net_http.rb +0 -60
  125. data/lib/solarwinds_apm/inst/rack.rb +0 -223
  126. data/lib/solarwinds_apm/inst/rack_cache.rb +0 -35
  127. data/lib/solarwinds_apm/inst/redis.rb +0 -280
  128. data/lib/solarwinds_apm/inst/redis_v4.rb +0 -273
  129. data/lib/solarwinds_apm/inst/resque.rb +0 -129
  130. data/lib/solarwinds_apm/inst/rest-client.rb +0 -43
  131. data/lib/solarwinds_apm/inst/sequel.rb +0 -241
  132. data/lib/solarwinds_apm/inst/sidekiq-client.rb +0 -63
  133. data/lib/solarwinds_apm/inst/sidekiq-worker.rb +0 -64
  134. data/lib/solarwinds_apm/inst/typhoeus.rb +0 -90
  135. data/lib/solarwinds_apm/instrumentation.rb +0 -22
  136. data/lib/solarwinds_apm/loading.rb +0 -65
  137. data/lib/solarwinds_apm/ruby.rb +0 -35
  138. data/lib/solarwinds_apm/sdk/current_trace_info.rb +0 -123
  139. data/lib/solarwinds_apm/sdk/custom_metrics.rb +0 -94
  140. data/lib/solarwinds_apm/sdk/logging.rb +0 -37
  141. data/lib/solarwinds_apm/sdk/trace_context_headers.rb +0 -69
  142. data/lib/solarwinds_apm/sdk/tracing.rb +0 -432
  143. data/lib/solarwinds_apm/support/profiling.rb +0 -25
  144. data/lib/solarwinds_apm/support/trace_context.rb +0 -53
  145. data/lib/solarwinds_apm/support/trace_state.rb +0 -69
  146. data/lib/solarwinds_apm/support/trace_string.rb +0 -89
  147. data/lib/solarwinds_apm/support/transaction_metrics.rb +0 -67
  148. data/lib/solarwinds_apm/test.rb +0 -165
  149. data/lib/solarwinds_apm/util.rb +0 -426
  150. data/log/.keep +0 -0
  151. data/log/postgresql/.keep +0 -0
  152. data/solarwinds_apm.gemspec +0 -55
  153. data/yardoc_frontpage.md +0 -24
@@ -1,223 +0,0 @@
1
- # Copyright (c) 2016 SolarWinds, LLC.
2
- # All rights reserved.
3
-
4
- require 'uri'
5
- require 'cgi'
6
-
7
- if SolarWindsAPM.loaded
8
- module SolarWindsAPM
9
- ##
10
- # SolarWindsAPM::Rack
11
- #
12
- # The SolarWindsAPM::Rack middleware used to sample a subset of incoming
13
- # requests for instrumentation and reporting. Tracing context can
14
- # be received here (via the X-Trace HTTP header) or initiated here
15
- # based on configured tracing mode.
16
- #
17
- # After the rack layer passes on to the following layers (Rails, Sinatra,
18
- # Padrino, Grape), then the instrumentation downstream will
19
- # automatically detect whether this is a sampled request or not
20
- # and act accordingly.
21
- #
22
- class Rack
23
- include SolarWindsAPM::SDK::TraceContextHeaders
24
-
25
- attr_reader :app
26
-
27
- def initialize(app)
28
- @app = app
29
- end
30
-
31
- def call(env)
32
-
33
- # In the case of nested Ruby apps such as Grape inside of Rails
34
- # or Grape inside of Grape, each app has it's own instance
35
- # of rack middleware. We want to avoid tracing rack more than once
36
- return @app.call(env) if SolarWindsAPM.tracing? && SolarWindsAPM.layer == :rack
37
-
38
- # there may be a legit existing context that we need to continue
39
- # we need to check if we marked a start in env
40
- # env is specific per request
41
- existing_context = false
42
- if SolarWindsAPM::Context.isValid
43
- existing_context = env['SW_APM_TRACE_STARTED'] == 'true'
44
-
45
- if existing_context
46
- # include or override tracecontext in env, so that the current context gets continued
47
- add_tracecontext_headers(env)
48
- else
49
- SolarWindsAPM::Context.clear
50
- end
51
- end
52
-
53
- SolarWindsAPM.transaction_name = nil
54
-
55
- url = env['PATH_INFO']
56
- options = SolarWindsAPM::XTraceOptions.new(env['HTTP_X_TRACE_OPTIONS'], env['HTTP_X_TRACE_OPTIONS_SIGNATURE'])
57
-
58
- # store incoming information in a thread local variable
59
- settings = SolarWindsAPM::TransactionSettings.new(url, env, options)
60
-
61
- profile_spans = SolarWindsAPM::Config['profiling'] == :enabled ? 1 : -1
62
-
63
- response =
64
- propagate_tracecontext(env, settings) do
65
- sample(env, settings, options, profile_spans) do
66
- if defined?(SolarWindsAPM::Profiling)
67
- SolarWindsAPM::Profiling.run do
68
- SolarWindsAPM::TransactionMetrics.metrics(env, settings) do
69
- @app.call(env)
70
- end
71
- end
72
- else
73
- SolarWindsAPM::TransactionMetrics.metrics(env, settings) do
74
- @app.call(env)
75
- end
76
- end
77
- end
78
- end || [500, {}, nil]
79
- options.add_response_header(response[1], settings)
80
-
81
- unless existing_context
82
- SolarWindsAPM::Context.clear
83
- SolarWindsAPM.trace_context = nil
84
- end
85
- response
86
- rescue
87
- unless existing_context
88
- SolarWindsAPM::Context.clear
89
- SolarWindsAPM.trace_context = nil
90
- end
91
- raise
92
- # can't use ensure for Context.clearing, because the Grape middleware
93
- # needs the context in case of an error, it is somewhat convoluted ...
94
- end
95
-
96
- def self.noop?
97
- false
98
- end
99
-
100
- private
101
-
102
- def collect(env)
103
- req = ::Rack::Request.new(env)
104
- report_kvs = {}
105
-
106
- begin
107
- report_kvs[:'HTTP-Host'] = req.host
108
- report_kvs[:Port] = req.port
109
- report_kvs[:Proto] = req.scheme
110
- report_kvs[:Method] = req.request_method
111
- report_kvs[:AJAX] = true if req.xhr?
112
- report_kvs[:ClientIP] = req.ip
113
-
114
- if SolarWindsAPM::Config[:rack][:log_args]
115
- report_kvs[:'Query-String'] = ::CGI.unescape(req.query_string) unless req.query_string.empty?
116
- end
117
-
118
- report_kvs[:URL] = SolarWindsAPM::Config[:rack][:log_args] ? ::CGI.unescape(req.fullpath) : ::CGI.unescape(req.path)
119
- report_kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:rack][:collect_backtraces]
120
-
121
- # Report any request queue'ing headers. Report as 'Request-Start' or the summed Queue-Time
122
- report_kvs[:'Request-Start'] = env['HTTP_X_REQUEST_START'] if env.key?('HTTP_X_REQUEST_START')
123
- report_kvs[:'Request-Start'] = env['HTTP_X_QUEUE_START'] if env.key?('HTTP_X_QUEUE_START')
124
- report_kvs[:'Queue-Time'] = env['HTTP_X_QUEUE_TIME'] if env.key?('HTTP_X_QUEUE_TIME')
125
-
126
- report_kvs[:'Forwarded-For'] = env['HTTP_X_FORWARDED_FOR'] if env.key?('HTTP_X_FORWARDED_FOR')
127
- report_kvs[:'Forwarded-Host'] = env['HTTP_X_FORWARDED_HOST'] if env.key?('HTTP_X_FORWARDED_HOST')
128
- report_kvs[:'Forwarded-Proto'] = env['HTTP_X_FORWARDED_PROTO'] if env.key?('HTTP_X_FORWARDED_PROTO')
129
- report_kvs[:'Forwarded-Port'] = env['HTTP_X_FORWARDED_PORT'] if env.key?('HTTP_X_FORWARDED_PORT')
130
-
131
- report_kvs[:'Ruby.SolarWindsAPM.Version'] = SolarWindsAPM::Version::STRING
132
- report_kvs[:ProcessID] = Process.pid
133
- report_kvs[:ThreadID] = Thread.current.to_s[/0x\w*/]
134
- rescue StandardError => e
135
- # Discard any potential exceptions. Debug log and report whatever we can.
136
- SolarWindsAPM.logger.debug "[solarwinds_apm/debug] Rack KV collection error: #{e.inspect}"
137
- end
138
- report_kvs
139
- end
140
-
141
- # this adds x-trace info to the request and return header
142
- # if it is not a request for an asset (defined in config file as 'dnt')
143
- def propagate_tracecontext(env, settings)
144
- return yield unless settings.do_propagate
145
-
146
- if SolarWindsAPM.trace_context&.tracestring
147
- # creating a dup because we are modifying it when setting/unsetting the sampling bit
148
- tracestring_dup = SolarWindsAPM.trace_context.tracestring.dup
149
- if settings.do_sample
150
- SolarWindsAPM::TraceString.set_sampled(tracestring_dup)
151
- else
152
- SolarWindsAPM::TraceString.unset_sampled(tracestring_dup)
153
- end
154
- env['HTTP_TRACEPARENT'] = tracestring_dup
155
- env['HTTP_TRACESTATE'] = SolarWindsAPM::TraceState.add_sw_member(
156
- SolarWindsAPM.trace_context&.tracestate,
157
- SolarWindsAPM::TraceString.span_id_flags(tracestring_dup)
158
- )
159
- end
160
-
161
- status, headers, response = yield
162
-
163
- # TODO this will be finalized when we have a spec for w3c response headers
164
- headers ||= {}
165
- headers['X-Trace'] = SolarWindsAPM::Context.toString if SolarWindsAPM::Context.isValid
166
-
167
- [status, headers, response]
168
- end
169
-
170
- def sample(env, settings, options, profile_spans)
171
- if settings.do_sample
172
- begin
173
- report_kvs = collect(env)
174
- settings.add_kvs(report_kvs)
175
- options&.add_kvs(report_kvs, settings)
176
-
177
- SolarWindsAPM::API.log_start(:rack, report_kvs, env, settings)
178
- # mark the trace as started for this request
179
- env['SW_APM_TRACE_STARTED'] = 'true'
180
-
181
- status, headers, response = yield
182
-
183
- SolarWindsAPM::API.log_exit(:rack, { Status: status,
184
- TransactionName: SolarWindsAPM.transaction_name,
185
- ProfileSpans: profile_spans })
186
-
187
- [status, headers, response]
188
- rescue Exception => e
189
- # it is ok to rescue Exception here because we are reraising it (we just need a chance to log_end)
190
- SolarWindsAPM::API.log_exception(:rack, e)
191
- SolarWindsAPM::API.log_exit(:rack, { Status: status,
192
- TransactionName: SolarWindsAPM.transaction_name,
193
- ProfileSpans: profile_spans
194
- })
195
- raise
196
- end
197
- else
198
- SolarWindsAPM::API.create_nontracing_context(SolarWindsAPM.trace_context.tracestring)
199
- yield
200
- end
201
- end
202
-
203
- end
204
- end
205
- else
206
- module SolarWindsAPM
207
- class Rack
208
- attr_reader :app
209
-
210
- def initialize(app)
211
- @app = app
212
- end
213
-
214
- def call(env)
215
- @app.call(env)
216
- end
217
-
218
- def self.noop?
219
- true
220
- end
221
- end
222
- end
223
- end
@@ -1,35 +0,0 @@
1
- # Copyright (c) 2020 SolarWinds, LLC.
2
- # All rights reserved.
3
-
4
- module SolarWindsAPM
5
- module RackCacheContext
6
-
7
- ###
8
- # This adds a controller.action like transaction name for
9
- # requests directly served from the cache without involving a controller.
10
- # The resulting transaction name is `rack-cache.<cache-store>`,
11
- # e.g. `rack-cache.memcached`
12
- #
13
- # It is not a full instrumentation, no span is added.
14
- #
15
- def call!(env)
16
- metastore_type = begin
17
- if options['rack-cache.metastore']
18
- options['rack-cache.metastore'].match(/^([^\:]*)\:/)[1]
19
- end || 'unknown_store'
20
- rescue
21
- 'unknown_store'
22
- end
23
-
24
- env['solarwinds_apm.action'] = metastore_type
25
- env['solarwinds_apm.controller'] = 'rack-cache'
26
-
27
- super
28
- end
29
- end
30
- end
31
-
32
- if SolarWindsAPM::Config[:rack_cache][:transaction_name] && defined?(Rack::Cache::Context)
33
- SolarWindsAPM.logger.info '[solarwinds_apm/loading] Instrumenting rack_cache' if SolarWindsAPM::Config[:verbose]
34
- Rack::Cache::Context.send(:prepend, ::SolarWindsAPM::RackCacheContext)
35
- end
@@ -1,280 +0,0 @@
1
- # Copyright (c) 2016 SolarWinds, LLC.
2
- # All rights reserved.
3
-
4
- module SolarWindsAPM
5
- module Inst
6
- module Redis
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
- # 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)
63
- end
64
-
65
- # Given any Redis operation command array, this method
66
- # extracts the Key/Values to report to the SolarWinds # dashboard.
67
- #
68
- # @param command [Array] the Redis operation array
69
- # @param r [Return] the return value from the operation
70
- # @return [Hash] the Key/Values to report
71
- def extract_trace_details(command, r, config)
72
- SolarWindsAPM.logger.debug "extract_trace_details command => #{command.inspect}"
73
- kvs = {}
74
- op = command.first
75
- op = op.to_sym
76
-
77
- kvs[:KVOp] = command[0]
78
- kvs[:RemoteHost] = config.host
79
- unless NO_KEY_OPS.include?(op) || op == :del && command[1..-1].flatten.count > 1
80
- if command[1].is_a?(Array)
81
- kvs[:KVKey] = command[1].first
82
- else
83
- kvs[:KVKey] = command[1]
84
- end
85
- end
86
-
87
- if KV_COLLECT_MAP[op]
88
- # Extract KVs from command for this op
89
- KV_COLLECT_MAP[op].each { |k, v| kvs[k] = command[v] }
90
- else
91
- # This case statement handle special cases not handled
92
- # by KV_COLLECT_MAP
93
- case op
94
- when :set
95
- if command.count > 3
96
- if command[3].is_a?(Hash)
97
- options = command[3]
98
- kvs[:ex] = options[:ex] if options.key?(:ex)
99
- kvs[:px] = options[:px] if options.key?(:px)
100
- kvs[:nx] = options[:nx] if options.key?(:nx)
101
- kvs[:xx] = options[:xx] if options.key?(:xx)
102
- else
103
- options = command[3..-1]
104
- until (opts = options.shift(2)).empty?
105
- case opts[0]
106
- when 'EX' then; kvs[:ex] = opts[1]
107
- when 'PX' then; kvs[:px] = opts[1]
108
- when 'NX' then; kvs[:nx] = opts[1]
109
- when 'XX' then; kvs[:xx] = opts[1]
110
- end
111
- end
112
- end
113
- end
114
-
115
- when :get
116
- kvs[:KVHit] = r.nil? ? 0 : 1
117
-
118
- when :hdel, :hexists, :hget, :hset, :hsetnx
119
- kvs[:field] = command[2] unless (command[2] && command[3]) # replace the idiom of command[2].is_a?(Array)
120
- if op == :hget
121
- kvs[:KVHit] = r.nil? ? 0 : 1
122
- end
123
-
124
- when :eval
125
- if command[1].length > 1024
126
- kvs[:Script] = command[1][0..1023] + '(...snip...)'
127
- else
128
- kvs[:Script] = command[1]
129
- end
130
-
131
- when :script
132
- kvs[:subcommand] = command[1]
133
- kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:redis][:collect_backtraces]
134
- if command[1] == 'load'
135
- if command[1].length > 1024
136
- kvs[:Script] = command[2][0..1023] + '(...snip...)'
137
- else
138
- kvs[:Script] = command[2]
139
- end
140
- elsif command[1] == "exists"
141
- if command[2].is_a?(Array)
142
- kvs[:KVKey] = command[2].inspect
143
- else
144
- kvs[:KVKey] = command[2]
145
- end
146
- end
147
-
148
- when :mget
149
- if command[1].is_a?(Array)
150
- kvs[:KVKeyCount] = command[1].count
151
- else
152
- kvs[:KVKeyCount] = command.count - 1
153
- end
154
- values = r.select { |i| i }
155
- kvs[:KVHitCount] = values.count
156
-
157
- when :hmget
158
- kvs[:KVKeyCount] = command.count - 2
159
- values = r.select { |i| i }
160
- kvs[:KVHitCount] = values.count
161
-
162
- when :mset, :msetnx
163
- if command[1].is_a?(Array)
164
- kvs[:KVKeyCount] = command[1].count / 2
165
- else
166
- kvs[:KVKeyCount] = (command.count - 1) / 2
167
- end
168
- end # case op
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
174
- rescue StandardError => e
175
- SolarWindsAPM.logger.debug "[solarwinds_apm/redis] Error collecting redis KVs: #{e.message}"
176
- SolarWindsAPM.logger.debug e.backtrace.join('\n')
177
- ensure
178
- return kvs
179
- end
180
-
181
- # Extracts the Key/Values to report from a pipelined
182
- # call to the SolarWinds dashboard.
183
- #
184
- # @param pipeline [Redis::Pipeline] the Redis pipeline instance
185
- # @return [Hash] the Key/Values to report
186
- def extract_pipeline_details(commands, config)
187
- SolarWindsAPM.logger.debug "extract_pipeline_details command => #{commands.inspect}"
188
- kvs = {}
189
-
190
- kvs[:RemoteHost] = config.host
191
- kvs[:Backtrace] = SolarWindsAPM::API.backtrace if SolarWindsAPM::Config[:redis][:collect_backtraces]
192
-
193
- command_count = commands.count
194
- kvs[:KVOpCount] = command_count
195
-
196
- kvs[:KVOp] = if commands.first == :multi
197
- :multi
198
- else
199
- :pipeline
200
- end
201
-
202
- # Report pipelined operations if the number
203
- # of ops is reasonable
204
- if command_count < 12
205
- ops = []
206
- commands.each do |c|
207
- ops << c.first
208
- end
209
- kvs[:KVOps] = ops.join(', ')
210
- end
211
- rescue StandardError => e
212
- SolarWindsAPM.logger.debug "[solarwinds_apm/debug] Error extracting pipelined commands: #{e.message}"
213
- SolarWindsAPM.logger.debug e.backtrace
214
- ensure
215
- return kvs
216
- end
217
-
218
- #
219
- # The wrapper method for RedisClient.call. Here
220
- # (when tracing) we capture KVs to report and pass
221
- # the call along
222
- #
223
- def call_with_sw_apm(command, config, &block)
224
- SolarWindsAPM.logger.debug "call_with_sw_apm command => #{command.inspect}"
225
- if SolarWindsAPM.tracing?
226
- SolarWindsAPM::API.log_entry(:redis, {})
227
-
228
- begin
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}"
232
- r
233
- rescue StandardError => e
234
- SolarWindsAPM::API.log_exception(:redis, e)
235
- raise
236
- ensure
237
- SolarWindsAPM::API.log_exit(:redis, report_kvs)
238
- end
239
-
240
- else
241
- call_without_sw_apm(command, config, &block)
242
- end
243
- end
244
-
245
- #
246
- # The wrapper method for RedisClient.call_pipeline. Here
247
- # (when tracing) we capture KVs to report and pass the call along
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}"
251
- if SolarWindsAPM.tracing?
252
- # Fall back to the raw tracing API so we can pass KVs
253
- # back on exit (a limitation of the SolarWindsAPM::API.trace
254
- # block method) This removes the need for an info
255
- # event to send additonal KVs
256
- SolarWindsAPM::API.log_entry(:redis, {})
257
- report_kvs = extract_pipeline_details(commands, config)
258
- begin
259
- call_pipelined_without_sw_apm(commands, config, &block)
260
- rescue StandardError => e
261
- SolarWindsAPM::API.log_exception(:redis, e)
262
- raise
263
- ensure
264
- SolarWindsAPM::API.log_exit(:redis, report_kvs)
265
- end
266
- else
267
- call_pipelined_without_sw_apm(commands, config, &block)
268
- end
269
- end
270
- end
271
- end
272
- end
273
- end
274
-
275
- if SolarWindsAPM::Config[:redis][:enabled]
276
- if defined?(::RedisClient)
277
- SolarWindsAPM.logger.info "[solarwinds_apm/loading] Instrumenting redis through RedisClient #{RedisClient::VERSION}" if SolarWindsAPM::Config[:verbose]
278
- ::RedisClient.register(SolarWindsAPM::Inst::Redis::Client)
279
- end
280
- end