appoptics_apm 4.0.2

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 (226) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +43 -0
  3. data/.dockerignore +5 -0
  4. data/.gitignore +23 -0
  5. data/.rubocop.yml +5 -0
  6. data/.travis.yml +82 -0
  7. data/CHANGELOG.md +769 -0
  8. data/CONFIG.md +33 -0
  9. data/Dockerfile +41 -0
  10. data/Dockerfile_test +66 -0
  11. data/Gemfile +41 -0
  12. data/LICENSE +193 -0
  13. data/README.md +351 -0
  14. data/Rakefile +202 -0
  15. data/Vagrantfile +67 -0
  16. data/appoptics_apm.gemspec +55 -0
  17. data/build_gems.sh +15 -0
  18. data/docker-compose.yml +73 -0
  19. data/examples/DNT.md +35 -0
  20. data/examples/carrying_context.rb +220 -0
  21. data/examples/instrumenting_metal_controller.rb +8 -0
  22. data/examples/puma_on_heroku_config.rb +17 -0
  23. data/examples/tracing_async_threads.rb +124 -0
  24. data/examples/tracing_background_jobs.rb +53 -0
  25. data/examples/tracing_forked_processes.rb +99 -0
  26. data/examples/unicorn_on_heroku_config.rb +28 -0
  27. data/ext/oboe_metal/extconf.rb +54 -0
  28. data/ext/oboe_metal/lib/.keep +0 -0
  29. data/ext/oboe_metal/lib/liboboe-1.0.so.0.0.0 +0 -0
  30. data/ext/oboe_metal/noop/noop.c +7 -0
  31. data/ext/oboe_metal/src/VERSION +1 -0
  32. data/ext/oboe_metal/src/bson/bson.h +221 -0
  33. data/ext/oboe_metal/src/bson/platform_hacks.h +91 -0
  34. data/ext/oboe_metal/src/oboe.h +883 -0
  35. data/ext/oboe_metal/src/oboe.hpp +793 -0
  36. data/ext/oboe_metal/src/oboe_debug.h +50 -0
  37. data/ext/oboe_metal/src/oboe_wrap.cxx +6088 -0
  38. data/ext/oboe_metal/tests/test.rb +11 -0
  39. data/gemfiles/delayed_job.gemfile +36 -0
  40. data/gemfiles/frameworks.gemfile +44 -0
  41. data/gemfiles/instrumentation_mocked.gemfile +29 -0
  42. data/gemfiles/libraries.gemfile +85 -0
  43. data/gemfiles/rails23.gemfile +39 -0
  44. data/gemfiles/rails30.gemfile +42 -0
  45. data/gemfiles/rails31.gemfile +44 -0
  46. data/gemfiles/rails32.gemfile +54 -0
  47. data/gemfiles/rails40.gemfile +27 -0
  48. data/gemfiles/rails41.gemfile +27 -0
  49. data/gemfiles/rails42.gemfile +35 -0
  50. data/gemfiles/rails50.gemfile +44 -0
  51. data/gemfiles/rails51.gemfile +44 -0
  52. data/get_version.rb +5 -0
  53. data/init.rb +4 -0
  54. data/lib/appoptics_apm/api/layerinit.rb +39 -0
  55. data/lib/appoptics_apm/api/logging.rb +359 -0
  56. data/lib/appoptics_apm/api/memcache.rb +34 -0
  57. data/lib/appoptics_apm/api/profiling.rb +201 -0
  58. data/lib/appoptics_apm/api/tracing.rb +152 -0
  59. data/lib/appoptics_apm/api/util.rb +128 -0
  60. data/lib/appoptics_apm/api.rb +18 -0
  61. data/lib/appoptics_apm/base.rb +252 -0
  62. data/lib/appoptics_apm/config.rb +281 -0
  63. data/lib/appoptics_apm/frameworks/grape.rb +93 -0
  64. data/lib/appoptics_apm/frameworks/padrino/templates.rb +58 -0
  65. data/lib/appoptics_apm/frameworks/padrino.rb +52 -0
  66. data/lib/appoptics_apm/frameworks/rails/inst/action_controller.rb +106 -0
  67. data/lib/appoptics_apm/frameworks/rails/inst/action_controller2.rb +61 -0
  68. data/lib/appoptics_apm/frameworks/rails/inst/action_controller3.rb +58 -0
  69. data/lib/appoptics_apm/frameworks/rails/inst/action_controller4.rb +48 -0
  70. data/lib/appoptics_apm/frameworks/rails/inst/action_controller5.rb +50 -0
  71. data/lib/appoptics_apm/frameworks/rails/inst/action_controller_api.rb +50 -0
  72. data/lib/appoptics_apm/frameworks/rails/inst/action_view.rb +58 -0
  73. data/lib/appoptics_apm/frameworks/rails/inst/action_view_2x.rb +56 -0
  74. data/lib/appoptics_apm/frameworks/rails/inst/action_view_30.rb +50 -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 +28 -0
  78. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/postgresql.rb +30 -0
  79. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils.rb +120 -0
  80. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +101 -0
  81. data/lib/appoptics_apm/frameworks/rails.rb +116 -0
  82. data/lib/appoptics_apm/frameworks/sinatra/templates.rb +56 -0
  83. data/lib/appoptics_apm/frameworks/sinatra.rb +71 -0
  84. data/lib/appoptics_apm/inst/bunny-client.rb +148 -0
  85. data/lib/appoptics_apm/inst/bunny-consumer.rb +92 -0
  86. data/lib/appoptics_apm/inst/curb.rb +329 -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 +105 -0
  90. data/lib/appoptics_apm/inst/excon.rb +130 -0
  91. data/lib/appoptics_apm/inst/faraday.rb +77 -0
  92. data/lib/appoptics_apm/inst/http.rb +83 -0
  93. data/lib/appoptics_apm/inst/httpclient.rb +176 -0
  94. data/lib/appoptics_apm/inst/memcache.rb +102 -0
  95. data/lib/appoptics_apm/inst/memcached.rb +94 -0
  96. data/lib/appoptics_apm/inst/mongo.rb +242 -0
  97. data/lib/appoptics_apm/inst/mongo2.rb +225 -0
  98. data/lib/appoptics_apm/inst/moped.rb +466 -0
  99. data/lib/appoptics_apm/inst/rack.rb +146 -0
  100. data/lib/appoptics_apm/inst/redis.rb +275 -0
  101. data/lib/appoptics_apm/inst/resque.rb +151 -0
  102. data/lib/appoptics_apm/inst/rest-client.rb +50 -0
  103. data/lib/appoptics_apm/inst/sequel.rb +178 -0
  104. data/lib/appoptics_apm/inst/sidekiq-client.rb +53 -0
  105. data/lib/appoptics_apm/inst/sidekiq-worker.rb +67 -0
  106. data/lib/appoptics_apm/inst/twitter-cassandra.rb +294 -0
  107. data/lib/appoptics_apm/inst/typhoeus.rb +113 -0
  108. data/lib/appoptics_apm/instrumentation.rb +22 -0
  109. data/lib/appoptics_apm/legacy_method_profiling.rb +97 -0
  110. data/lib/appoptics_apm/loading.rb +66 -0
  111. data/lib/appoptics_apm/logger.rb +41 -0
  112. data/lib/appoptics_apm/method_profiling.rb +33 -0
  113. data/lib/appoptics_apm/ruby.rb +35 -0
  114. data/lib/appoptics_apm/support.rb +135 -0
  115. data/lib/appoptics_apm/test.rb +94 -0
  116. data/lib/appoptics_apm/thread_local.rb +26 -0
  117. data/lib/appoptics_apm/util.rb +312 -0
  118. data/lib/appoptics_apm/version.rb +15 -0
  119. data/lib/appoptics_apm/xtrace.rb +103 -0
  120. data/lib/appoptics_apm.rb +72 -0
  121. data/lib/joboe_metal.rb +214 -0
  122. data/lib/oboe/README +2 -0
  123. data/lib/oboe/backward_compatibility.rb +80 -0
  124. data/lib/oboe/inst/rack.rb +11 -0
  125. data/lib/oboe.rb +7 -0
  126. data/lib/oboe_metal.rb +187 -0
  127. data/lib/rails/generators/appoptics_apm/install_generator.rb +45 -0
  128. data/lib/rails/generators/appoptics_apm/templates/appoptics_apm_initializer.rb +222 -0
  129. data/ruby_setup.sh +47 -0
  130. data/run_docker_build_gem_upload_to_packagecloud.sh +20 -0
  131. data/run_tests_docker.rb +32 -0
  132. data/test/benchmark/README.md +65 -0
  133. data/test/benchmark/logging_bench.rb +54 -0
  134. data/test/benchmark/with_libraries_gemfile/bunny_bench.rb +69 -0
  135. data/test/benchmark/with_rails5x_gemfile/action_controller5x_bench.rb +43 -0
  136. data/test/frameworks/apps/grape_nested.rb +33 -0
  137. data/test/frameworks/apps/grape_simple.rb +80 -0
  138. data/test/frameworks/apps/padrino_simple.rb +80 -0
  139. data/test/frameworks/apps/sinatra_simple.rb +55 -0
  140. data/test/frameworks/grape_test.rb +286 -0
  141. data/test/frameworks/padrino_test.rb +222 -0
  142. data/test/frameworks/rails3x_test.rb +554 -0
  143. data/test/frameworks/rails4x_test.rb +570 -0
  144. data/test/frameworks/rails5x_api_test.rb +210 -0
  145. data/test/frameworks/rails5x_test.rb +376 -0
  146. data/test/frameworks/rails_shared_tests.rb +172 -0
  147. data/test/frameworks/sinatra_test.rb +140 -0
  148. data/test/instrumentation/bunny_client_test.rb +276 -0
  149. data/test/instrumentation/bunny_consumer_test.rb +204 -0
  150. data/test/instrumentation/curb_test.rb +398 -0
  151. data/test/instrumentation/dalli_test.rb +177 -0
  152. data/test/instrumentation/em_http_request_test.rb +89 -0
  153. data/test/instrumentation/excon_test.rb +231 -0
  154. data/test/instrumentation/faraday_test.rb +228 -0
  155. data/test/instrumentation/http_test.rb +143 -0
  156. data/test/instrumentation/httpclient_test.rb +320 -0
  157. data/test/instrumentation/memcache_test.rb +260 -0
  158. data/test/instrumentation/memcached_test.rb +229 -0
  159. data/test/instrumentation/mongo_v1_test.rb +479 -0
  160. data/test/instrumentation/mongo_v2_index_test.rb +124 -0
  161. data/test/instrumentation/mongo_v2_test.rb +584 -0
  162. data/test/instrumentation/mongo_v2_view_test.rb +435 -0
  163. data/test/instrumentation/moped_test.rb +517 -0
  164. data/test/instrumentation/rack_test.rb +165 -0
  165. data/test/instrumentation/redis_hashes_test.rb +268 -0
  166. data/test/instrumentation/redis_keys_test.rb +321 -0
  167. data/test/instrumentation/redis_lists_test.rb +310 -0
  168. data/test/instrumentation/redis_misc_test.rb +163 -0
  169. data/test/instrumentation/redis_sets_test.rb +296 -0
  170. data/test/instrumentation/redis_sortedsets_test.rb +328 -0
  171. data/test/instrumentation/redis_strings_test.rb +349 -0
  172. data/test/instrumentation/resque_test.rb +185 -0
  173. data/test/instrumentation/rest-client_test.rb +288 -0
  174. data/test/instrumentation/sequel_mysql2_test.rb +353 -0
  175. data/test/instrumentation/sequel_mysql_test.rb +334 -0
  176. data/test/instrumentation/sequel_pg_test.rb +336 -0
  177. data/test/instrumentation/sidekiq-client_test.rb +159 -0
  178. data/test/instrumentation/sidekiq-worker_test.rb +180 -0
  179. data/test/instrumentation/twitter-cassandra_test.rb +424 -0
  180. data/test/instrumentation/typhoeus_test.rb +284 -0
  181. data/test/jobs/delayed_job/db_worker_job.rb +29 -0
  182. data/test/jobs/delayed_job/error_worker_job.rb +10 -0
  183. data/test/jobs/delayed_job/remote_call_worker_job.rb +20 -0
  184. data/test/jobs/resque/db_worker_job.rb +29 -0
  185. data/test/jobs/resque/error_worker_job.rb +10 -0
  186. data/test/jobs/resque/remote_call_worker_job.rb +20 -0
  187. data/test/jobs/sidekiq/db_worker_job.rb +29 -0
  188. data/test/jobs/sidekiq/error_worker_job.rb +10 -0
  189. data/test/jobs/sidekiq/remote_call_worker_job.rb +20 -0
  190. data/test/minitest_helper.rb +276 -0
  191. data/test/mocked/curb_mocked_test.rb +311 -0
  192. data/test/mocked/excon_mocked_test.rb +166 -0
  193. data/test/mocked/faraday_mocked_test.rb +93 -0
  194. data/test/mocked/http_mocked_test.rb +129 -0
  195. data/test/mocked/httpclient_mocked_test.rb +245 -0
  196. data/test/mocked/rest_client_mocked_test.rb +103 -0
  197. data/test/mocked/typhoeus_mocked_test.rb +192 -0
  198. data/test/models/widget.rb +36 -0
  199. data/test/profiling/legacy_method_profiling_test.rb +201 -0
  200. data/test/profiling/method_profiling_test.rb +631 -0
  201. data/test/queues/delayed_job-client_test.rb +95 -0
  202. data/test/queues/delayed_job-worker_test.rb +91 -0
  203. data/test/reporter/reporter_test.rb +14 -0
  204. data/test/servers/delayed_job.rb +107 -0
  205. data/test/servers/rackapp_8101.rb +29 -0
  206. data/test/servers/rails3x_8140.rb +96 -0
  207. data/test/servers/rails4x_8140.rb +96 -0
  208. data/test/servers/rails5x_8140.rb +95 -0
  209. data/test/servers/rails5x_api_8150.rb +78 -0
  210. data/test/servers/sidekiq.rb +29 -0
  211. data/test/servers/sidekiq.yml +7 -0
  212. data/test/servers/sidekiq_initializer.rb +25 -0
  213. data/test/settings +0 -0
  214. data/test/support/auto_tracing_test.rb +50 -0
  215. data/test/support/backcompat_test.rb +276 -0
  216. data/test/support/config_test.rb +149 -0
  217. data/test/support/dnt_test.rb +98 -0
  218. data/test/support/init_report_test.rb +25 -0
  219. data/test/support/liboboe_settings_test.rb +110 -0
  220. data/test/support/logging_test.rb +130 -0
  221. data/test/support/noop_test.rb +88 -0
  222. data/test/support/sql_sanitize_test.rb +55 -0
  223. data/test/support/tracing_mode_test.rb +33 -0
  224. data/test/support/tvalias_test.rb +15 -0
  225. data/test/support/xtrace_test.rb +41 -0
  226. metadata +475 -0
@@ -0,0 +1,275 @@
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 = [: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
+
80
+ unless NO_KEY_OPS.include?(op) || (command[1].is_a?(Array) && command[1].count > 1)
81
+ if command[1].is_a?(Array)
82
+ kvs[:KVKey] = command[1].first
83
+ else
84
+ kvs[:KVKey] = command[1]
85
+ end
86
+ end
87
+
88
+ if KV_COLLECT_MAP[op]
89
+ # Extract KVs from command for this op
90
+ KV_COLLECT_MAP[op].each { |k, v| kvs[k] = command[v] }
91
+ else
92
+ # This case statement handle special cases not handled
93
+ # by KV_COLLECT_MAP
94
+ case op
95
+ when :set
96
+ if command.count > 3
97
+ if command[3].is_a?(Hash)
98
+ options = command[3]
99
+ kvs[:ex] = options[:ex] if options.key?(:ex)
100
+ kvs[:px] = options[:px] if options.key?(:px)
101
+ kvs[:nx] = options[:nx] if options.key?(:nx)
102
+ kvs[:xx] = options[:xx] if options.key?(:xx)
103
+ else
104
+ options = command[3..-1]
105
+ until (opts = options.shift(2)).empty?
106
+ case opts[0]
107
+ when 'EX' then; kvs[:ex] = opts[1]
108
+ when 'PX' then; kvs[:px] = opts[1]
109
+ when 'NX' then; kvs[:nx] = opts[1]
110
+ when 'XX' then; kvs[:xx] = opts[1]
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ when :get
117
+ kvs[:KVHit] = r.nil? ? 0 : 1
118
+
119
+ when :hdel, :hexists, :hget, :hset, :hsetnx
120
+ kvs[:field] = command[2] unless command[2].is_a?(Array)
121
+ if op == :hget
122
+ kvs[:KVHit] = r.nil? ? 0 : 1
123
+ end
124
+
125
+ when :eval
126
+ if command[1].length > 1024
127
+ kvs[:Script] = command[1][0..1023] + '(...snip...)'
128
+ else
129
+ kvs[:Script] = command[1]
130
+ end
131
+
132
+ when :script
133
+ kvs[:subcommand] = command[1]
134
+ kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:redis][:collect_backtraces]
135
+ if command[1] == 'load'
136
+ if command[1].length > 1024
137
+ kvs[:Script] = command[2][0..1023] + '(...snip...)'
138
+ else
139
+ kvs[:Script] = command[2]
140
+ end
141
+ elsif command[1] == :exists
142
+ if command[2].is_a?(Array)
143
+ kvs[:KVKey] = command[2].inspect
144
+ else
145
+ kvs[:KVKey] = command[2]
146
+ end
147
+ end
148
+
149
+ when :mget
150
+ if command[1].is_a?(Array)
151
+ kvs[:KVKeyCount] = command[1].count
152
+ else
153
+ kvs[:KVKeyCount] = command.count - 1
154
+ end
155
+ values = r.select { |i| i }
156
+ kvs[:KVHitCount] = values.count
157
+
158
+ when :hmget
159
+ kvs[:KVKeyCount] = command.count - 2
160
+ values = r.select { |i| i }
161
+ kvs[:KVHitCount] = values.count
162
+
163
+ when :mset, :msetnx
164
+ if command[1].is_a?(Array)
165
+ kvs[:KVKeyCount] = command[1].count / 2
166
+ else
167
+ kvs[:KVKeyCount] = (command.count - 1) / 2
168
+ end
169
+ end # case op
170
+ end # if KV_COLLECT_MAP[op]
171
+ rescue StandardError => e
172
+ AppOpticsAPM.logger.debug "Error collecting redis KVs: #{e.message}"
173
+ AppOpticsAPM.logger.debug e.backtrace.join('\n')
174
+ ensure
175
+ return kvs
176
+ end
177
+
178
+ # Extracts the Key/Values to report from a pipelined
179
+ # call to the AppOptics dashboard.
180
+ #
181
+ # @param pipeline [Redis::Pipeline] the Redis pipeline instance
182
+ # @return [Hash] the Key/Values to report
183
+ def extract_pipeline_details(pipeline)
184
+ kvs = {}
185
+
186
+ kvs[:RemoteHost] = @options[:host]
187
+ kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:redis][:collect_backtraces]
188
+
189
+ command_count = pipeline.commands.count
190
+ kvs[:KVOpCount] = command_count
191
+
192
+ kvs[:KVOp] = if pipeline.commands.first == :multi
193
+ :multi
194
+ else
195
+ :pipeline
196
+ end
197
+
198
+ # Report pipelined operations if the number
199
+ # of ops is reasonable
200
+ if command_count < 12
201
+ ops = []
202
+ pipeline.commands.each do |c|
203
+ ops << c.first
204
+ end
205
+ kvs[:KVOps] = ops.join(', ')
206
+ end
207
+ rescue StandardError => e
208
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] Error extracting pipelined commands: #{e.message}"
209
+ AppOpticsAPM.logger.debug e.backtrace
210
+ ensure
211
+ return kvs
212
+ end
213
+
214
+ #
215
+ # The wrapper method for Redis::Client.call. Here
216
+ # (when tracing) we capture KVs to report and pass
217
+ # the call along
218
+ #
219
+ def call_with_appoptics(command, &block)
220
+ if AppOpticsAPM.tracing?
221
+ ::AppOpticsAPM::API.log_entry(:redis, {})
222
+
223
+ begin
224
+ r = call_without_appoptics(command, &block)
225
+ report_kvs = extract_trace_details(command, r)
226
+ r
227
+ rescue StandardError => e
228
+ ::AppOpticsAPM::API.log_exception(:redis, e)
229
+ raise
230
+ ensure
231
+ ::AppOpticsAPM::API.log_exit(:redis, report_kvs)
232
+ end
233
+
234
+ else
235
+ call_without_appoptics(command, &block)
236
+ end
237
+ end
238
+
239
+ #
240
+ # The wrapper method for Redis::Client.call_pipeline. Here
241
+ # (when tracing) we capture KVs to report and pass the call along
242
+ #
243
+ def call_pipeline_with_appoptics(pipeline)
244
+ if AppOpticsAPM.tracing?
245
+ # Fall back to the raw tracing API so we can pass KVs
246
+ # back on exit (a limitation of the AppOpticsAPM::API.trace
247
+ # block method) This removes the need for an info
248
+ # event to send additonal KVs
249
+ ::AppOpticsAPM::API.log_entry(:redis, {})
250
+
251
+ report_kvs = extract_pipeline_details(pipeline)
252
+
253
+ begin
254
+ call_pipeline_without_appoptics(pipeline)
255
+ rescue StandardError => e
256
+ ::AppOpticsAPM::API.log_exception(:redis, e)
257
+ raise
258
+ ensure
259
+ ::AppOpticsAPM::API.log_exit(:redis, report_kvs)
260
+ end
261
+ else
262
+ call_pipeline_without_appoptics(pipeline)
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end
269
+
270
+ if AppOpticsAPM::Config[:redis][:enabled]
271
+ if defined?(::Redis) && Gem::Version.new(::Redis::VERSION) >= Gem::Version.new('3.0.0')
272
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting redis' if AppOpticsAPM::Config[:verbose]
273
+ ::AppOpticsAPM::Util.send_include(::Redis::Client, ::AppOpticsAPM::Inst::Redis::Client)
274
+ end
275
+ 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::API.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) && RUBY_VERSION >= '1.9.3'
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,50 @@
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?(uri)
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 AppOpticsAPM::Config[:rest_client][:enabled]
46
+ if defined?(::RestClient)
47
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting rest-client' if AppOpticsAPM::Config[:verbose]
48
+ ::AppOpticsAPM::Util.send_include(::RestClient::Request, ::AppOpticsAPM::Inst::RestClientRequest)
49
+ end
50
+ 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