appoptics_apm-zearn 4.13.1

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.
Files changed (145) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +5 -0
  3. data/.github/ISSUE_TEMPLATE/bug-or-feature-request.md +16 -0
  4. data/.github/workflows/build_and_release_gem.yml +103 -0
  5. data/.github/workflows/build_for_packagecloud.yml +70 -0
  6. data/.github/workflows/docker-images.yml +47 -0
  7. data/.github/workflows/run_cpluplus_tests.yml +73 -0
  8. data/.github/workflows/run_tests.yml +168 -0
  9. data/.github/workflows/scripts/test_install.rb +23 -0
  10. data/.github/workflows/swig/swig-v4.0.2.tar.gz +0 -0
  11. data/.github/workflows/test_on_4_linux.yml +159 -0
  12. data/.gitignore +36 -0
  13. data/.rubocop.yml +29 -0
  14. data/.travis.yml +130 -0
  15. data/.yardopts +6 -0
  16. data/CHANGELOG.md +769 -0
  17. data/CONFIG.md +33 -0
  18. data/Gemfile +14 -0
  19. data/LICENSE +202 -0
  20. data/README.md +393 -0
  21. data/appoptics_apm.gemspec +70 -0
  22. data/bin/appoptics_apm_config +15 -0
  23. data/examples/prepend.rb +13 -0
  24. data/examples/sdk_examples.rb +158 -0
  25. data/ext/oboe_metal/README.md +69 -0
  26. data/ext/oboe_metal/extconf.rb +151 -0
  27. data/ext/oboe_metal/lib/.keep +0 -0
  28. data/ext/oboe_metal/lib/liboboe-1.0-alpine-x86_64.so.0.0.0.sha256 +1 -0
  29. data/ext/oboe_metal/lib/liboboe-1.0-x86_64.so.0.0.0.sha256 +1 -0
  30. data/ext/oboe_metal/noop/noop.c +8 -0
  31. data/ext/oboe_metal/src/README.md +6 -0
  32. data/ext/oboe_metal/src/VERSION +2 -0
  33. data/ext/oboe_metal/src/bson/bson.h +220 -0
  34. data/ext/oboe_metal/src/bson/platform_hacks.h +91 -0
  35. data/ext/oboe_metal/src/frames.cc +246 -0
  36. data/ext/oboe_metal/src/frames.h +40 -0
  37. data/ext/oboe_metal/src/init_appoptics_apm.cc +21 -0
  38. data/ext/oboe_metal/src/logging.cc +95 -0
  39. data/ext/oboe_metal/src/logging.h +35 -0
  40. data/ext/oboe_metal/src/oboe.h +1156 -0
  41. data/ext/oboe_metal/src/oboe_api.cpp +652 -0
  42. data/ext/oboe_metal/src/oboe_api.hpp +431 -0
  43. data/ext/oboe_metal/src/oboe_debug.h +59 -0
  44. data/ext/oboe_metal/src/oboe_swig_wrap.cc +7329 -0
  45. data/ext/oboe_metal/src/profiling.cc +435 -0
  46. data/ext/oboe_metal/src/profiling.h +78 -0
  47. data/ext/oboe_metal/test/CMakeLists.txt +53 -0
  48. data/ext/oboe_metal/test/FindGMock.cmake +43 -0
  49. data/ext/oboe_metal/test/README.md +56 -0
  50. data/ext/oboe_metal/test/frames_test.cc +164 -0
  51. data/ext/oboe_metal/test/profiling_test.cc +93 -0
  52. data/ext/oboe_metal/test/ruby_inc_dir.rb +8 -0
  53. data/ext/oboe_metal/test/ruby_prefix.rb +8 -0
  54. data/ext/oboe_metal/test/ruby_test_helper.rb +67 -0
  55. data/ext/oboe_metal/test/test.h +11 -0
  56. data/ext/oboe_metal/test/test_main.cc +32 -0
  57. data/init.rb +4 -0
  58. data/lib/appoptics_apm/api/layerinit.rb +41 -0
  59. data/lib/appoptics_apm/api/logging.rb +381 -0
  60. data/lib/appoptics_apm/api/memcache.rb +37 -0
  61. data/lib/appoptics_apm/api/metrics.rb +63 -0
  62. data/lib/appoptics_apm/api/tracing.rb +57 -0
  63. data/lib/appoptics_apm/api/util.rb +120 -0
  64. data/lib/appoptics_apm/api.rb +21 -0
  65. data/lib/appoptics_apm/base.rb +231 -0
  66. data/lib/appoptics_apm/config.rb +299 -0
  67. data/lib/appoptics_apm/frameworks/grape.rb +98 -0
  68. data/lib/appoptics_apm/frameworks/padrino.rb +78 -0
  69. data/lib/appoptics_apm/frameworks/rails/inst/action_controller.rb +104 -0
  70. data/lib/appoptics_apm/frameworks/rails/inst/action_controller4.rb +48 -0
  71. data/lib/appoptics_apm/frameworks/rails/inst/action_controller5.rb +50 -0
  72. data/lib/appoptics_apm/frameworks/rails/inst/action_controller6.rb +50 -0
  73. data/lib/appoptics_apm/frameworks/rails/inst/action_controller_api.rb +50 -0
  74. data/lib/appoptics_apm/frameworks/rails/inst/action_view.rb +88 -0
  75. data/lib/appoptics_apm/frameworks/rails/inst/active_record.rb +27 -0
  76. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/mysql.rb +43 -0
  77. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/mysql2.rb +29 -0
  78. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/postgresql.rb +31 -0
  79. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils.rb +119 -0
  80. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +114 -0
  81. data/lib/appoptics_apm/frameworks/rails/inst/logger_formatters.rb +27 -0
  82. data/lib/appoptics_apm/frameworks/rails.rb +100 -0
  83. data/lib/appoptics_apm/frameworks/sinatra.rb +96 -0
  84. data/lib/appoptics_apm/inst/bunny-client.rb +148 -0
  85. data/lib/appoptics_apm/inst/bunny-consumer.rb +89 -0
  86. data/lib/appoptics_apm/inst/curb.rb +332 -0
  87. data/lib/appoptics_apm/inst/dalli.rb +85 -0
  88. data/lib/appoptics_apm/inst/delayed_job.rb +92 -0
  89. data/lib/appoptics_apm/inst/em-http-request.rb +101 -0
  90. data/lib/appoptics_apm/inst/excon.rb +125 -0
  91. data/lib/appoptics_apm/inst/faraday.rb +106 -0
  92. data/lib/appoptics_apm/inst/graphql.rb +240 -0
  93. data/lib/appoptics_apm/inst/grpc_client.rb +159 -0
  94. data/lib/appoptics_apm/inst/grpc_server.rb +120 -0
  95. data/lib/appoptics_apm/inst/http.rb +81 -0
  96. data/lib/appoptics_apm/inst/httpclient.rb +174 -0
  97. data/lib/appoptics_apm/inst/logger_formatter.rb +50 -0
  98. data/lib/appoptics_apm/inst/logging_log_event.rb +28 -0
  99. data/lib/appoptics_apm/inst/lumberjack_formatter.rb +13 -0
  100. data/lib/appoptics_apm/inst/memcached.rb +86 -0
  101. data/lib/appoptics_apm/inst/mongo.rb +246 -0
  102. data/lib/appoptics_apm/inst/mongo2.rb +225 -0
  103. data/lib/appoptics_apm/inst/moped.rb +466 -0
  104. data/lib/appoptics_apm/inst/rack.rb +182 -0
  105. data/lib/appoptics_apm/inst/rack_cache.rb +35 -0
  106. data/lib/appoptics_apm/inst/redis.rb +274 -0
  107. data/lib/appoptics_apm/inst/resque.rb +151 -0
  108. data/lib/appoptics_apm/inst/rest-client.rb +48 -0
  109. data/lib/appoptics_apm/inst/sequel.rb +178 -0
  110. data/lib/appoptics_apm/inst/sidekiq-client.rb +55 -0
  111. data/lib/appoptics_apm/inst/sidekiq-worker.rb +66 -0
  112. data/lib/appoptics_apm/inst/twitter-cassandra.rb +294 -0
  113. data/lib/appoptics_apm/inst/typhoeus.rb +108 -0
  114. data/lib/appoptics_apm/instrumentation.rb +22 -0
  115. data/lib/appoptics_apm/loading.rb +65 -0
  116. data/lib/appoptics_apm/logger.rb +14 -0
  117. data/lib/appoptics_apm/noop/README.md +9 -0
  118. data/lib/appoptics_apm/noop/context.rb +27 -0
  119. data/lib/appoptics_apm/noop/metadata.rb +25 -0
  120. data/lib/appoptics_apm/noop/profiling.rb +21 -0
  121. data/lib/appoptics_apm/oboe_init_options.rb +211 -0
  122. data/lib/appoptics_apm/ruby.rb +35 -0
  123. data/lib/appoptics_apm/sdk/current_trace.rb +77 -0
  124. data/lib/appoptics_apm/sdk/custom_metrics.rb +94 -0
  125. data/lib/appoptics_apm/sdk/logging.rb +37 -0
  126. data/lib/appoptics_apm/sdk/tracing.rb +434 -0
  127. data/lib/appoptics_apm/support/profiling.rb +18 -0
  128. data/lib/appoptics_apm/support/transaction_metrics.rb +67 -0
  129. data/lib/appoptics_apm/support/transaction_settings.rb +219 -0
  130. data/lib/appoptics_apm/support/x_trace_options.rb +110 -0
  131. data/lib/appoptics_apm/support_report.rb +119 -0
  132. data/lib/appoptics_apm/test.rb +95 -0
  133. data/lib/appoptics_apm/thread_local.rb +26 -0
  134. data/lib/appoptics_apm/util.rb +326 -0
  135. data/lib/appoptics_apm/version.rb +16 -0
  136. data/lib/appoptics_apm/xtrace.rb +115 -0
  137. data/lib/appoptics_apm.rb +77 -0
  138. data/lib/joboe_metal.rb +212 -0
  139. data/lib/oboe.rb +7 -0
  140. data/lib/oboe_metal.rb +172 -0
  141. data/lib/rails/generators/appoptics_apm/install_generator.rb +47 -0
  142. data/lib/rails/generators/appoptics_apm/templates/appoptics_apm_initializer.rb +425 -0
  143. data/log/.keep +0 -0
  144. data/yardoc_frontpage.md +26 -0
  145. metadata +231 -0
@@ -0,0 +1,274 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
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
+ # We wrap two of the Redis methods to instrument
61
+ # operations
62
+ AppOpticsAPM::Util.method_alias(klass, :call, ::Redis::Client)
63
+ AppOpticsAPM::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 AppOptics
68
+ # dashboard.
69
+ #
70
+ # @param command [Array] the Redis operation array
71
+ # @param r [Return] the return value from the operation
72
+ # @return [Hash] the Key/Values to report
73
+ def extract_trace_details(command, r)
74
+ kvs = {}
75
+ op = command.first
76
+
77
+ kvs[:KVOp] = command[0]
78
+ kvs[:RemoteHost] = @options[: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].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] = AppOpticsAPM::API.backtrace if AppOpticsAPM::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
+ rescue StandardError => e
171
+ AppOpticsAPM.logger.debug "[appoptics_apm/redis] Error collecting redis KVs: #{e.message}"
172
+ AppOpticsAPM.logger.debug e.backtrace.join('\n')
173
+ ensure
174
+ return kvs
175
+ end
176
+
177
+ # Extracts the Key/Values to report from a pipelined
178
+ # call to the AppOptics dashboard.
179
+ #
180
+ # @param pipeline [Redis::Pipeline] the Redis pipeline instance
181
+ # @return [Hash] the Key/Values to report
182
+ def extract_pipeline_details(pipeline)
183
+ kvs = {}
184
+
185
+ kvs[:RemoteHost] = @options[:host]
186
+ kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:redis][:collect_backtraces]
187
+
188
+ command_count = pipeline.commands.count
189
+ kvs[:KVOpCount] = command_count
190
+
191
+ kvs[:KVOp] = if pipeline.commands.first == :multi
192
+ :multi
193
+ else
194
+ :pipeline
195
+ end
196
+
197
+ # Report pipelined operations if the number
198
+ # of ops is reasonable
199
+ if command_count < 12
200
+ ops = []
201
+ pipeline.commands.each do |c|
202
+ ops << c.first
203
+ end
204
+ kvs[:KVOps] = ops.join(', ')
205
+ end
206
+ rescue StandardError => e
207
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] Error extracting pipelined commands: #{e.message}"
208
+ AppOpticsAPM.logger.debug e.backtrace
209
+ ensure
210
+ return kvs
211
+ end
212
+
213
+ #
214
+ # The wrapper method for Redis::Client.call. Here
215
+ # (when tracing) we capture KVs to report and pass
216
+ # the call along
217
+ #
218
+ def call_with_appoptics(command, &block)
219
+ if AppOpticsAPM.tracing?
220
+ AppOpticsAPM::API.log_entry(:redis, {})
221
+
222
+ begin
223
+ r = call_without_appoptics(command, &block)
224
+ report_kvs = extract_trace_details(command, r)
225
+ r
226
+ rescue StandardError => e
227
+ AppOpticsAPM::API.log_exception(:redis, e)
228
+ raise
229
+ ensure
230
+ AppOpticsAPM::API.log_exit(:redis, report_kvs)
231
+ end
232
+
233
+ else
234
+ call_without_appoptics(command, &block)
235
+ end
236
+ end
237
+
238
+ #
239
+ # The wrapper method for Redis::Client.call_pipeline. Here
240
+ # (when tracing) we capture KVs to report and pass the call along
241
+ #
242
+ def call_pipeline_with_appoptics(pipeline)
243
+ if AppOpticsAPM.tracing?
244
+ # Fall back to the raw tracing API so we can pass KVs
245
+ # back on exit (a limitation of the AppOpticsAPM::API.trace
246
+ # block method) This removes the need for an info
247
+ # event to send additonal KVs
248
+ AppOpticsAPM::API.log_entry(:redis, {})
249
+
250
+ report_kvs = extract_pipeline_details(pipeline)
251
+
252
+ begin
253
+ call_pipeline_without_appoptics(pipeline)
254
+ rescue StandardError => e
255
+ AppOpticsAPM::API.log_exception(:redis, e)
256
+ raise
257
+ ensure
258
+ AppOpticsAPM::API.log_exit(:redis, report_kvs)
259
+ end
260
+ else
261
+ call_pipeline_without_appoptics(pipeline)
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
268
+
269
+ if AppOpticsAPM::Config[:redis][:enabled]
270
+ if defined?(Redis) && Gem::Version.new(Redis::VERSION) >= Gem::Version.new('3.0.0')
271
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting redis' if AppOpticsAPM::Config[:verbose]
272
+ AppOpticsAPM::Util.send_include(Redis::Client, AppOpticsAPM::Inst::Redis::Client)
273
+ end
274
+ end
@@ -0,0 +1,151 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ require 'socket'
5
+ require 'json'
6
+
7
+ module AppOpticsAPM
8
+ module Inst
9
+ module ResqueClient
10
+ def self.included(klass)
11
+ klass.send :extend, ::Resque
12
+ AppOpticsAPM::Util.method_alias(klass, :enqueue, ::Resque)
13
+ AppOpticsAPM::Util.method_alias(klass, :enqueue_to, ::Resque)
14
+ AppOpticsAPM::Util.method_alias(klass, :dequeue, ::Resque)
15
+ end
16
+
17
+ def extract_trace_details(op, klass, args)
18
+ report_kvs = {}
19
+
20
+ begin
21
+ report_kvs[:Spec] = :pushq
22
+ report_kvs[:Flavor] = :resque
23
+ report_kvs[:JobName] = klass.to_s
24
+
25
+ if AppOpticsAPM::Config[:resqueclient][:log_args]
26
+ kv_args = args.to_json
27
+
28
+ # Limit the argument json string to 1024 bytes
29
+ if kv_args.length > 1024
30
+ report_kvs[:Args] = kv_args[0..1023] + '...[snipped]'
31
+ else
32
+ report_kvs[:Args] = kv_args
33
+ end
34
+ end
35
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:resqueclient][:collect_backtraces]
36
+ report_kvs[:Queue] = klass.instance_variable_get(:@queue)
37
+ rescue => e
38
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" if AppOpticsAPM::Config[:verbose]
39
+ end
40
+
41
+ report_kvs
42
+ end
43
+
44
+ def enqueue_with_appoptics(klass, *args)
45
+ if AppOpticsAPM.tracing?
46
+ report_kvs = extract_trace_details(:enqueue, klass, args)
47
+
48
+ AppOpticsAPM::API.trace(:'resque-client', report_kvs, :enqueue) do
49
+ enqueue_without_appoptics(klass, *args)
50
+ end
51
+ else
52
+ enqueue_without_appoptics(klass, *args)
53
+ end
54
+ end
55
+
56
+ def enqueue_to_with_appoptics(queue, klass, *args)
57
+ if AppOpticsAPM.tracing? && !AppOpticsAPM.tracing_layer_op?(:enqueue)
58
+ report_kvs = extract_trace_details(:enqueue_to, klass, args)
59
+ report_kvs[:Queue] = queue.to_s if queue
60
+
61
+ AppOpticsAPM::API.trace(:'resque-client', report_kvs) do
62
+ enqueue_to_without_appoptics(queue, klass, *args)
63
+ end
64
+ else
65
+ enqueue_to_without_appoptics(queue, klass, *args)
66
+ end
67
+ end
68
+
69
+ def dequeue_with_appoptics(klass, *args)
70
+ if AppOpticsAPM.tracing?
71
+ report_kvs = extract_trace_details(:dequeue, klass, args)
72
+
73
+ AppOpticsAPM::API.trace(:'resque-client', report_kvs) do
74
+ dequeue_without_appoptics(klass, *args)
75
+ end
76
+ else
77
+ dequeue_without_appoptics(klass, *args)
78
+ end
79
+ end
80
+ end
81
+
82
+ module ResqueWorker
83
+ def self.included(klass)
84
+ AppOpticsAPM::Util.method_alias(klass, :perform, ::Resque::Worker)
85
+ end
86
+
87
+ def perform_with_appoptics(job)
88
+ report_kvs = {}
89
+
90
+ begin
91
+ report_kvs[:Spec] = :job
92
+ report_kvs[:Flavor] = :resque
93
+ report_kvs[:JobName] = job.payload['class'].to_s
94
+ report_kvs[:Queue] = job.queue
95
+
96
+ # Set these keys for the ability to separate out
97
+ # background tasks into a separate app on the server-side UI
98
+
99
+ report_kvs[:'HTTP-Host'] = Socket.gethostname
100
+ report_kvs[:Controller] = "Resque_#{job.queue}"
101
+ report_kvs[:Action] = job.payload['class'].to_s
102
+ report_kvs[:URL] = "/resque/#{job.queue}/#{job.payload['class']}"
103
+
104
+ if AppOpticsAPM::Config[:resqueworker][:log_args]
105
+ kv_args = job.payload['args'].to_json
106
+
107
+ # Limit the argument json string to 1024 bytes
108
+ if kv_args.length > 1024
109
+ report_kvs[:Args] = kv_args[0..1023] + '...[snipped]'
110
+ else
111
+ report_kvs[:Args] = kv_args
112
+ end
113
+ end
114
+
115
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:resqueworker][:collect_backtraces]
116
+ rescue => e
117
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" if AppOpticsAPM::Config[:verbose]
118
+ end
119
+
120
+ AppOpticsAPM::SDK.start_trace(:'resque-worker', nil, report_kvs) do
121
+ perform_without_appoptics(job)
122
+ end
123
+ end
124
+ end
125
+
126
+ module ResqueJob
127
+ def self.included(klass)
128
+ AppOpticsAPM::Util.method_alias(klass, :fail, ::Resque::Job)
129
+ end
130
+
131
+ def fail_with_appoptics(exception)
132
+ if AppOpticsAPM.tracing?
133
+ AppOpticsAPM::API.log_exception(:resque, exception)
134
+ end
135
+ fail_without_appoptics(exception)
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ if defined?(Resque)
142
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting resque' if AppOpticsAPM::Config[:verbose]
143
+
144
+ AppOpticsAPM::Util.send_include(Resque, AppOpticsAPM::Inst::ResqueClient) if AppOpticsAPM::Config[:resqueclient][:enabled]
145
+ AppOpticsAPM::Util.send_include(Resque::Worker, AppOpticsAPM::Inst::ResqueWorker) if AppOpticsAPM::Config[:resqueworker][:enabled]
146
+ if AppOpticsAPM::Config[:resqueclient][:enabled] || AppOpticsAPM::Config[:resqueworker][:enabled]
147
+ AppOpticsAPM::Util.send_include(Resque::Job, AppOpticsAPM::Inst::ResqueJob)
148
+ end
149
+ end
150
+
151
+
@@ -0,0 +1,48 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ module Inst
6
+ module RestClientRequest
7
+ def self.included(klass)
8
+ AppOpticsAPM::Util.method_alias(klass, :execute, ::RestClient::Request)
9
+ end
10
+
11
+ ##
12
+ # execute_with_appoptics
13
+ #
14
+ # The wrapper method for RestClient::Request.execute
15
+ #
16
+ def execute_with_appoptics(&block)
17
+ blacklisted = AppOpticsAPM::API.blacklisted?(url)
18
+
19
+ unless AppOpticsAPM.tracing?
20
+ xtrace = AppOpticsAPM::Context.toString
21
+ @processed_headers['X-Trace'] = AppOpticsAPM::Context.toString if AppOpticsAPM::XTrace.valid?(xtrace) && !blacklisted
22
+ return execute_without_appoptics(&block)
23
+ end
24
+
25
+ begin
26
+ kvs = {}
27
+ kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:rest_client][:collect_backtraces]
28
+ AppOpticsAPM::API.log_entry('rest-client', kvs)
29
+
30
+ @processed_headers['X-Trace'] = AppOpticsAPM::Context.toString unless blacklisted
31
+
32
+ # The core rest-client call
33
+ execute_without_appoptics(&block)
34
+ rescue => e
35
+ AppOpticsAPM::API.log_exception('rest-client', e)
36
+ raise e
37
+ ensure
38
+ AppOpticsAPM::API.log_exit('rest-client')
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ if defined?(RestClient) && AppOpticsAPM::Config[:rest_client][:enabled]
46
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting rest-client' if AppOpticsAPM::Config[:verbose]
47
+ AppOpticsAPM::Util.send_include(RestClient::Request, AppOpticsAPM::Inst::RestClientRequest)
48
+ end
@@ -0,0 +1,178 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ module Inst
6
+ ##
7
+ # AppOpticsAPM::Inst::Sequel
8
+ #
9
+ # The common (shared) methods used by the AppOpticsAPM Sequel instrumentation
10
+ # across multiple modules/classes.
11
+ #
12
+ module Sequel
13
+ ##
14
+ # extract_trace_details
15
+ #
16
+ # Given SQL and the options hash, this method extracts the interesting
17
+ # bits for reporting to the AppOptics dashboard.
18
+ #
19
+ def extract_trace_details(sql, opts)
20
+ kvs = {}
21
+
22
+ if !sql.is_a?(String)
23
+ kvs[:IsPreparedStatement] = true
24
+ end
25
+
26
+ if ::Sequel::VERSION > '4.36.0' && !sql.is_a?(String)
27
+ # In 4.37.0, sql was converted to a prepared statement object
28
+ sql = sql.prepared_sql unless sql.is_a?(Symbol)
29
+ end
30
+
31
+ if AppOpticsAPM::Config[:sanitize_sql]
32
+ # Sanitize SQL and don't report binds
33
+ if sql.is_a?(Symbol)
34
+ kvs[:Query] = sql
35
+ else
36
+ kvs[:Query] = AppOpticsAPM::Util.sanitize_sql(sql)
37
+ end
38
+ else
39
+ # Report raw SQL and any binds if they exist
40
+ kvs[:Query] = sql.to_s
41
+ kvs[:QueryArgs] = opts[:arguments] if opts.is_a?(Hash) && opts.key?(:arguments)
42
+ end
43
+
44
+ kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:sequel][:collect_backtraces]
45
+
46
+ if ::Sequel::VERSION < '3.41.0' && !(self.class.to_s =~ /Dataset$/)
47
+ db_opts = @opts
48
+ elsif @pool
49
+ db_opts = @pool.db.opts
50
+ else
51
+ db_opts = @db.opts
52
+ end
53
+
54
+ kvs[:Database] = db_opts[:database]
55
+ kvs[:RemoteHost] = db_opts[:host]
56
+ kvs[:RemotePort] = db_opts[:port] if db_opts.key?(:port)
57
+ kvs[:Flavor] = db_opts[:adapter]
58
+ rescue => e
59
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug Error capturing Sequel KVs: #{e.message}" if AppOpticsAPM::Config[:verbose]
60
+ ensure
61
+ return kvs
62
+ end
63
+
64
+ ##
65
+ # exec_with_appoptics
66
+ #
67
+ # This method wraps and routes the call to the specified
68
+ # original method call
69
+ #
70
+ def exec_with_appoptics(method, sql, opts = ::Sequel::OPTS, &block)
71
+ if AppOpticsAPM.tracing?
72
+ kvs = extract_trace_details(sql, opts)
73
+ AppOpticsAPM::API.log_entry(:sequel, kvs)
74
+ end
75
+
76
+ send(method, sql, opts, &block)
77
+ rescue => e
78
+ AppOpticsAPM::API.log_exception(:sequel, e)
79
+ raise e
80
+ ensure
81
+ AppOpticsAPM::API.log_exit(:sequel)
82
+ end
83
+ end
84
+
85
+ module SequelDatabase
86
+ include AppOpticsAPM::Inst::Sequel
87
+
88
+ def self.included(klass)
89
+ AppOpticsAPM::Util.method_alias(klass, :run, ::Sequel::Database)
90
+ AppOpticsAPM::Util.method_alias(klass, :execute_ddl, ::Sequel::Database)
91
+ AppOpticsAPM::Util.method_alias(klass, :execute_dui, ::Sequel::Database)
92
+ AppOpticsAPM::Util.method_alias(klass, :execute_insert, ::Sequel::Database)
93
+ end
94
+
95
+ def run_with_appoptics(sql, opts = ::Sequel::OPTS)
96
+ if AppOpticsAPM.tracing?
97
+ kvs = extract_trace_details(sql, opts)
98
+ AppOpticsAPM::API.log_entry(:sequel, kvs)
99
+ end
100
+
101
+ run_without_appoptics(sql, opts)
102
+ rescue => e
103
+ AppOpticsAPM::API.log_exception(:sequel, e)
104
+ raise e
105
+ ensure
106
+ AppOpticsAPM::API.log_exit(:sequel)
107
+ end
108
+
109
+ def execute_ddl_with_appoptics(sql, opts = ::Sequel::OPTS, &block)
110
+ # If we're already tracing a sequel operation, then this call likely came
111
+ # from Sequel::Dataset. In this case, just pass it on.
112
+ return execute_ddl_without_appoptics(sql, opts, &block) if AppOpticsAPM.tracing_layer?(:sequel)
113
+
114
+ exec_with_appoptics(:execute_ddl_without_appoptics, sql, opts, &block)
115
+ end
116
+
117
+ def execute_dui_with_appoptics(sql, opts = ::Sequel::OPTS, &block)
118
+ # If we're already tracing a sequel operation, then this call likely came
119
+ # from Sequel::Dataset. In this case, just pass it on.
120
+ return execute_dui_without_appoptics(sql, opts, &block) if AppOpticsAPM.tracing_layer?(:sequel)
121
+
122
+ exec_with_appoptics(:execute_dui_without_appoptics, sql, opts, &block)
123
+ end
124
+
125
+ def execute_insert_with_appoptics(sql, opts = ::Sequel::OPTS, &block)
126
+ # If we're already tracing a sequel operation, then this call likely came
127
+ # from Sequel::Dataset. In this case, just pass it on.
128
+ return execute_insert_without_appoptics(sql, opts, &block) if AppOpticsAPM.tracing_layer?(:sequel)
129
+
130
+ exec_with_appoptics(:execute_insert_without_appoptics, sql, opts, &block)
131
+ end
132
+ end # module SequelDatabase
133
+
134
+ module SequelDataset
135
+ include AppOpticsAPM::Inst::Sequel
136
+
137
+ def self.included(klass)
138
+ AppOpticsAPM::Util.method_alias(klass, :execute, ::Sequel::Dataset)
139
+ AppOpticsAPM::Util.method_alias(klass, :execute_ddl, ::Sequel::Dataset)
140
+ AppOpticsAPM::Util.method_alias(klass, :execute_dui, ::Sequel::Dataset)
141
+ AppOpticsAPM::Util.method_alias(klass, :execute_insert, ::Sequel::Dataset)
142
+ end
143
+
144
+ def execute_with_appoptics(sql, opts = ::Sequel::OPTS, &block)
145
+ exec_with_appoptics(:execute_without_appoptics, sql, opts, &block)
146
+ end
147
+
148
+ def execute_ddl_with_appoptics(sql, opts = ::Sequel::OPTS, &block)
149
+ exec_with_appoptics(:execute_ddl_without_appoptics, sql, opts, &block)
150
+ end
151
+
152
+ def execute_dui_with_appoptics(sql, opts = ::Sequel::OPTS, &block)
153
+ exec_with_appoptics(:execute_dui_without_appoptics, sql, opts, &block)
154
+ end
155
+
156
+ def execute_insert_with_appoptics(sql, opts = ::Sequel::OPTS, &block)
157
+ exec_with_appoptics(:execute_insert_without_appoptics, sql, opts, &block)
158
+ end
159
+
160
+ end # module SequelDataset
161
+ end # module Inst
162
+ end # module AppOpticsAPM
163
+
164
+ if AppOpticsAPM::Config[:sequel][:enabled]
165
+ if defined?(::Sequel) && ::Sequel::VERSION < '4.0.0'
166
+ # For versions before 4.0.0, Sequel::OPTS wasn't defined.
167
+ # Define it as an empty hash for backwards compatibility.
168
+ module ::Sequel
169
+ OPTS = {}
170
+ end
171
+ end
172
+
173
+ if defined?(::Sequel)
174
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting sequel' if AppOpticsAPM::Config[:verbose]
175
+ AppOpticsAPM::Util.send_include(::Sequel::Database, AppOpticsAPM::Inst::SequelDatabase)
176
+ AppOpticsAPM::Util.send_include(::Sequel::Dataset, AppOpticsAPM::Inst::SequelDataset)
177
+ end
178
+ end