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,329 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ module Inst
6
+
7
+ # Curb instrumentation wraps instance and class methods in two classes:
8
+ # Curl::Easy and Curl::Multi. This CurlUtility module is used as a common module
9
+ # to be shared among both modules.
10
+ module CurlUtility
11
+
12
+ private
13
+ ##
14
+ # appoptics_collect
15
+ #
16
+ # Used as a central area to retrieve and return values
17
+ # that we're interesting in reporting to AppOpticsAPM
18
+ #
19
+ def appoptics_collect(verb = nil)
20
+ kvs = {}
21
+
22
+ kvs[:IsService] = 1
23
+
24
+ # Conditionally log query args
25
+ if AppOpticsAPM::Config[:curb][:log_args]
26
+ kvs[:RemoteURL] = url
27
+ else
28
+ kvs[:RemoteURL] = url.split('?').first
29
+ end
30
+
31
+ kvs[:HTTPMethod] = verb if verb
32
+
33
+ # Avoid cross host tracing for blacklisted domains
34
+ kvs[:blacklisted] = AppOpticsAPM::API.blacklisted?(URI(url).hostname)
35
+ kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:curb][:collect_backtraces]
36
+
37
+ kvs
38
+ rescue => e
39
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] Error capturing curb KVs: #{e.message}"
40
+ if AppOpticsAPM::Config[:verbose]
41
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
42
+ AppOpticsAPM.logger.debug e.backtrace.join('\n')
43
+ end
44
+ ensure
45
+ return kvs
46
+ end
47
+
48
+ ##
49
+ # profile_curb_method
50
+ #
51
+ # An agnostic method that will profile any Curl::Easy method (and optional args and block)
52
+ # that you throw at it.
53
+ #
54
+ def profile_curb_method(kvs, method, args, &block)
55
+ # If we're not tracing, just do a fast return.
56
+ unless AppOpticsAPM.tracing?
57
+ unless AppOpticsAPM::API.blacklisted?(URI(url).hostname)
58
+ self.headers['X-Trace'] = AppOpticsAPM::Context.toString if AppOpticsAPM::Context.isValid
59
+ end
60
+ return self.send(method, args, &block)
61
+ end
62
+
63
+ begin
64
+ response_context = nil
65
+ kvs.merge! appoptics_collect
66
+
67
+ AppOpticsAPM::API.log_entry(:curb, kvs)
68
+ kvs.clear
69
+
70
+ # The core curb call
71
+ unless AppOpticsAPM::API.blacklisted?(URI(url).hostname)
72
+ self.headers['X-Trace'] = AppOpticsAPM::Context.toString if AppOpticsAPM::Context.isValid
73
+ end
74
+ response = self.send(method, *args, &block)
75
+
76
+ kvs[:HTTPStatus] = response_code
77
+
78
+ # If we get a redirect, report the location header
79
+ if ((300..308).to_a.include? response_code) && headers.key?("Location")
80
+ kvs[:Location] = headers["Location"]
81
+ end
82
+
83
+ _, *response_headers = header_str.split(/[\r\n]+/).map(&:strip)
84
+ response_headers = Hash[response_headers.flat_map{ |s| s.scan(/^(\S+): (.+)/) }]
85
+
86
+ response_context = response_headers['X-Trace']
87
+ if response_context && !kvs[:blacklisted]
88
+ AppOpticsAPM::XTrace.continue_service_context(self.headers['X-Trace'], response_context)
89
+ end
90
+
91
+ response
92
+ rescue => e
93
+ AppOpticsAPM::API.log_exception(:curb, e)
94
+ raise e
95
+ ensure
96
+ AppOpticsAPM::API.log_exit(:curb, kvs)
97
+ end
98
+ end
99
+
100
+ end # CurlUtility
101
+
102
+ # Instrumentation specific to ::Curl::Easy
103
+ module CurlEasy
104
+ # Common methods
105
+ include AppOpticsAPM::Inst::CurlUtility
106
+
107
+ def self.included(klass)
108
+ ::AppOpticsAPM::Util.method_alias(klass, :http, ::Curl::Easy)
109
+ ::AppOpticsAPM::Util.method_alias(klass, :perform, ::Curl::Easy)
110
+ ::AppOpticsAPM::Util.method_alias(klass, :http_put, ::Curl::Easy)
111
+ ::AppOpticsAPM::Util.method_alias(klass, :http_post, ::Curl::Easy)
112
+ end
113
+
114
+ ##
115
+ # http_post_with_appoptics
116
+ #
117
+ # ::Curl::Easy.new.http_post wrapper
118
+ #
119
+ def http_post_with_appoptics(*args, &block)
120
+ # If we're not tracing, just do a fast return.
121
+ if !AppOpticsAPM.tracing? || AppOpticsAPM.tracing_layer?(:curb)
122
+ unless AppOpticsAPM::API.blacklisted?(URI(url).hostname)
123
+ self.headers['X-Trace'] = AppOpticsAPM::Context.toString() if AppOpticsAPM::Context.isValid
124
+ end
125
+ return http_post_without_appoptics(*args)
126
+ end
127
+
128
+ kvs = {}
129
+ kvs[:HTTPMethod] = :POST
130
+
131
+ profile_curb_method(kvs, :http_post_without_appoptics, args, &block)
132
+ end
133
+
134
+ ##
135
+ # http_put_with_appoptics
136
+ #
137
+ # ::Curl::Easy.new.http_put wrapper
138
+ #
139
+ def http_put_with_appoptics(*args, &block)
140
+ # If we're not tracing, just do a fast return.
141
+ if !AppOpticsAPM.tracing? || AppOpticsAPM.tracing_layer?(:curb)
142
+ unless AppOpticsAPM::API.blacklisted?(URI(url).hostname)
143
+ self.headers['X-Trace'] = AppOpticsAPM::Context.toString() if AppOpticsAPM::Context.isValid
144
+ end
145
+ return http_put_without_appoptics(data)
146
+ end
147
+
148
+ kvs = {}
149
+ kvs[:HTTPMethod] = :PUT
150
+
151
+ profile_curb_method(kvs, :http_put_without_appoptics, args, &block)
152
+ end
153
+
154
+ ##
155
+ # perform_with_appoptics
156
+ #
157
+ # ::Curl::Easy.new.perform wrapper
158
+ #
159
+ def perform_with_appoptics(&block)
160
+ # If we're not tracing, just do a fast return.
161
+ # excluding curb layer: because the curb C code for easy.http calls perform,
162
+ # we have to make sure we don't log again
163
+ if !AppOpticsAPM.tracing? || AppOpticsAPM.tracing_layer?(:curb)
164
+ unless AppOpticsAPM::API.blacklisted?(URI(url).hostname)
165
+ self.headers['X-Trace'] = AppOpticsAPM::Context.toString() if AppOpticsAPM::Context.isValid
166
+ end
167
+ return perform_without_appoptics(&block)
168
+ end
169
+
170
+ kvs = {}
171
+ # This perform gets called from two places, ::Curl::Easy.new.perform
172
+ # and Curl::Easy.new.http_head. In the case of http_head we detect the
173
+ # HTTP verb via get info.
174
+ if self.getinfo(self.sym2curl(:nobody))
175
+ kvs[:HTTPMethod] = :HEAD
176
+ else
177
+ kvs[:HTTPMethod] = :GET
178
+ end
179
+
180
+ profile_curb_method(kvs, :perform_without_appoptics, nil, &block)
181
+ end
182
+
183
+ ##
184
+ # http_with_appoptics
185
+ #
186
+ # ::Curl::Easy.new.http wrapper
187
+ #
188
+ def http_with_appoptics(verb, &block)
189
+ # If we're not tracing, just do a fast return.
190
+ unless AppOpticsAPM.tracing?
191
+ unless AppOpticsAPM::API.blacklisted?(URI(url).hostname)
192
+ self.headers['X-Trace'] = AppOpticsAPM::Context.toString() if AppOpticsAPM::Context.isValid
193
+ end
194
+ return http_without_appoptics(verb)
195
+ end
196
+
197
+ kvs = {}
198
+ kvs[:HTTPMethod] = verb
199
+
200
+ profile_curb_method(kvs, :http_without_appoptics, [verb], &block)
201
+ end
202
+ end
203
+
204
+ ##
205
+ # CurlMultiCM
206
+ #
207
+ # This module contains the class method wrappers for the CurlMulti class.
208
+ # This module should be _extended_ by CurlMulti.
209
+ #
210
+ module CurlMultiCM
211
+ include AppOpticsAPM::Inst::CurlUtility
212
+
213
+ def self.extended(klass)
214
+ ::AppOpticsAPM::Util.class_method_alias(klass, :http, ::Curl::Multi)
215
+ end
216
+
217
+ ##
218
+ # http_with_appoptics
219
+ #
220
+ # ::Curl::Multi.new.http wrapper
221
+ #
222
+ def http_with_appoptics(urls_with_config, multi_options={}, &block)
223
+ # If we're not tracing, just do a fast return.
224
+ unless AppOpticsAPM.tracing?
225
+ urls_with_config.each do |conf|
226
+ unless AppOpticsAPM::API.blacklisted?(URI(conf[:url]).hostname)
227
+ conf[:headers] ||= {}
228
+ conf[:headers]['X-Trace'] = AppOpticsAPM::Context.toString if AppOpticsAPM::Context.isValid
229
+ end
230
+ end
231
+ return http_without_appoptics(urls_with_config, multi_options, &block)
232
+ end
233
+
234
+ begin
235
+ kvs = {}
236
+ kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:curb][:collect_backtraces]
237
+
238
+ AppOpticsAPM::API.log_entry(:curb_multi, kvs)
239
+ context = AppOpticsAPM::Context.toString
240
+ urls_with_config.each do |conf|
241
+ unless AppOpticsAPM::API.blacklisted?(URI(conf[:url]).hostname)
242
+ conf[:headers] ||= {}
243
+ conf[:headers]['X-Trace'] = context if AppOpticsAPM::Context.isValid
244
+ end
245
+ end
246
+
247
+ traces = []
248
+ # The core curb call
249
+ http_without_appoptics(urls_with_config, multi_options) do |easy, response_code, method|
250
+ # this is the only way we can access the headers, they are not exposed otherwise
251
+ unless AppOpticsAPM::API.blacklisted?(URI(easy.url).hostname)
252
+ xtrace = easy.header_str.scan(/X-Trace: ([0-9A-F]*)/).map{ |m| m[0] }
253
+ traces << xtrace[0] unless xtrace.empty?
254
+ end
255
+ block.call(easy, response_code, method) if block
256
+ end
257
+ AppOpticsAPM::XTrace.continue_service_context(context, traces.pop) unless traces.empty?
258
+ rescue => e
259
+ AppOpticsAPM::API.log_exception(:curb_multi, e)
260
+ raise e
261
+ ensure
262
+ AppOpticsAPM::API.log_multi_exit(:curb_multi, traces)
263
+ end
264
+ end
265
+ end
266
+
267
+ ##
268
+ # CurlMultiIM
269
+ #
270
+ # This module contains the instance method wrappers for the CurlMulti class.
271
+ # This module should be _included_ into CurlMulti.
272
+ #
273
+ module CurlMultiIM
274
+ include AppOpticsAPM::Inst::CurlUtility
275
+
276
+ def self.included(klass)
277
+ ::AppOpticsAPM::Util.method_alias(klass, :perform, ::Curl::Multi)
278
+ end
279
+
280
+ ##
281
+ # perform_with_appoptics
282
+ #
283
+ # ::Curl::Multi.new.perform wrapper
284
+ #
285
+ # the reason we instrument this method is because it can be called directly,
286
+ # therefore we exclude calls that already have a curb layer assigned
287
+ # Be aware: this method is also called from the c-implementation
288
+ #
289
+ def perform_with_appoptics(&block)
290
+ # If we're not tracing or we're already tracing curb, just do a fast return.
291
+ if !AppOpticsAPM.tracing? || [:curb, :curb_multi].include?(AppOpticsAPM.layer)
292
+ self.requests.each do |request|
293
+ unless AppOpticsAPM::API.blacklisted?(URI(request.url).hostname)
294
+ request.headers['X-Trace'] = AppOpticsAPM::Context.toString if AppOpticsAPM::Context.isValid
295
+ end
296
+ end
297
+ return perform_without_appoptics(&block)
298
+ end
299
+
300
+ begin
301
+ kvs = {}
302
+ kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:curb][:collect_backtraces]
303
+
304
+ AppOpticsAPM::API.log_entry(:curb_multi, kvs)
305
+
306
+ self.requests.each do |request|
307
+ unless AppOpticsAPM::API.blacklisted?(URI(request.url).hostname)
308
+ request.headers['X-Trace'] = AppOpticsAPM::Context.toString if AppOpticsAPM::Context.isValid
309
+ end
310
+ end
311
+
312
+ perform_without_appoptics(&block)
313
+ rescue => e
314
+ AppOpticsAPM::API.log_exception(:curb_multi, e)
315
+ raise e
316
+ ensure
317
+ AppOpticsAPM::API.log_exit(:curb_multi)
318
+ end
319
+ end
320
+ end
321
+ end
322
+ end
323
+
324
+ if AppOpticsAPM::Config[:curb][:enabled] && defined?(::Curl) && RUBY_VERSION > '1.8.7'
325
+ ::AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting curb' if AppOpticsAPM::Config[:verbose]
326
+ ::AppOpticsAPM::Util.send_include(::Curl::Easy, ::AppOpticsAPM::Inst::CurlEasy)
327
+ ::AppOpticsAPM::Util.send_extend(::Curl::Multi, ::AppOpticsAPM::Inst::CurlMultiCM)
328
+ ::AppOpticsAPM::Util.send_include(::Curl::Multi, ::AppOpticsAPM::Inst::CurlMultiIM)
329
+ end
@@ -0,0 +1,85 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ module Inst
6
+ module Dalli
7
+ include AppOpticsAPM::API::Memcache
8
+
9
+ def self.included(cls)
10
+ cls.class_eval do
11
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting memcache (dalli)' if AppOpticsAPM::Config[:verbose]
12
+ if ::Dalli::Client.private_method_defined? :perform
13
+ alias perform_without_appoptics perform
14
+ alias perform perform_with_appoptics
15
+ else AppOpticsAPM.logger.warn '[appoptics_apm/loading] Couldn\'t properly instrument Memcache (Dalli). Partial traces may occur.'
16
+ end
17
+
18
+ if ::Dalli::Client.method_defined? :get_multi
19
+ alias get_multi_without_appoptics get_multi
20
+ alias get_multi get_multi_with_appoptics
21
+ end
22
+ end
23
+ end
24
+
25
+ def perform_with_appoptics(*all_args, &blk)
26
+ op, key, *args = *all_args
27
+
28
+ report_kvs = {}
29
+ report_kvs[:KVOp] = op
30
+ report_kvs[:KVKey] = key
31
+ if @servers.is_a?(Array) && !@servers.empty?
32
+ report_kvs[:RemoteHost] = @servers.join(", ")
33
+ end
34
+
35
+ if AppOpticsAPM.tracing? && !AppOpticsAPM.tracing_layer_op?(:get_multi)
36
+ AppOpticsAPM::API.trace(:memcache, report_kvs) do
37
+ result = perform_without_appoptics(*all_args, &blk)
38
+
39
+ # Clear the hash for a potential info event
40
+ report_kvs.clear
41
+ report_kvs[:KVHit] = memcache_hit?(result) if op == :get && key.class == String
42
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:dalli][:collect_backtraces]
43
+
44
+ AppOpticsAPM::API.log(:memcache, :info, report_kvs) unless report_kvs.empty?
45
+ result
46
+ end
47
+ else
48
+ perform_without_appoptics(*all_args, &blk)
49
+ end
50
+ end
51
+
52
+ def get_multi_with_appoptics(*keys)
53
+ return get_multi_without_appoptics(*keys) unless AppOpticsAPM.tracing?
54
+
55
+ info_kvs = {}
56
+
57
+ begin
58
+ info_kvs[:KVKeyCount] = keys.flatten.length
59
+ info_kvs[:KVKeyCount] = (info_kvs[:KVKeyCount] - 1) if keys.last.is_a?(Hash) || keys.last.nil?
60
+ if @servers.is_a?(Array) && !@servers.empty?
61
+ info_kvs[:RemoteHost] = @servers.join(", ")
62
+ end
63
+ rescue => e
64
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" if AppOpticsAPM::Config[:verbose]
65
+ end
66
+
67
+ AppOpticsAPM::API.trace(:memcache, { :KVOp => :get_multi }, :get_multi) do
68
+ values = get_multi_without_appoptics(*keys)
69
+
70
+ info_kvs[:KVHitCount] = values.length
71
+ info_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:dalli][:collect_backtraces]
72
+ AppOpticsAPM::API.log(:memcache, :info, info_kvs)
73
+
74
+ values
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ if defined?(Dalli) && AppOpticsAPM::Config[:dalli][:enabled]
82
+ ::Dalli::Client.module_eval do
83
+ include AppOpticsAPM::Inst::Dalli
84
+ end
85
+ end
@@ -0,0 +1,92 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ if defined?(::Delayed)
5
+ module AppOpticsAPM
6
+ module Inst
7
+ module DelayedJob
8
+ ##
9
+ # ForkHandler
10
+ #
11
+ # Since delayed job doesn't offer a hook into `after_fork`, we alias the method
12
+ # here to do our magic after a fork happens.
13
+ #
14
+ module ForkHandler
15
+ def self.extended(klass)
16
+ ::AppOpticsAPM::Util.class_method_alias(klass, :after_fork, ::Delayed::Worker)
17
+ end
18
+
19
+ def after_fork_with_appoptics
20
+ ::AppOpticsAPM.logger.info '[appoptics_apm/delayed_job] Detected fork. Restarting AppOpticsAPM reporter.' if AppOpticsAPM::Config[:verbose]
21
+ ::AppOpticsAPM::Reporter.restart unless ENV.key?('APPOPTICS_GEM_TEST')
22
+
23
+ after_fork_without_appoptics
24
+ end
25
+ end
26
+
27
+ ##
28
+ # AppOpticsAPM::Inst::DelayedJob::Plugin
29
+ #
30
+ # The AppOpticsAPM DelayedJob plugin. Here we wrap `enqueue` and
31
+ # `perform` to capture the timing of the bits we're interested
32
+ # in.
33
+ #
34
+ class Plugin < Delayed::Plugin
35
+ callbacks do |lifecycle|
36
+
37
+ # enqueue
38
+ if AppOpticsAPM::Config[:delayed_jobclient][:enabled]
39
+ lifecycle.around(:enqueue) do |job, &block|
40
+ begin
41
+ report_kvs = {}
42
+ report_kvs[:Spec] = :pushq
43
+ report_kvs[:Flavor] = :DelayedJob
44
+ report_kvs[:JobName] = job.name
45
+ report_kvs[:MsgID] = job.id
46
+ report_kvs[:Queue] = job.queue if job.queue
47
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:delayed_jobclient][:collect_backtraces]
48
+
49
+ AppOpticsAPM::API.trace(:'delayed_job-client', report_kvs) do
50
+ block.call(job)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ # invoke_job
57
+ if AppOpticsAPM::Config[:delayed_jobworker][:enabled]
58
+ lifecycle.around(:perform) do |worker, job, &block|
59
+ begin
60
+ report_kvs = {}
61
+ report_kvs[:Spec] = :job
62
+ report_kvs[:Flavor] = :DelayedJob
63
+ report_kvs[:JobName] = job.name
64
+ report_kvs[:MsgID] = job.id
65
+ report_kvs[:Queue] = job.queue if job.queue
66
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:delayed_jobworker][:collect_backtraces]
67
+
68
+ # DelayedJob Specific KVs
69
+ report_kvs[:priority] = job.priority
70
+ report_kvs[:attempts] = job.attempts
71
+ report_kvs[:WorkerName] = job.locked_by
72
+ rescue => e
73
+ AppOpticsAPM.logger.warn "[appoptics_apm/warning] inst/delayed_job.rb: #{e.message}"
74
+ end
75
+
76
+ result = AppOpticsAPM::API.start_trace(:'delayed_job-worker', nil, report_kvs) do
77
+ block.call(worker, job)
78
+ AppOpticsAPM::API.log_exception(nil, job.error) if job.error
79
+ end
80
+ result[0]
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ ::AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting delayed_job' if AppOpticsAPM::Config[:verbose]
90
+ ::AppOpticsAPM::Util.send_extend(::Delayed::Worker, ::AppOpticsAPM::Inst::DelayedJob::ForkHandler)
91
+ ::Delayed::Worker.plugins << ::AppOpticsAPM::Inst::DelayedJob::Plugin
92
+ end
@@ -0,0 +1,105 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ module Inst
6
+ module EventMachine
7
+ module HttpConnection
8
+ def setup_request_with_appoptics(*args, &block)
9
+ context = AppOpticsAPM::Context.toString
10
+ blacklisted = AppOpticsAPM::API.blacklisted?(@uri)
11
+
12
+ if AppOpticsAPM.tracing?
13
+ report_kvs = {}
14
+
15
+ begin
16
+ report_kvs[:IsService] = 1
17
+ report_kvs[:RemoteURL] = @uri
18
+ report_kvs[:HTTPMethod] = args[0]
19
+ report_kvs[:Blacklisted] = true if blacklisted
20
+
21
+ if AppOpticsAPM::Config[:em_http_request][:collect_backtraces]
22
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace
23
+ end
24
+ rescue => e
25
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] em-http-request KV error: #{e.inspect}"
26
+ end
27
+
28
+ ::AppOpticsAPM::API.log_entry('em-http-request', report_kvs)
29
+ end
30
+ client = setup_request_without_appoptics(*args, &block)
31
+ client.req.headers['X-Trace'] = context unless blacklisted
32
+ client
33
+ end
34
+ end
35
+
36
+ module HttpClient
37
+ def parse_response_header_with_appoptics(*args, &block)
38
+ report_kvs = {}
39
+ xtrace = nil
40
+ blacklisted = AppOpticsAPM::API.blacklisted?(@uri)
41
+
42
+ begin
43
+ report_kvs[:HTTPStatus] = args[2]
44
+ report_kvs[:Async] = 1
45
+ rescue => e
46
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] em-http-request KV error: #{e.inspect}"
47
+ end
48
+
49
+ parse_response_header_without_appoptics(*args, &block)
50
+
51
+ unless blacklisted
52
+ headers = args[0]
53
+ context = AppOpticsAPM::Context.toString
54
+ task_id = AppOpticsAPM::XTrace.task_id(context)
55
+
56
+ if headers.is_a?(Hash) && headers.key?('X-Trace')
57
+ xtrace = headers['X-Trace']
58
+ end
59
+
60
+ if AppOpticsAPM::XTrace.valid?(xtrace) && AppOpticsAPM.tracing?
61
+
62
+ # Assure that we received back a valid X-Trace with the same task_id
63
+ if task_id == AppOpticsAPM::XTrace.task_id(xtrace)
64
+ AppOpticsAPM::Context.fromString(xtrace)
65
+ else
66
+ AppOpticsAPM.logger.debug "Mismatched returned X-Trace ID : #{xtrace}"
67
+ end
68
+ end
69
+
70
+ end
71
+ ensure
72
+ ::AppOpticsAPM::API.log_exit(:'em-http-request', report_kvs)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ if RUBY_VERSION >= '1.9'
80
+ if defined?(::EventMachine::HttpConnection) && defined?(::EventMachine::HttpClient) && AppOpticsAPM::Config[:em_http_request][:enabled]
81
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting em-http-request' if AppOpticsAPM::Config[:verbose]
82
+
83
+ class ::EventMachine::HttpConnection
84
+ include AppOpticsAPM::Inst::EventMachine::HttpConnection
85
+
86
+ if method_defined?(:setup_request)
87
+ class_eval 'alias :setup_request_without_appoptics :setup_request'
88
+ class_eval 'alias :setup_request :setup_request_with_appoptics'
89
+ else
90
+ AppOpticsAPM.logger.warn '[appoptics_apm/loading] Couldn\'t properly instrument em-http-request (:setup_request). Partial traces may occur.'
91
+ end
92
+ end
93
+
94
+ class ::EventMachine::HttpClient
95
+ include AppOpticsAPM::Inst::EventMachine::HttpClient
96
+
97
+ if method_defined?(:parse_response_header)
98
+ class_eval 'alias :parse_response_header_without_appoptics :parse_response_header'
99
+ class_eval 'alias :parse_response_header :parse_response_header_with_appoptics'
100
+ else
101
+ AppOpticsAPM.logger.warn '[appoptics_apm/loading] Couldn\'t properly instrument em-http-request (:parse_response_header). Partial traces may occur.'
102
+ end
103
+ end
104
+ end
105
+ end