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,53 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ class SidekiqClient
6
+ def collect_kvs(args)
7
+ begin
8
+ # Attempt to collect up pertinent info. If we hit something unexpected,
9
+ # keep calm and instrument on.
10
+
11
+ report_kvs = {}
12
+ worker_class, msg, queue, _ = args
13
+
14
+ report_kvs[:Spec] = :pushq
15
+ report_kvs[:Flavor] = :sidekiq
16
+ report_kvs[:Queue] = queue
17
+ report_kvs[:Retry] = msg['retry']
18
+ report_kvs[:JobName] = worker_class
19
+ report_kvs[:MsgID] = msg['jid']
20
+ report_kvs[:Args] = msg['args'].to_s[0..1024] if AppOpticsAPM::Config[:sidekiqclient][:log_args]
21
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:sidekiqclient][:collect_backtraces]
22
+ rescue => e
23
+ AppOpticsAPM.logger.warn "[appoptics_apm/sidekiq] Non-fatal error capturing KVs: #{e.message}"
24
+ end
25
+ report_kvs
26
+ end
27
+
28
+ def call(*args)
29
+ # args: 0: worker_class, 1: msg, 2: queue, 3: redis_pool
30
+ if AppOpticsAPM.tracing?
31
+ report_kvs = collect_kvs(args)
32
+ AppOpticsAPM::API.log_entry(:'sidekiq-client', report_kvs)
33
+ args[1]['SourceTrace'] = AppOpticsAPM::Context.toString
34
+ end
35
+
36
+ result = yield
37
+ rescue => e
38
+ AppOpticsAPM::API.log_exception(:'sidekiq-client', e, { :JobID => result['jid'] })
39
+ raise
40
+ ensure
41
+ AppOpticsAPM::API.log_exit(:'sidekiq-client', { :JobID => result['jid'] })
42
+ end
43
+ end
44
+ end
45
+
46
+ if defined?(::Sidekiq) && RUBY_VERSION >= '2.0' && AppOpticsAPM::Config[:sidekiqclient][:enabled]
47
+ ::Sidekiq.configure_client do |config|
48
+ config.client_middleware do |chain|
49
+ ::AppOpticsAPM.logger.info '[appoptics_apm/loading] Adding Sidekiq client middleware' if AppOpticsAPM::Config[:verbose]
50
+ chain.add ::AppOpticsAPM::SidekiqClient
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,67 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ class SidekiqWorker
6
+ def collect_kvs(args)
7
+ begin
8
+ # Attempt to collect up pertinent info. If we hit something unexpected,
9
+ # keep calm and instrument on.
10
+ report_kvs = {}
11
+ worker, msg, queue = args
12
+
13
+ # Background Job Spec KVs
14
+ report_kvs[:Spec] = :job
15
+ report_kvs[:Flavor] = :sidekiq
16
+ report_kvs[:Queue] = queue
17
+ report_kvs[:Retry] = msg['retry']
18
+ report_kvs[:JobName] = worker.class.to_s
19
+ report_kvs[:MsgID] = msg['jid']
20
+ report_kvs[:Args] = msg['args'].to_s[0..1024] if AppOpticsAPM::Config[:sidekiqworker][:log_args]
21
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:sidekiqworker][:collect_backtraces]
22
+
23
+ # Webserver Spec KVs
24
+ report_kvs[:'HTTP-Host'] = Socket.gethostname
25
+ report_kvs[:Controller] = "Sidekiq_#{queue}"
26
+ report_kvs[:Action] = msg['class']
27
+ report_kvs[:URL] = "/sidekiq/#{queue}/#{msg['class']}"
28
+ rescue => e
29
+ AppOpticsAPM.logger.warn "[appoptics_apm/sidekiq] Non-fatal error capturing KVs: #{e.message}"
30
+ end
31
+ report_kvs
32
+ end
33
+
34
+ def call(*args)
35
+ # args: 0: worker, 1: msg, 2: queue
36
+ report_kvs = collect_kvs(args)
37
+
38
+ # Something is happening across Celluloid threads where liboboe settings
39
+ # are being lost. So we re-set the tracing mode to assure
40
+ # we sample as desired. Setting the tracing mode will re-update
41
+ # the liboboe settings.
42
+ AppOpticsAPM::Config[:tracing_mode] = AppOpticsAPM::Config[:tracing_mode]
43
+
44
+ # Continue the trace from the enqueue side?
45
+ if args[1].is_a?(Hash) && AppOpticsAPM::XTrace.valid?(args[1]['SourceTrace'])
46
+ report_kvs[:SourceTrace] = args[1]['SourceTrace']
47
+ end
48
+
49
+ result = AppOpticsAPM::API.start_trace(:'sidekiq-worker', nil, report_kvs) do
50
+ yield
51
+ end
52
+
53
+ result[0]
54
+ end
55
+ end
56
+ end
57
+
58
+ if defined?(::Sidekiq) && RUBY_VERSION >= '2.0' && AppOpticsAPM::Config[:sidekiqworker][:enabled]
59
+ ::AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting sidekiq' if AppOpticsAPM::Config[:verbose]
60
+
61
+ ::Sidekiq.configure_server do |config|
62
+ config.server_middleware do |chain|
63
+ ::AppOpticsAPM.logger.info '[appoptics_apm/loading] Adding Sidekiq worker middleware' if AppOpticsAPM::Config[:verbose]
64
+ chain.add ::AppOpticsAPM::SidekiqWorker
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,294 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ module Inst
6
+ module Cassandra
7
+ def extract_trace_details(op, column_family, keys, args, options = {})
8
+ report_kvs = {}
9
+
10
+ begin
11
+ report_kvs[:Op] = op.to_s
12
+ report_kvs[:Cf] = column_family.to_s if column_family
13
+ report_kvs[:Key] = keys.inspect if keys
14
+
15
+ # Open issue - how to handle multiple Cassandra servers
16
+ report_kvs[:RemoteHost], report_kvs[:RemotePort] = @servers.first.split(':')
17
+
18
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:cassandra][:collect_backtraces]
19
+
20
+ if options.empty? && args.is_a?(Array)
21
+ options = args.last if args.last.is_a?(Hash)
22
+ end
23
+
24
+ unless options.empty?
25
+ [:start_key, :finish_key, :key_count, :batch_size, :columns, :count, :start,
26
+ :stop, :finish, :finished, :reversed, :consistency, :ttl].each do |k|
27
+ report_kvs[k.to_s.capitalize] = options[k] if options.key?(k)
28
+ end
29
+
30
+ if op == :get_indexed_slices
31
+ index_clause = columns_and_options[:index_clause] || {}
32
+ unless index_clause.empty?
33
+ [:column_name, :value, :comparison].each do |k|
34
+ report_kvs[k.to_s.capitalize] = index_clause[k] if index_clause.key?(k)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ rescue => e
40
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" if AppOpticsAPM::Config[:verbose]
41
+ end
42
+
43
+ report_kvs
44
+ end
45
+
46
+ def insert_with_appoptics(column_family, key, hash, options = {})
47
+ return insert_without_appoptics(column_family, key, hash, options = {}) unless AppOpticsAPM.tracing?
48
+
49
+ report_kvs = extract_trace_details(:insert, column_family, key, hash, options)
50
+
51
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
52
+ insert_without_appoptics(column_family, key, hash, options = {})
53
+ end
54
+ end
55
+
56
+ def remove_with_appoptics(column_family, key, *columns_and_options)
57
+ args = [column_family, key] + columns_and_options
58
+
59
+ return send :remove_without_appoptics, *args unless AppOpticsAPM.tracing?
60
+
61
+ report_kvs = extract_trace_details(:remove, column_family, key, columns_and_options)
62
+
63
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
64
+ send :remove_without_appoptics, *args
65
+ end
66
+ end
67
+
68
+ def count_columns_with_appoptics(column_family, key, *columns_and_options)
69
+ args = [column_family, key] + columns_and_options
70
+
71
+ return send :count_columns_without_appoptics, *args unless AppOpticsAPM.tracing?
72
+
73
+ report_kvs = extract_trace_details(:count_columns, column_family, key, columns_and_options)
74
+
75
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
76
+ send :count_columns_without_appoptics, *args
77
+ end
78
+ end
79
+
80
+ def get_columns_with_appoptics(column_family, key, *columns_and_options)
81
+ args = [column_family, key] + columns_and_options
82
+
83
+ if AppOpticsAPM.tracing? && !AppOpticsAPM.tracing_layer_op?(:multi_get_columns)
84
+ report_kvs = extract_trace_details(:get_columns, column_family, key, columns_and_options)
85
+
86
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
87
+ send :get_columns_without_appoptics, *args
88
+ end
89
+ else
90
+ send :get_columns_without_appoptics, *args
91
+ end
92
+ end
93
+
94
+ def multi_get_columns_with_appoptics(column_family, key, *columns_and_options)
95
+ args = [column_family, key] + columns_and_options
96
+
97
+ return send :multi_get_columns_without_appoptics, *args unless AppOpticsAPM.tracing?
98
+
99
+ report_kvs = extract_trace_details(:multi_get_columns, column_family, key, columns_and_options)
100
+
101
+ AppOpticsAPM::API.trace(:cassandra, report_kvs, :multi_get_columns) do
102
+ send :multi_get_columns_without_appoptics, *args
103
+ end
104
+ end
105
+
106
+ def get_with_appoptics(column_family, key, *columns_and_options)
107
+ args = [column_family, key] + columns_and_options
108
+
109
+ return send :get_without_appoptics, *args unless AppOpticsAPM.tracing?
110
+
111
+ report_kvs = extract_trace_details(:get, column_family, key, columns_and_options)
112
+
113
+ AppOpticsAPM::API.trace(:cassandra, report_kvs, :get) do
114
+ send :get_without_appoptics, *args
115
+ end
116
+ end
117
+
118
+ def multi_get_with_appoptics(column_family, key, *columns_and_options)
119
+ args = [column_family, key] + columns_and_options
120
+
121
+ if AppOpticsAPM.tracing? && !AppOpticsAPM.tracing_layer_op?(:get)
122
+ report_kvs = extract_trace_details(:multi_get, column_family, key, columns_and_options)
123
+
124
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
125
+ send :multi_get_without_appoptics, *args
126
+ end
127
+ else
128
+ send :multi_get_without_appoptics, *args
129
+ end
130
+ end
131
+
132
+ def exists_with_appoptics?(column_family, key, *columns_and_options)
133
+ args = [column_family, key] + columns_and_options
134
+
135
+ return send :exists_without_appoptics?, *args unless AppOpticsAPM.tracing?
136
+
137
+ report_kvs = extract_trace_details(:exists?, column_family, key, columns_and_options)
138
+
139
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
140
+ send :exists_without_appoptics?, *args
141
+ end
142
+ end
143
+
144
+ def get_range_single_with_appoptics(column_family, options = {})
145
+ if AppOpticsAPM.tracing? && !AppOpticsAPM.tracing_layer_op?(:get_range_batch)
146
+ report_kvs = extract_trace_details(:get_range_single, column_family, nil, nil)
147
+
148
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
149
+ get_range_single_without_appoptics(column_family, options)
150
+ end
151
+ else
152
+ get_range_single_without_appoptics(column_family, options)
153
+ end
154
+ end
155
+
156
+ def get_range_batch_with_appoptics(column_family, options = {})
157
+ return get_range_batch_without_appoptics(column_family, options) unless AppOpticsAPM.tracing?
158
+
159
+ report_kvs = extract_trace_details(:get_range_batch, column_family, nil, nil)
160
+
161
+ AppOpticsAPM::API.trace(:cassandra, report_kvs, :get_range_batch) do
162
+ get_range_batch_without_appoptics(column_family, options)
163
+ end
164
+ end
165
+
166
+ def get_indexed_slices_with_appoptics(column_family, index_clause, *columns_and_options)
167
+ args = [column_family, index_clause] + columns_and_options
168
+
169
+ return send :get_indexed_slices_without_appoptics, *args unless AppOpticsAPM.tracing?
170
+
171
+ report_kvs = extract_trace_details(:get_indexed_slices, column_family, nil, columns_and_options)
172
+
173
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
174
+ send :get_indexed_slices_without_appoptics, *args
175
+ end
176
+ end
177
+
178
+ def create_index_with_appoptics(keyspace, column_family, column_name, validation_class)
179
+ unless AppOpticsAPM.tracing?
180
+ return create_index_without_appoptics(keyspace, column_family, column_name, validation_class)
181
+ end
182
+
183
+ report_kvs = extract_trace_details(:create_index, column_family, nil, nil)
184
+ begin
185
+ report_kvs[:Keyspace] = keyspace.to_s
186
+ report_kvs[:Column_name] = column_name.to_s
187
+ report_kvs[:Validation_class] = validation_class.to_s
188
+ rescue => e
189
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" if AppOpticsAPM::Config[:verbose]
190
+ end
191
+
192
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
193
+ create_index_without_appoptics(keyspace, column_family, column_name, validation_class)
194
+ end
195
+ end
196
+
197
+ def drop_index_with_appoptics(keyspace, column_family, column_name)
198
+ return drop_index_without_appoptics(keyspace, column_family, column_name) unless AppOpticsAPM.tracing?
199
+
200
+ report_kvs = extract_trace_details(:drop_index, column_family, nil, nil)
201
+ begin
202
+ report_kvs[:Keyspace] = keyspace.to_s
203
+ report_kvs[:Column_name] = column_name.to_s
204
+ rescue => e
205
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" if AppOpticsAPM::Config[:verbose]
206
+ end
207
+
208
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
209
+ drop_index_without_appoptics(keyspace, column_family, column_name)
210
+ end
211
+ end
212
+
213
+ def add_column_family_with_appoptics(cf_def)
214
+ return add_column_family_without_appoptics(cf_def) unless AppOpticsAPM.tracing?
215
+
216
+ report_kvs = extract_trace_details(:add_column_family, nil, nil, nil)
217
+ begin
218
+ report_kvs[:Cf] = cf_def[:name] if cf_def.is_a?(Hash) && cf_def.key?(:name)
219
+ rescue => e
220
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" if AppOpticsAPM::Config[:verbose]
221
+ end
222
+
223
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
224
+ add_column_family_without_appoptics(cf_def)
225
+ end
226
+ end
227
+
228
+ def drop_column_family_with_appoptics(column_family)
229
+ return drop_column_family_without_appoptics(column_family) unless AppOpticsAPM.tracing?
230
+
231
+ report_kvs = extract_trace_details(:drop_column_family, column_family, nil, nil)
232
+
233
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
234
+ drop_column_family_without_appoptics(column_family)
235
+ end
236
+ end
237
+
238
+ def add_keyspace_with_appoptics(ks_def)
239
+ return add_keyspace_without_appoptics(ks_def) unless AppOpticsAPM.tracing?
240
+
241
+ report_kvs = extract_trace_details(:add_keyspace, nil, nil, nil)
242
+ report_kvs[:Name] = ks_def.name rescue ''
243
+
244
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
245
+ add_keyspace_without_appoptics(ks_def)
246
+ end
247
+ end
248
+
249
+ def drop_keyspace_with_appoptics(keyspace)
250
+ return drop_keyspace_without_appoptics(keyspace) unless AppOpticsAPM.tracing?
251
+
252
+ report_kvs = extract_trace_details(:drop_keyspace, nil, nil, nil)
253
+ report_kvs[:Name] = keyspace.to_s rescue ''
254
+
255
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
256
+ drop_keyspace_without_appoptics(keyspace)
257
+ end
258
+ end
259
+ end
260
+ end
261
+ end
262
+
263
+ # There are two main Cassandra clients for Ruby. This one from Twitter
264
+ # and the other from datastax. This one defined ::Cassandra as a class
265
+ # and datastax defines it as a module. We use this to detect
266
+ # and differentiate between the client in use.
267
+
268
+ if defined?(::Cassandra) && ::Cassandra.is_a?(Class) && AppOpticsAPM::Config[:cassandra][:enabled]
269
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting cassandra' if AppOpticsAPM::Config[:verbose]
270
+
271
+ class ::Cassandra
272
+ include AppOpticsAPM::Inst::Cassandra
273
+
274
+ [:insert, :remove, :count_columns, :get_columns, :multi_get_columns, :get,
275
+ :multi_get, :get_range_single, :get_range_batch, :get_indexed_slices,
276
+ :create_index, :drop_index, :add_column_family, :drop_column_family,
277
+ :add_keyspace, :drop_keyspace].each do |m|
278
+ if method_defined?(m)
279
+ class_eval "alias #{m}_without_appoptics #{m}"
280
+ class_eval "alias #{m} #{m}_with_appoptics"
281
+ else AppOpticsAPM.logger.warn "[appoptics_apm/loading] Couldn't properly instrument Cassandra (#{m}). Partial traces may occur."
282
+ end
283
+ end
284
+
285
+ # Special case handler for question mark methods
286
+ if method_defined?(:exists?)
287
+ alias exists_without_appoptics? exists?
288
+ alias exists? exists_with_appoptics?
289
+ else AppOpticsAPM.logger.warn '[appoptics_apm/loading] Couldn\'t properly instrument Cassandra (exists?). Partial traces may occur.'
290
+ end
291
+ end # class Cassandra
292
+ end
293
+
294
+
@@ -0,0 +1,113 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ module Inst
6
+ module TyphoeusRequestOps
7
+
8
+ def self.included(klass)
9
+ ::AppOpticsAPM::Util.method_alias(klass, :run, ::Typhoeus::Request::Operations)
10
+ end
11
+
12
+ def run_with_appoptics
13
+ blacklisted = AppOpticsAPM::API.blacklisted?(url)
14
+ unless AppOpticsAPM.tracing?
15
+ context = AppOpticsAPM::Context.toString
16
+ options[:headers]['X-Trace'] = context if AppOpticsAPM::XTrace.valid?(context) && !blacklisted
17
+ return run_without_appoptics
18
+ end
19
+
20
+ begin
21
+ AppOpticsAPM::API.log_entry(:typhoeus)
22
+
23
+ # Prepare X-Trace header handling
24
+ context = AppOpticsAPM::Context.toString
25
+ options[:headers]['X-Trace'] = context unless blacklisted
26
+
27
+ response = run_without_appoptics
28
+
29
+ if response.code == 0
30
+ AppOpticsAPM::API.log(:typhoeus, :error, { :ErrorClass => response.return_code,
31
+ :ErrorMsg => response.return_message })
32
+ end
33
+
34
+ kvs = {}
35
+ kvs[:IsService] = 1
36
+ kvs[:HTTPStatus] = response.code
37
+ kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:typhoeus][:collect_backtraces]
38
+
39
+ uri = URI(response.effective_url)
40
+
41
+ # Conditionally log query params
42
+ if AppOpticsAPM::Config[:typhoeus][:log_args]
43
+ kvs[:RemoteURL] = uri.to_s
44
+ else
45
+ kvs[:RemoteURL] = uri.to_s.split('?').first
46
+ end
47
+
48
+ kvs[:HTTPMethod] = ::AppOpticsAPM::Util.upcase(options[:method])
49
+ kvs[:Blacklisted] = true if blacklisted
50
+
51
+ # Re-attach net::http edge unless it's blacklisted or if we don't have a
52
+ # valid X-Trace header
53
+ unless blacklisted
54
+ xtrace = response.headers['X-Trace']
55
+ AppOpticsAPM::XTrace.continue_service_context(context, xtrace)
56
+ end
57
+
58
+ AppOpticsAPM::API.log_info(:typhoeus, kvs)
59
+ response
60
+ rescue => e
61
+ AppOpticsAPM::API.log_exception(:typhoeus, e)
62
+ raise e
63
+ ensure
64
+ AppOpticsAPM::API.log_exit(:typhoeus)
65
+ end
66
+ end
67
+ end
68
+
69
+ module TyphoeusHydraRunnable
70
+ def self.included(klass)
71
+ ::AppOpticsAPM::Util.method_alias(klass, :run, ::Typhoeus::Hydra)
72
+ end
73
+
74
+ def run_with_appoptics
75
+ unless AppOpticsAPM.tracing?
76
+ context = AppOpticsAPM::Context.toString
77
+ queued_requests.map do |request|
78
+ blacklisted = AppOpticsAPM::API.blacklisted?(request.base_url)
79
+ request.options[:headers]['X-Trace'] = context if AppOpticsAPM::XTrace.valid?(context) && !blacklisted
80
+ end
81
+ return run_without_appoptics
82
+ end
83
+
84
+ kvs = {}
85
+
86
+ kvs[:queued_requests] = queued_requests.count
87
+ kvs[:max_concurrency] = max_concurrency
88
+ kvs[:Async] = 1
89
+
90
+ # FIXME: Until we figure out a strategy to deal with libcurl internal
91
+ # threading and Ethon's use of easy handles, here we just do a simple
92
+ # trace of the hydra run.
93
+ AppOpticsAPM::API.trace(:typhoeus_hydra, kvs) do
94
+ queued_requests.map do |request|
95
+ blacklisted = AppOpticsAPM::API.blacklisted?(request.base_url)
96
+ request.options[:headers]['X-Trace'] = AppOpticsAPM::Context.toString unless blacklisted
97
+ end
98
+
99
+ run_without_appoptics
100
+ end
101
+ end
102
+ end
103
+
104
+ end
105
+ end
106
+
107
+ if AppOpticsAPM::Config[:typhoeus][:enabled]
108
+ if defined?(::Typhoeus)
109
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting typhoeus' if AppOpticsAPM::Config[:verbose]
110
+ ::AppOpticsAPM::Util.send_include(::Typhoeus::Request::Operations, ::AppOpticsAPM::Inst::TyphoeusRequestOps)
111
+ ::AppOpticsAPM::Util.send_include(::Typhoeus::Hydra, ::AppOpticsAPM::Inst::TyphoeusHydraRunnable)
112
+ end
113
+ end
@@ -0,0 +1,22 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ ##
6
+ # The Inst module holds all of the instrumentation extensions for various
7
+ # libraries suchs as Redis, Dalli and Resque.
8
+ module Inst
9
+ def self.load_instrumentation
10
+ # Load the general instrumentation
11
+ pattern = File.join(File.dirname(__FILE__), 'inst', '*.rb')
12
+ Dir.glob(pattern) do |f|
13
+ begin
14
+ require f
15
+ rescue => e
16
+ AppOpticsAPM.logger.error "[appoptics_apm/loading] Error loading instrumentation file '#{f}' : #{e}"
17
+ AppOpticsAPM.logger.debug "[appoptics_apm/loading] #{e.backtrace.first}"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,97 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ ##
5
+ # Provides the methods necessary for method profiling. Profiling
6
+ # results are sent to the AppOptics dashboard.
7
+ #
8
+ # Example usage:
9
+ # class MyApp
10
+ # include AppOpticsAPMMethodProfiling
11
+ #
12
+ # def process_request()
13
+ # # The hard work
14
+ # end
15
+ #
16
+ # # call syntax: profile_method <method>, <profile_name>
17
+ # profile_method :process_request, 'request_processor'
18
+ # end
19
+ module AppOpticsAPMMethodProfiling
20
+ def self.included(klass)
21
+ klass.extend ClassMethods
22
+ end
23
+
24
+ module ClassMethods
25
+ def profile_method_noop(*args)
26
+ nil
27
+ end
28
+
29
+ def profile_method_real(method_name, profile_name, store_args = false, store_return = false, *_)
30
+ begin
31
+ # this only gets file and line where profiling is turned on, presumably
32
+ # right after the function definition. ruby 1.9 and 2.0 has nice introspection (Method.source_location)
33
+ # but its appears no such luck for ruby 1.8
34
+ file = ''
35
+ line = ''
36
+ if RUBY_VERSION >= '1.9'
37
+ info = instance_method(method_name).source_location
38
+ unless info.nil?
39
+ file = info[0].to_s
40
+ line = info[1].to_s
41
+ end
42
+ else
43
+ info = Kernel.caller[0].split(':')
44
+ file = info.first.to_s
45
+ line = info.last.to_s
46
+ end
47
+
48
+ # Safety: Make sure there are no quotes or double quotes to break the class_eval
49
+ file = file.gsub(/[\'\"]/, '')
50
+ line = line.gsub(/[\'\"]/, '')
51
+
52
+ # profiling via ruby-prof, is it possible to get return value of profiled code?
53
+ code = "def _appoptics_profiled_#{method_name}(*args, &block)
54
+ entry_kvs = {}
55
+ entry_kvs['Language'] = 'ruby'
56
+ entry_kvs['ProfileName'] = '#{AppOpticsAPM::Util.prettify(profile_name)}'
57
+ entry_kvs['FunctionName'] = '#{AppOpticsAPM::Util.prettify(method_name)}'
58
+ entry_kvs['File'] = '#{file}'
59
+ entry_kvs['LineNumber'] = '#{line}'
60
+ entry_kvs['Args'] = AppOpticsAPM::API.pps(*args) if #{store_args}
61
+ entry_kvs.merge!(::AppOpticsAPM::API.get_class_name(self))
62
+
63
+ AppOpticsAPM::API.log(nil, 'profile_entry', entry_kvs)
64
+
65
+ ret = _appoptics_orig_#{method_name}(*args, &block)
66
+
67
+ exit_kvs = {}
68
+ exit_kvs['Language'] = 'ruby'
69
+ exit_kvs['ProfileName'] = '#{AppOpticsAPM::Util.prettify(profile_name)}'
70
+ exit_kvs['ReturnValue'] = AppOpticsAPM::API.pps(ret) if #{store_return}
71
+
72
+ AppOpticsAPM::API.log(nil, 'profile_exit', exit_kvs)
73
+ ret
74
+ end"
75
+ rescue => e
76
+ AppOpticsAPM.logger.warn "[appoptics_apm/warn] profile_method: #{e.inspect}"
77
+ end
78
+
79
+ begin
80
+ class_eval code, __FILE__, __LINE__
81
+ alias_method "_appoptics_orig_#{method_name}", method_name
82
+ alias_method method_name, "_appoptics_profiled_#{method_name}"
83
+ rescue => e
84
+ AppOpticsAPM.logger.warn "[appoptics_apm/warn] Fatal error profiling method (#{method_name}): #{e.inspect}" if AppOpticsAPM::Config[:verbose]
85
+ end
86
+ end
87
+
88
+ # This allows this module to be included and called even if the gem is in
89
+ # no-op mode (no base libraries).
90
+ if AppOpticsAPM.loaded
91
+ alias :profile_method :profile_method_real
92
+ else
93
+ alias :profile_method :profile_method_noop
94
+ end
95
+
96
+ end
97
+ end