appoptics_apm 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
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,130 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ module Inst
6
+ module ExconConnection
7
+ def self.included(klass)
8
+ ::AppOpticsAPM::Util.method_alias(klass, :request, ::Excon::Connection)
9
+ ::AppOpticsAPM::Util.method_alias(klass, :requests, ::Excon::Connection)
10
+ end
11
+
12
+ private
13
+
14
+ def appoptics_collect(params)
15
+ kvs = {}
16
+ kvs[:IsService] = 1
17
+ kvs[:RemoteProtocol] = ::AppOpticsAPM::Util.upcase(@data[:scheme])
18
+ kvs[:RemoteHost] = @data[:host]
19
+
20
+ # Conditionally log query args
21
+ if AppOpticsAPM::Config[:excon][:log_args] && @data[:query]
22
+ if @data[:query].is_a?(Hash)
23
+ if RUBY_VERSION >= '1.9.2'
24
+ kvs[:ServiceArg] = "#{@data[:path]}?#{URI.encode_www_form(@data[:query])}"
25
+ else
26
+ # An imperfect solution for the lack of URI.encode_www_form for Ruby versions before
27
+ # 1.9.2. We manually create a query string for reporting purposes only.
28
+ query_arg = ""
29
+ @data[:query].each_pair { |k,v| query_arg += "#{k}=#{v}?"; }
30
+ kvs[:ServiceArg] = "#{@data[:path]}?#{query_arg.chop}"
31
+ end
32
+ else
33
+ kvs[:ServiceArg] = "#{@data[:path]}?#{@data[:query]}"
34
+ end
35
+ else
36
+ kvs[:ServiceArg] = @data[:path]
37
+ end
38
+
39
+ # In the case of HTTP pipelining, params could be an array of
40
+ # request hashes.
41
+ if params.is_a?(Array)
42
+ methods = []
43
+ params.each do |p|
44
+ methods << ::AppOpticsAPM::Util.upcase(p[:method])
45
+ end
46
+ kvs[:HTTPMethods] = methods.join(', ')[0..1024]
47
+ kvs[:Pipeline] = true
48
+ else
49
+ kvs[:HTTPMethod] = ::AppOpticsAPM::Util.upcase(params[:method])
50
+ end
51
+ kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:excon][:collect_backtraces]
52
+ kvs
53
+ rescue => e
54
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] Error capturing excon KVs: #{e.message}"
55
+ AppOpticsAPM.logger.debug e.backtrace.join('\n') if ::AppOpticsAPM::Config[:verbose]
56
+ ensure
57
+ return kvs
58
+ end
59
+
60
+ public
61
+
62
+ def requests_with_appoptics(pipeline_params)
63
+ responses = nil
64
+ AppOpticsAPM::API.trace(:excon, appoptics_collect(pipeline_params)) do
65
+ responses = requests_without_appoptics(pipeline_params)
66
+ end
67
+ responses
68
+ end
69
+
70
+ def request_with_appoptics(params={}, &block)
71
+ # Avoid cross host tracing for blacklisted domains
72
+ blacklisted = AppOpticsAPM::API.blacklisted?(@data[:hostname])
73
+
74
+ # If we're not tracing, just do a fast return.
75
+ # If making HTTP pipeline requests (ordered batched)
76
+ # then just return as we're tracing from parent
77
+ # <tt>requests</tt>
78
+ if !AppOpticsAPM.tracing? || params[:pipeline]
79
+ @data[:headers]['X-Trace'] = AppOpticsAPM::Context.toString if AppOpticsAPM::Context.isValid && !blacklisted
80
+ return request_without_appoptics(params, &block)
81
+ end
82
+
83
+ begin
84
+ response_context = nil
85
+
86
+ kvs = appoptics_collect(params)
87
+ kvs[:Blacklisted] = true if blacklisted
88
+
89
+ AppOpticsAPM::API.log_entry(:excon, kvs)
90
+ kvs.clear
91
+
92
+ req_context = AppOpticsAPM::Context.toString
93
+ @data[:headers]['X-Trace'] = req_context unless blacklisted
94
+
95
+ # The core excon call
96
+ response = request_without_appoptics(params, &block)
97
+
98
+ # excon only passes back a hash (datum) for HTTP pipelining...
99
+ # In that case, we should never arrive here but for the OCD, double check
100
+ # the datatype before trying to extract pertinent info
101
+ if response.is_a?(Excon::Response)
102
+ response_context = response.headers['X-Trace']
103
+ kvs[:HTTPStatus] = response.status
104
+
105
+ # If we get a redirect, report the location header
106
+ if ((300..308).to_a.include? response.status.to_i) && response.headers.key?('Location')
107
+ kvs[:Location] = response.headers['Location']
108
+ end
109
+
110
+ if response_context && !blacklisted
111
+ AppOpticsAPM::XTrace.continue_service_context(req_context, response_context)
112
+ end
113
+ end
114
+
115
+ response
116
+ rescue => e
117
+ AppOpticsAPM::API.log_exception(:excon, e)
118
+ raise e
119
+ ensure
120
+ AppOpticsAPM::API.log_exit(:excon, kvs) unless params[:pipeline]
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ if AppOpticsAPM::Config[:excon][:enabled] && defined?(::Excon)
128
+ ::AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting excon' if AppOpticsAPM::Config[:verbose]
129
+ ::AppOpticsAPM::Util.send_include(::Excon::Connection, ::AppOpticsAPM::Inst::ExconConnection)
130
+ end
@@ -0,0 +1,77 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ module Inst
6
+ module FaradayConnection
7
+ def self.included(klass)
8
+ ::AppOpticsAPM::Util.method_alias(klass, :run_request, ::Faraday::Connection)
9
+ end
10
+
11
+ def run_request_with_appoptics(method, url, body, headers, &block)
12
+ unless AppOpticsAPM.tracing?
13
+ xtrace = AppOpticsAPM::Context.toString
14
+ @headers['X-Trace'] = xtrace if AppOpticsAPM::XTrace.valid?(xtrace) && !AppOpticsAPM::API.blacklisted?(@url_prefix.to_s)
15
+ return run_request_without_appoptics(method, url, body, headers, &block)
16
+ end
17
+
18
+ begin
19
+ AppOpticsAPM::API.log_entry(:faraday)
20
+
21
+ xtrace = AppOpticsAPM::Context.toString
22
+ @headers['X-Trace'] = xtrace if AppOpticsAPM::XTrace.valid?(xtrace) && !AppOpticsAPM::API.blacklisted?(@url_prefix.to_s)
23
+ result = run_request_without_appoptics(method, url, body, headers, &block)
24
+
25
+ kvs = {}
26
+ kvs[:Middleware] = @builder.handlers
27
+ kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:faraday][:collect_backtraces]
28
+
29
+ # Only send service KVs if we're not using the Net::HTTP adapter
30
+ # Otherwise, the Net::HTTP instrumentation will send the service KVs
31
+ handle_service = !@builder.handlers.include?(Faraday::Adapter::NetHttp) &&
32
+ !@builder.handlers.include?(Faraday::Adapter::Excon)
33
+ if handle_service
34
+ blacklisted = AppOpticsAPM::API.blacklisted?(@url_prefix.to_s)
35
+ context = AppOpticsAPM::Context.toString
36
+ task_id = AppOpticsAPM::XTrace.task_id(context)
37
+
38
+ # Avoid cross host tracing for blacklisted domains
39
+ # Conditionally add the X-Trace header to the outgoing request
40
+ @headers['X-Trace'] = context unless blacklisted
41
+
42
+ kvs[:IsService] = 1
43
+ kvs[:RemoteProtocol] = (@url_prefix.scheme == 'https') ? 'HTTPS' : 'HTTP'
44
+ kvs[:RemoteHost] = @url_prefix.host
45
+ kvs[:RemotePort] = @url_prefix.port
46
+ kvs[:ServiceArg] = url
47
+ kvs[:HTTPMethod] = method
48
+ kvs[:HTTPStatus] = result.status
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 = result.headers['X-Trace']
55
+ AppOpticsAPM::XTrace.continue_service_context(context, xtrace)
56
+ end
57
+ end
58
+
59
+ AppOpticsAPM::API.log(:faraday, :info, kvs)
60
+ result
61
+ rescue => e
62
+ AppOpticsAPM::API.log_exception(:faraday, e)
63
+ raise e
64
+ ensure
65
+ AppOpticsAPM::API.log_exit(:faraday)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ if AppOpticsAPM::Config[:faraday][:enabled]
73
+ if defined?(::Faraday)
74
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting faraday' if AppOpticsAPM::Config[:verbose]
75
+ ::AppOpticsAPM::Util.send_include(::Faraday::Connection, ::AppOpticsAPM::Inst::FaradayConnection)
76
+ end
77
+ end
@@ -0,0 +1,83 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ require 'net/http'
5
+
6
+ if AppOpticsAPM::Config[:nethttp][:enabled]
7
+
8
+ Net::HTTP.class_eval do
9
+ def request_with_appoptics(*args, &block)
10
+ # Avoid cross host tracing for blacklisted domains
11
+ blacklisted = AppOpticsAPM::API.blacklisted?(addr_port)
12
+
13
+ # If we're not tracing, just do a fast return. Since
14
+ # net/http.request calls itself, only trace
15
+ # once the http session has been started.
16
+ if !AppOpticsAPM.tracing? || !started?
17
+ unless blacklisted
18
+ xtrace = AppOpticsAPM::Context.toString
19
+ args[0]['X-Trace'] = xtrace if AppOpticsAPM::XTrace.valid?(xtrace)
20
+ end
21
+ return request_without_appoptics(*args, &block)
22
+ end
23
+
24
+ AppOpticsAPM::API.trace(:'net-http') do
25
+ opts = {}
26
+ context = AppOpticsAPM::Context.toString
27
+ task_id = AppOpticsAPM::XTrace.task_id(context)
28
+
29
+ # Collect KVs to report in the info event
30
+ if args.length && args[0]
31
+ req = args[0]
32
+
33
+ opts[:IsService] = 1
34
+ opts[:RemoteProtocol] = use_ssl? ? :HTTPS : :HTTP
35
+ opts[:RemoteHost] = addr_port
36
+
37
+ # Conditionally log query params
38
+ if AppOpticsAPM::Config[:nethttp][:log_args]
39
+ opts[:ServiceArg] = req.path
40
+ else
41
+ opts[:ServiceArg] = req.path.split('?').first
42
+ end
43
+
44
+ opts[:HTTPMethod] = req.method
45
+ opts[:Blacklisted] = true if blacklisted
46
+ opts[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:nethttp][:collect_backtraces]
47
+
48
+ req['X-Trace'] = context unless blacklisted
49
+ end
50
+
51
+ begin
52
+ # The actual net::http call
53
+ resp = request_without_appoptics(*args, &block)
54
+
55
+ # Re-attach net::http edge unless blacklisted and is a valid X-Trace ID
56
+ unless blacklisted
57
+ xtrace = resp.get_fields('X-Trace')
58
+ xtrace = xtrace[0] if xtrace && xtrace.is_a?(Array)
59
+
60
+ AppOpticsAPM::XTrace.continue_service_context(context, xtrace)
61
+ end
62
+
63
+ opts[:HTTPStatus] = resp.code
64
+
65
+ # If we get a redirect, report the location header
66
+ if ((300..308).to_a.include? resp.code.to_i) && resp.header["Location"]
67
+ opts[:Location] = resp.header["Location"]
68
+ end
69
+
70
+ next resp
71
+ ensure
72
+ # Log the info event with the KVs in opts
73
+ AppOpticsAPM::API.log(:'net-http', :info, opts)
74
+ end
75
+ end
76
+ end
77
+
78
+ alias request_without_appoptics request
79
+ alias request request_with_appoptics
80
+
81
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting net/http' if AppOpticsAPM::Config[:verbose]
82
+ end
83
+ end
@@ -0,0 +1,176 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ module Inst
6
+ module HTTPClient
7
+ def self.included(klass)
8
+ ::AppOpticsAPM::Util.method_alias(klass, :do_request, ::HTTPClient)
9
+ ::AppOpticsAPM::Util.method_alias(klass, :do_request_async, ::HTTPClient)
10
+ ::AppOpticsAPM::Util.method_alias(klass, :do_get_stream, ::HTTPClient)
11
+ end
12
+
13
+ def appoptics_collect(method, uri, query = nil)
14
+ kvs = {}
15
+ kvs[:IsService] = 1
16
+
17
+ # Conditionally log URL query params
18
+ # Because of the hook points, the query arg can come in under <tt>query</tt>
19
+ # or as a part of <tt>uri</tt> (not both). Here we handle both cases.
20
+ if AppOpticsAPM::Config[:httpclient][:log_args]
21
+ if query
22
+ kvs[:RemoteURL] = uri.to_s + '?' + AppOpticsAPM::Util.to_query(query)
23
+ else
24
+ kvs[:RemoteURL] = uri.to_s
25
+ end
26
+ else
27
+ kvs[:RemoteURL] = uri.to_s.split('?').first
28
+ end
29
+
30
+ kvs[:RemoteProtocol] = uri.scheme.upcase
31
+ kvs[:RemoteHost] = "#{uri.host}:#{uri.port}"
32
+ kvs[:ServiceArg] = "/?#{uri.query}"
33
+ kvs[:HTTPMethod] = ::AppOpticsAPM::Util.upcase(method)
34
+ kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:httpclient][:collect_backtraces]
35
+ kvs
36
+ rescue => e
37
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] Error capturing httpclient KVs: #{e.message}"
38
+ AppOpticsAPM.logger.debug e.backtrace.join('\n') if ::AppOpticsAPM::Config[:verbose]
39
+ ensure
40
+ return kvs
41
+ end
42
+
43
+ def do_request_with_appoptics(method, uri, query, body, header, &block)
44
+ # Avoid cross host tracing for blacklisted domains
45
+ blacklisted = AppOpticsAPM::API.blacklisted?(uri.hostname)
46
+
47
+ # If we're not tracing, just do a fast return.
48
+ unless AppOpticsAPM.tracing?
49
+ add_xtrace_header(header) unless blacklisted
50
+ return do_request_without_appoptics(method, uri, query, body, header, &block)
51
+ end
52
+
53
+ begin
54
+ req_context = nil
55
+ response_context = nil
56
+
57
+ kvs = appoptics_collect(method, uri, query)
58
+ kvs[:Blacklisted] = true if blacklisted
59
+
60
+ AppOpticsAPM::API.log_entry(:httpclient, kvs)
61
+ kvs.clear
62
+
63
+ req_context = add_xtrace_header(header) unless blacklisted
64
+
65
+ # The core httpclient call
66
+ response = do_request_without_appoptics(method, uri, query, body, header, &block)
67
+
68
+ response_context = response.headers['X-Trace']
69
+ kvs[:HTTPStatus] = response.status_code
70
+
71
+ # If we get a redirect, report the location header
72
+ if ((300..308).to_a.include? response.status.to_i) && response.headers.key?('Location')
73
+ kvs[:Location] = response.headers['Location']
74
+ end
75
+
76
+ if response_context && !blacklisted
77
+ AppOpticsAPM::XTrace.continue_service_context(req_context, response_context)
78
+ end
79
+
80
+ response
81
+ rescue => e
82
+ AppOpticsAPM::API.log_exception(:httpclient, e)
83
+ raise e
84
+ ensure
85
+ AppOpticsAPM::API.log_exit(:httpclient, kvs)
86
+ end
87
+ end
88
+
89
+ def do_request_async_with_appoptics(method, uri, query, body, header)
90
+ add_xtrace_header(header)
91
+ do_request_async_without_appoptics(method, uri, query, body, header)
92
+ end
93
+
94
+ def do_get_stream_with_appoptics(req, proxy, conn)
95
+ AppOpticsAPM::Context.fromString(req.header['X-Trace'].first) unless req.header['X-Trace'].empty?
96
+ # Avoid cross host tracing for blacklisted domains
97
+ uri = req.http_header.request_uri
98
+ blacklisted = AppOpticsAPM::API.blacklisted?(uri.hostname)
99
+
100
+ unless AppOpticsAPM.tracing?
101
+ req.header.delete('X-Trace') if blacklisted
102
+ return do_get_stream_without_appoptics(req, proxy, conn)
103
+ end
104
+
105
+ begin
106
+ response = nil
107
+ req_context = nil
108
+ method = req.http_header.request_method
109
+
110
+ kvs = appoptics_collect(method, uri)
111
+ kvs[:Blacklisted] = true if blacklisted
112
+ kvs[:Async] = 1
113
+
114
+ AppOpticsAPM::API.log_entry(:httpclient, kvs)
115
+ kvs.clear
116
+
117
+ blacklisted ? req.header.delete('X-Trace') : req_context = add_xtrace_header(req.header)
118
+
119
+ # The core httpclient call
120
+ result = do_get_stream_without_appoptics(req, proxy, conn)
121
+
122
+ # Older HTTPClient < 2.6.0 returns HTTPClient::Connection
123
+ if result.is_a?(::HTTP::Message)
124
+ response = result
125
+ else
126
+ response = conn.pop
127
+ end
128
+
129
+ response_context = response.headers['X-Trace']
130
+ kvs[:HTTPStatus] = response.status_code
131
+
132
+ # If we get a redirect, report the location header
133
+ if ((300..308).to_a.include? response.status.to_i) && response.headers.key?('Location')
134
+ kvs[:Location] = response.headers['Location']
135
+ end
136
+
137
+ if response_context && !blacklisted
138
+ AppOpticsAPM::XTrace.continue_service_context(req_context, response_context)
139
+ end
140
+
141
+ # Older HTTPClient < 2.6.0 returns HTTPClient::Connection
142
+ conn.push response if result.is_a?(::HTTPClient::Connection)
143
+ result
144
+ rescue => e
145
+ AppOpticsAPM::API.log_exception(:httpclient, e)
146
+ raise e
147
+ ensure
148
+ # AppOpticsAPM::API.log_exit('httpclient', kvs.merge('Async' => 1))
149
+ AppOpticsAPM::API.log_exit(:httpclient, kvs)
150
+ end
151
+ end
152
+
153
+ private
154
+
155
+ def add_xtrace_header(headers)
156
+ req_context = AppOpticsAPM::Context.toString
157
+ return nil unless AppOpticsAPM::XTrace.valid?(req_context)
158
+ # Be aware of various ways to call/use httpclient
159
+ if headers.is_a?(Array)
160
+ headers.delete_if { |kv| kv[0] == 'X-Trace' }
161
+ headers.push ['X-Trace', req_context]
162
+ elsif headers.is_a?(Hash)
163
+ headers['X-Trace'] = req_context
164
+ elsif headers.is_a? HTTP::Message::Headers
165
+ headers.set('X-Trace', req_context)
166
+ end
167
+ req_context
168
+ end
169
+ end
170
+ end
171
+ end
172
+
173
+ if AppOpticsAPM::Config[:httpclient][:enabled] && defined?(::HTTPClient)
174
+ ::AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting httpclient' if AppOpticsAPM::Config[:verbose]
175
+ ::AppOpticsAPM::Util.send_include(::HTTPClient, ::AppOpticsAPM::Inst::HTTPClient)
176
+ end
@@ -0,0 +1,102 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ module Inst
6
+ module MemCache
7
+ include AppOpticsAPM::API::Memcache
8
+
9
+ def self.included(cls)
10
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting memcache' if AppOpticsAPM::Config[:verbose]
11
+
12
+ cls.class_eval do
13
+ MEMCACHE_OPS.reject { |m| !method_defined?(m) }.each do |m|
14
+
15
+ define_method("#{m}_with_appoptics") do |*args|
16
+ report_kvs = { :KVOp => m }
17
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:memcache][:collect_backtraces]
18
+
19
+ if AppOpticsAPM.tracing?
20
+ AppOpticsAPM::API.trace(:memcache, report_kvs) do
21
+ send("#{m}_without_appoptics", *args)
22
+ end
23
+ else
24
+ send("#{m}_without_appoptics", *args)
25
+ end
26
+ end
27
+
28
+ class_eval "alias #{m}_without_appoptics #{m}"
29
+ class_eval "alias #{m} #{m}_with_appoptics"
30
+ end
31
+ end
32
+
33
+ [:request_setup, :cache_get, :get_multi].each do |m|
34
+ if ::MemCache.method_defined? :request_setup
35
+ cls.class_eval "alias #{m}_without_appoptics #{m}"
36
+ cls.class_eval "alias #{m} #{m}_with_appoptics"
37
+ elsif AppOpticsAPM::Config[:verbose]
38
+ AppOpticsAPM.logger.warn "[appoptics_apm/loading] Couldn't properly instrument Memcache: #{m}"
39
+ end
40
+ end
41
+ end
42
+
43
+ def get_multi_with_appoptics(*args)
44
+ return get_multi_without_appoptics(args) unless AppOpticsAPM.tracing?
45
+
46
+ info_kvs = {}
47
+
48
+ begin
49
+ info_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:memcache][:collect_backtraces]
50
+
51
+ if args.last.is_a?(Hash) || args.last.nil?
52
+ info_kvs[:KVKeyCount] = args.flatten.length - 1
53
+ else
54
+ info_kvs[:KVKeyCount] = args.flatten.length
55
+ end
56
+ rescue StandardError => e
57
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] Error collecting info keys: #{e.message}"
58
+ AppOpticsAPM.logger.debug e.backtrace
59
+ end
60
+
61
+ AppOpticsAPM::API.trace(:memcache, { :KVOp => :get_multi }, :get_multi) do
62
+ values = get_multi_without_appoptics(args)
63
+
64
+ info_kvs[:KVHitCount] = values.length
65
+ AppOpticsAPM::API.log(:memcache, :info, info_kvs)
66
+
67
+ values
68
+ end
69
+ end
70
+
71
+ def request_setup_with_appoptics(*args)
72
+ if AppOpticsAPM.tracing? && !AppOpticsAPM.tracing_layer_op?(:get_multi)
73
+ server, cache_key = request_setup_without_appoptics(*args)
74
+
75
+ info_kvs = { :KVKey => cache_key, :RemoteHost => server.host }
76
+ info_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:memcache][:collect_backtraces]
77
+ AppOpticsAPM::API.log(:memcache, :info, info_kvs)
78
+
79
+ [server, cache_key]
80
+ else
81
+ request_setup_without_appoptics(*args)
82
+ end
83
+ end
84
+
85
+ def cache_get_with_appoptics(server, cache_key)
86
+ result = cache_get_without_appoptics(server, cache_key)
87
+
88
+ info_kvs = { :KVHit => memcache_hit?(result) }
89
+ info_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:memcache][:collect_backtraces]
90
+ AppOpticsAPM::API.log(:memcache, :info, info_kvs)
91
+
92
+ result
93
+ end
94
+ end # module MemCache
95
+ end # module Inst
96
+ end # module AppOpticsAPM
97
+
98
+ if defined?(::MemCache) && AppOpticsAPM::Config[:memcache][:enabled]
99
+ ::MemCache.class_eval do
100
+ include AppOpticsAPM::Inst::MemCache
101
+ end
102
+ end
@@ -0,0 +1,94 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ module Inst
6
+ module Memcached
7
+ include AppOpticsAPM::API::Memcache
8
+
9
+ def self.included(cls)
10
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting memcached' if AppOpticsAPM::Config[:verbose]
11
+
12
+ cls.class_eval do
13
+ MEMCACHE_OPS.reject { |m| !method_defined?(m) }.each do |m|
14
+ define_method("#{m}_with_appoptics") do |*args|
15
+ opts = { :KVOp => m }
16
+
17
+ if args.length && !args[0].is_a?(Array)
18
+ opts[:KVKey] = args[0].to_s
19
+ rhost = remote_host(args[0].to_s)
20
+ opts[:RemoteHost] = rhost if rhost
21
+ end
22
+
23
+ AppOpticsAPM::API.trace(:memcache, opts) do
24
+ result = send("#{m}_without_appoptics", *args)
25
+
26
+ info_kvs = {}
27
+ info_kvs[:KVHit] = memcache_hit?(result) if m == :get && args.length && args[0].class == String
28
+ info_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:memcached][:collect_backtraces]
29
+
30
+ AppOpticsAPM::API.log(:memcache, :info, info_kvs) unless info_kvs.empty?
31
+ result
32
+ end
33
+ end
34
+
35
+ class_eval "alias #{m}_without_appoptics #{m}"
36
+ class_eval "alias #{m} #{m}_with_appoptics"
37
+ end
38
+ end
39
+ end
40
+
41
+ end # module Memcached
42
+
43
+ module MemcachedRails
44
+ def self.included(cls)
45
+ cls.class_eval do
46
+ if ::Memcached::Rails.method_defined? :get_multi
47
+ alias get_multi_without_appoptics get_multi
48
+ alias get_multi get_multi_with_appoptics
49
+ elsif AppOpticsAPM::Config[:verbose]
50
+ AppOpticsAPM.logger.warn '[appoptics_apm/loading] Couldn\'t properly instrument Memcached. Partial traces may occur.'
51
+ end
52
+ end
53
+ end
54
+
55
+ def get_multi_with_appoptics(keys, raw = false)
56
+ if AppOpticsAPM.tracing?
57
+ layer_kvs = {}
58
+ layer_kvs[:KVOp] = :get_multi
59
+
60
+ AppOpticsAPM::API.trace(:memcache, layer_kvs || {}, :get_multi) do
61
+ begin
62
+ info_kvs = {}
63
+ info_kvs[:KVKeyCount] = keys.flatten.length
64
+
65
+ values = get_multi_without_appoptics(keys, raw)
66
+
67
+ info_kvs[:KVHitCount] = values.length
68
+ info_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:memcached][:collect_backtraces]
69
+
70
+ AppOpticsAPM::API.log(:memcache, :info, info_kvs)
71
+ rescue
72
+ values = get_multi_without_appoptics(keys, raw)
73
+ end
74
+ values
75
+ end
76
+ else
77
+ get_multi_without_appoptics(keys, raw)
78
+ end
79
+ end
80
+ end # module MemcachedRails
81
+ end # module Inst
82
+ end # module AppOpticsAPM
83
+
84
+ if defined?(::Memcached) && AppOpticsAPM::Config[:memcached][:enabled]
85
+ ::Memcached.class_eval do
86
+ include AppOpticsAPM::Inst::Memcached
87
+ end
88
+
89
+ if defined?(::Memcached::Rails)
90
+ ::Memcached::Rails.class_eval do
91
+ include AppOpticsAPM::Inst::MemcachedRails
92
+ end
93
+ end
94
+ end