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,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