appoptics_apm_mnfst 4.5.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 (104) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +5 -0
  3. data/.github/ISSUE_TEMPLATE/bug-or-feature-request.md +16 -0
  4. data/.gitignore +29 -0
  5. data/.rubocop.yml +8 -0
  6. data/.travis.yml +121 -0
  7. data/.yardopts +4 -0
  8. data/CHANGELOG.md +769 -0
  9. data/CONFIG.md +33 -0
  10. data/Gemfile +29 -0
  11. data/LICENSE +193 -0
  12. data/README.md +393 -0
  13. data/Rakefile +230 -0
  14. data/appoptics_apm.gemspec +61 -0
  15. data/bin/appoptics_apm_config +15 -0
  16. data/build_gem.sh +15 -0
  17. data/build_gem_upload_to_packagecloud.sh +20 -0
  18. data/examples/SDK/01_basic_tracing.rb +67 -0
  19. data/examples/carrying_context.rb +220 -0
  20. data/ext/oboe_metal/extconf.rb +114 -0
  21. data/ext/oboe_metal/lib/.keep +0 -0
  22. data/ext/oboe_metal/noop/noop.c +7 -0
  23. data/ext/oboe_metal/src/VERSION +1 -0
  24. data/init.rb +4 -0
  25. data/lib/appoptics_apm.rb +76 -0
  26. data/lib/appoptics_apm/api.rb +20 -0
  27. data/lib/appoptics_apm/api/layerinit.rb +41 -0
  28. data/lib/appoptics_apm/api/logging.rb +375 -0
  29. data/lib/appoptics_apm/api/memcache.rb +37 -0
  30. data/lib/appoptics_apm/api/metrics.rb +55 -0
  31. data/lib/appoptics_apm/api/profiling.rb +203 -0
  32. data/lib/appoptics_apm/api/tracing.rb +53 -0
  33. data/lib/appoptics_apm/api/util.rb +122 -0
  34. data/lib/appoptics_apm/base.rb +230 -0
  35. data/lib/appoptics_apm/config.rb +254 -0
  36. data/lib/appoptics_apm/frameworks/grape.rb +97 -0
  37. data/lib/appoptics_apm/frameworks/padrino.rb +108 -0
  38. data/lib/appoptics_apm/frameworks/rails.rb +94 -0
  39. data/lib/appoptics_apm/frameworks/rails/inst/action_controller.rb +104 -0
  40. data/lib/appoptics_apm/frameworks/rails/inst/action_controller3.rb +55 -0
  41. data/lib/appoptics_apm/frameworks/rails/inst/action_controller4.rb +48 -0
  42. data/lib/appoptics_apm/frameworks/rails/inst/action_controller5.rb +50 -0
  43. data/lib/appoptics_apm/frameworks/rails/inst/action_controller_api.rb +50 -0
  44. data/lib/appoptics_apm/frameworks/rails/inst/action_view.rb +58 -0
  45. data/lib/appoptics_apm/frameworks/rails/inst/action_view_30.rb +50 -0
  46. data/lib/appoptics_apm/frameworks/rails/inst/active_record.rb +27 -0
  47. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/mysql.rb +43 -0
  48. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/mysql2.rb +29 -0
  49. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/postgresql.rb +31 -0
  50. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils.rb +119 -0
  51. data/lib/appoptics_apm/frameworks/rails/inst/connection_adapters/utils5x.rb +108 -0
  52. data/lib/appoptics_apm/frameworks/sinatra.rb +125 -0
  53. data/lib/appoptics_apm/inst/bunny-client.rb +148 -0
  54. data/lib/appoptics_apm/inst/bunny-consumer.rb +89 -0
  55. data/lib/appoptics_apm/inst/curb.rb +330 -0
  56. data/lib/appoptics_apm/inst/dalli.rb +85 -0
  57. data/lib/appoptics_apm/inst/delayed_job.rb +92 -0
  58. data/lib/appoptics_apm/inst/em-http-request.rb +101 -0
  59. data/lib/appoptics_apm/inst/excon.rb +125 -0
  60. data/lib/appoptics_apm/inst/faraday.rb +94 -0
  61. data/lib/appoptics_apm/inst/grpc_client.rb +162 -0
  62. data/lib/appoptics_apm/inst/grpc_server.rb +120 -0
  63. data/lib/appoptics_apm/inst/http.rb +73 -0
  64. data/lib/appoptics_apm/inst/httpclient.rb +174 -0
  65. data/lib/appoptics_apm/inst/memcached.rb +86 -0
  66. data/lib/appoptics_apm/inst/mongo.rb +246 -0
  67. data/lib/appoptics_apm/inst/mongo2.rb +225 -0
  68. data/lib/appoptics_apm/inst/moped.rb +466 -0
  69. data/lib/appoptics_apm/inst/rack.rb +199 -0
  70. data/lib/appoptics_apm/inst/redis.rb +275 -0
  71. data/lib/appoptics_apm/inst/resque.rb +151 -0
  72. data/lib/appoptics_apm/inst/rest-client.rb +48 -0
  73. data/lib/appoptics_apm/inst/sequel.rb +178 -0
  74. data/lib/appoptics_apm/inst/sidekiq-client.rb +55 -0
  75. data/lib/appoptics_apm/inst/sidekiq-worker.rb +65 -0
  76. data/lib/appoptics_apm/inst/twitter-cassandra.rb +294 -0
  77. data/lib/appoptics_apm/inst/typhoeus.rb +108 -0
  78. data/lib/appoptics_apm/instrumentation.rb +22 -0
  79. data/lib/appoptics_apm/legacy_method_profiling.rb +90 -0
  80. data/lib/appoptics_apm/loading.rb +65 -0
  81. data/lib/appoptics_apm/logger.rb +42 -0
  82. data/lib/appoptics_apm/method_profiling.rb +33 -0
  83. data/lib/appoptics_apm/noop/README.md +9 -0
  84. data/lib/appoptics_apm/noop/context.rb +26 -0
  85. data/lib/appoptics_apm/noop/metadata.rb +22 -0
  86. data/lib/appoptics_apm/ruby.rb +35 -0
  87. data/lib/appoptics_apm/sdk/custom_metrics.rb +92 -0
  88. data/lib/appoptics_apm/sdk/tracing.rb +315 -0
  89. data/lib/appoptics_apm/support.rb +119 -0
  90. data/lib/appoptics_apm/test.rb +94 -0
  91. data/lib/appoptics_apm/thread_local.rb +26 -0
  92. data/lib/appoptics_apm/util.rb +319 -0
  93. data/lib/appoptics_apm/version.rb +15 -0
  94. data/lib/appoptics_apm/xtrace.rb +103 -0
  95. data/lib/joboe_metal.rb +212 -0
  96. data/lib/oboe.rb +7 -0
  97. data/lib/oboe/README +2 -0
  98. data/lib/oboe/backward_compatibility.rb +80 -0
  99. data/lib/oboe/inst/rack.rb +11 -0
  100. data/lib/oboe_metal.rb +198 -0
  101. data/lib/rails/generators/appoptics_apm/install_generator.rb +45 -0
  102. data/lib/rails/generators/appoptics_apm/templates/appoptics_apm_initializer.rb +265 -0
  103. data/yardoc_frontpage.md +26 -0
  104. metadata +266 -0
@@ -0,0 +1,294 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ module Inst
6
+ module Cassandra
7
+ def extract_trace_details(op, column_family, keys, args, options = {})
8
+ report_kvs = {}
9
+
10
+ begin
11
+ report_kvs[:Op] = op.to_s
12
+ report_kvs[:Cf] = column_family.to_s if column_family
13
+ report_kvs[:Key] = keys.inspect if keys
14
+
15
+ # Open issue - how to handle multiple Cassandra servers
16
+ report_kvs[:RemoteHost], report_kvs[:RemotePort] = @servers.first.split(':')
17
+
18
+ report_kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:cassandra][:collect_backtraces]
19
+
20
+ if options.empty? && args.is_a?(Array)
21
+ options = args.last if args.last.is_a?(Hash)
22
+ end
23
+
24
+ unless options.empty?
25
+ [:start_key, :finish_key, :key_count, :batch_size, :columns, :count, :start,
26
+ :stop, :finish, :finished, :reversed, :consistency, :ttl].each do |k|
27
+ report_kvs[k.to_s.capitalize] = options[k] if options.key?(k)
28
+ end
29
+
30
+ if op == :get_indexed_slices
31
+ index_clause = columns_and_options[:index_clause] || {}
32
+ unless index_clause.empty?
33
+ [:column_name, :value, :comparison].each do |k|
34
+ report_kvs[k.to_s.capitalize] = index_clause[k] if index_clause.key?(k)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ rescue => e
40
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" if AppOpticsAPM::Config[:verbose]
41
+ end
42
+
43
+ report_kvs
44
+ end
45
+
46
+ def insert_with_appoptics(column_family, key, hash, options = {})
47
+ return insert_without_appoptics(column_family, key, hash, options = {}) unless AppOpticsAPM.tracing?
48
+
49
+ report_kvs = extract_trace_details(:insert, column_family, key, hash, options)
50
+
51
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
52
+ insert_without_appoptics(column_family, key, hash, options = {})
53
+ end
54
+ end
55
+
56
+ def remove_with_appoptics(column_family, key, *columns_and_options)
57
+ args = [column_family, key] + columns_and_options
58
+
59
+ return send :remove_without_appoptics, *args unless AppOpticsAPM.tracing?
60
+
61
+ report_kvs = extract_trace_details(:remove, column_family, key, columns_and_options)
62
+
63
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
64
+ send :remove_without_appoptics, *args
65
+ end
66
+ end
67
+
68
+ def count_columns_with_appoptics(column_family, key, *columns_and_options)
69
+ args = [column_family, key] + columns_and_options
70
+
71
+ return send :count_columns_without_appoptics, *args unless AppOpticsAPM.tracing?
72
+
73
+ report_kvs = extract_trace_details(:count_columns, column_family, key, columns_and_options)
74
+
75
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
76
+ send :count_columns_without_appoptics, *args
77
+ end
78
+ end
79
+
80
+ def get_columns_with_appoptics(column_family, key, *columns_and_options)
81
+ args = [column_family, key] + columns_and_options
82
+
83
+ if AppOpticsAPM.tracing? && !AppOpticsAPM.tracing_layer_op?(:multi_get_columns)
84
+ report_kvs = extract_trace_details(:get_columns, column_family, key, columns_and_options)
85
+
86
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
87
+ send :get_columns_without_appoptics, *args
88
+ end
89
+ else
90
+ send :get_columns_without_appoptics, *args
91
+ end
92
+ end
93
+
94
+ def multi_get_columns_with_appoptics(column_family, key, *columns_and_options)
95
+ args = [column_family, key] + columns_and_options
96
+
97
+ return send :multi_get_columns_without_appoptics, *args unless AppOpticsAPM.tracing?
98
+
99
+ report_kvs = extract_trace_details(:multi_get_columns, column_family, key, columns_and_options)
100
+
101
+ AppOpticsAPM::API.trace(:cassandra, report_kvs, :multi_get_columns) do
102
+ send :multi_get_columns_without_appoptics, *args
103
+ end
104
+ end
105
+
106
+ def get_with_appoptics(column_family, key, *columns_and_options)
107
+ args = [column_family, key] + columns_and_options
108
+
109
+ return send :get_without_appoptics, *args unless AppOpticsAPM.tracing?
110
+
111
+ report_kvs = extract_trace_details(:get, column_family, key, columns_and_options)
112
+
113
+ AppOpticsAPM::API.trace(:cassandra, report_kvs, :get) do
114
+ send :get_without_appoptics, *args
115
+ end
116
+ end
117
+
118
+ def multi_get_with_appoptics(column_family, key, *columns_and_options)
119
+ args = [column_family, key] + columns_and_options
120
+
121
+ if AppOpticsAPM.tracing? && !AppOpticsAPM.tracing_layer_op?(:get)
122
+ report_kvs = extract_trace_details(:multi_get, column_family, key, columns_and_options)
123
+
124
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
125
+ send :multi_get_without_appoptics, *args
126
+ end
127
+ else
128
+ send :multi_get_without_appoptics, *args
129
+ end
130
+ end
131
+
132
+ def exists_with_appoptics?(column_family, key, *columns_and_options)
133
+ args = [column_family, key] + columns_and_options
134
+
135
+ return send :exists_without_appoptics?, *args unless AppOpticsAPM.tracing?
136
+
137
+ report_kvs = extract_trace_details(:exists?, column_family, key, columns_and_options)
138
+
139
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
140
+ send :exists_without_appoptics?, *args
141
+ end
142
+ end
143
+
144
+ def get_range_single_with_appoptics(column_family, options = {})
145
+ if AppOpticsAPM.tracing? && !AppOpticsAPM.tracing_layer_op?(:get_range_batch)
146
+ report_kvs = extract_trace_details(:get_range_single, column_family, nil, nil)
147
+
148
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
149
+ get_range_single_without_appoptics(column_family, options)
150
+ end
151
+ else
152
+ get_range_single_without_appoptics(column_family, options)
153
+ end
154
+ end
155
+
156
+ def get_range_batch_with_appoptics(column_family, options = {})
157
+ return get_range_batch_without_appoptics(column_family, options) unless AppOpticsAPM.tracing?
158
+
159
+ report_kvs = extract_trace_details(:get_range_batch, column_family, nil, nil)
160
+
161
+ AppOpticsAPM::API.trace(:cassandra, report_kvs, :get_range_batch) do
162
+ get_range_batch_without_appoptics(column_family, options)
163
+ end
164
+ end
165
+
166
+ def get_indexed_slices_with_appoptics(column_family, index_clause, *columns_and_options)
167
+ args = [column_family, index_clause] + columns_and_options
168
+
169
+ return send :get_indexed_slices_without_appoptics, *args unless AppOpticsAPM.tracing?
170
+
171
+ report_kvs = extract_trace_details(:get_indexed_slices, column_family, nil, columns_and_options)
172
+
173
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
174
+ send :get_indexed_slices_without_appoptics, *args
175
+ end
176
+ end
177
+
178
+ def create_index_with_appoptics(keyspace, column_family, column_name, validation_class)
179
+ unless AppOpticsAPM.tracing?
180
+ return create_index_without_appoptics(keyspace, column_family, column_name, validation_class)
181
+ end
182
+
183
+ report_kvs = extract_trace_details(:create_index, column_family, nil, nil)
184
+ begin
185
+ report_kvs[:Keyspace] = keyspace.to_s
186
+ report_kvs[:Column_name] = column_name.to_s
187
+ report_kvs[:Validation_class] = validation_class.to_s
188
+ rescue => e
189
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" if AppOpticsAPM::Config[:verbose]
190
+ end
191
+
192
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
193
+ create_index_without_appoptics(keyspace, column_family, column_name, validation_class)
194
+ end
195
+ end
196
+
197
+ def drop_index_with_appoptics(keyspace, column_family, column_name)
198
+ return drop_index_without_appoptics(keyspace, column_family, column_name) unless AppOpticsAPM.tracing?
199
+
200
+ report_kvs = extract_trace_details(:drop_index, column_family, nil, nil)
201
+ begin
202
+ report_kvs[:Keyspace] = keyspace.to_s
203
+ report_kvs[:Column_name] = column_name.to_s
204
+ rescue => e
205
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" if AppOpticsAPM::Config[:verbose]
206
+ end
207
+
208
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
209
+ drop_index_without_appoptics(keyspace, column_family, column_name)
210
+ end
211
+ end
212
+
213
+ def add_column_family_with_appoptics(cf_def)
214
+ return add_column_family_without_appoptics(cf_def) unless AppOpticsAPM.tracing?
215
+
216
+ report_kvs = extract_trace_details(:add_column_family, nil, nil, nil)
217
+ begin
218
+ report_kvs[:Cf] = cf_def[:name] if cf_def.is_a?(Hash) && cf_def.key?(:name)
219
+ rescue => e
220
+ AppOpticsAPM.logger.debug "[appoptics_apm/debug] #{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" if AppOpticsAPM::Config[:verbose]
221
+ end
222
+
223
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
224
+ add_column_family_without_appoptics(cf_def)
225
+ end
226
+ end
227
+
228
+ def drop_column_family_with_appoptics(column_family)
229
+ return drop_column_family_without_appoptics(column_family) unless AppOpticsAPM.tracing?
230
+
231
+ report_kvs = extract_trace_details(:drop_column_family, column_family, nil, nil)
232
+
233
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
234
+ drop_column_family_without_appoptics(column_family)
235
+ end
236
+ end
237
+
238
+ def add_keyspace_with_appoptics(ks_def)
239
+ return add_keyspace_without_appoptics(ks_def) unless AppOpticsAPM.tracing?
240
+
241
+ report_kvs = extract_trace_details(:add_keyspace, nil, nil, nil)
242
+ report_kvs[:Name] = ks_def.name rescue ''
243
+
244
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
245
+ add_keyspace_without_appoptics(ks_def)
246
+ end
247
+ end
248
+
249
+ def drop_keyspace_with_appoptics(keyspace)
250
+ return drop_keyspace_without_appoptics(keyspace) unless AppOpticsAPM.tracing?
251
+
252
+ report_kvs = extract_trace_details(:drop_keyspace, nil, nil, nil)
253
+ report_kvs[:Name] = keyspace.to_s rescue ''
254
+
255
+ AppOpticsAPM::API.trace(:cassandra, report_kvs) do
256
+ drop_keyspace_without_appoptics(keyspace)
257
+ end
258
+ end
259
+ end
260
+ end
261
+ end
262
+
263
+ # There are two main Cassandra clients for Ruby. This one from Twitter
264
+ # and the other from datastax. This one defined Cassandra as a class
265
+ # and datastax defines it as a module. We use this to detect
266
+ # and differentiate between the client in use.
267
+
268
+ if defined?(Cassandra) && Cassandra.is_a?(Class) && AppOpticsAPM::Config[:cassandra][:enabled]
269
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting cassandra' if AppOpticsAPM::Config[:verbose]
270
+
271
+ class Cassandra
272
+ include AppOpticsAPM::Inst::Cassandra
273
+
274
+ [:insert, :remove, :count_columns, :get_columns, :multi_get_columns, :get,
275
+ :multi_get, :get_range_single, :get_range_batch, :get_indexed_slices,
276
+ :create_index, :drop_index, :add_column_family, :drop_column_family,
277
+ :add_keyspace, :drop_keyspace].each do |m|
278
+ if method_defined?(m)
279
+ class_eval "alias #{m}_without_appoptics #{m}"
280
+ class_eval "alias #{m} #{m}_with_appoptics"
281
+ else AppOpticsAPM.logger.warn "[appoptics_apm/loading] Couldn't properly instrument Cassandra (#{m}). Partial traces may occur."
282
+ end
283
+ end
284
+
285
+ # Special case handler for question mark methods
286
+ if method_defined?(:exists?)
287
+ alias exists_without_appoptics? exists?
288
+ alias exists? exists_with_appoptics?
289
+ else AppOpticsAPM.logger.warn '[appoptics_apm/loading] Couldn\'t properly instrument Cassandra (exists?). Partial traces may occur.'
290
+ end
291
+ end # class Cassandra
292
+ end
293
+
294
+
@@ -0,0 +1,108 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+ class TyphoeusError < StandardError; end
4
+
5
+ module AppOpticsAPM
6
+ module Inst
7
+ module TyphoeusRequestOps
8
+
9
+ def self.included(klass)
10
+ AppOpticsAPM::Util.method_alias(klass, :run, ::Typhoeus::Request::Operations)
11
+ end
12
+
13
+ def run_with_appoptics
14
+ blacklisted = AppOpticsAPM::API.blacklisted?(url)
15
+ unless AppOpticsAPM.tracing?
16
+ context = AppOpticsAPM::Context.toString
17
+ options[:headers]['X-Trace'] = context if AppOpticsAPM::XTrace.valid?(context) && !blacklisted
18
+ return run_without_appoptics
19
+ end
20
+
21
+ begin
22
+ AppOpticsAPM::API.log_entry(:typhoeus)
23
+
24
+ # Prepare X-Trace header handling
25
+ context = AppOpticsAPM::Context.toString
26
+ options[:headers]['X-Trace'] = context unless blacklisted
27
+
28
+ kvs = {}
29
+ kvs[:Spec] = 'rsc'
30
+ kvs[:IsService] = 1
31
+ kvs[:HTTPMethod] = AppOpticsAPM::Util.upcase(options[:method])
32
+
33
+ response = run_without_appoptics
34
+
35
+ # Re-attach edge unless it's blacklisted
36
+ # or if we don't have a valid X-Trace header
37
+ unless blacklisted
38
+ xtrace = response.headers['X-Trace']
39
+ AppOpticsAPM::XTrace.continue_service_context(context, xtrace)
40
+ end
41
+
42
+ if response.code == 0
43
+ exception = TyphoeusError.new(response.return_message)
44
+ exception.set_backtrace(AppOpticsAPM::API.backtrace)
45
+ AppOpticsAPM::API.log_exception(:typhoeus, exception)
46
+ end
47
+
48
+ kvs[:HTTPStatus] = response.code
49
+ kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:typhoeus][:collect_backtraces]
50
+ # Conditionally log query params
51
+ uri = URI(response.effective_url)
52
+ kvs[:RemoteURL] = AppOpticsAPM::Config[:typhoeus][:log_args] ? uri.to_s : uri.to_s.split('?').first
53
+ kvs[:Blacklisted] = true if blacklisted
54
+
55
+ response
56
+ rescue => e
57
+ AppOpticsAPM::API.log_exception(:typhoeus, e)
58
+ raise e
59
+ ensure
60
+ AppOpticsAPM::API.log_exit(:typhoeus, kvs)
61
+ end
62
+ end
63
+ end
64
+
65
+ module TyphoeusHydraRunnable
66
+ def self.included(klass)
67
+ AppOpticsAPM::Util.method_alias(klass, :run, ::Typhoeus::Hydra)
68
+ end
69
+
70
+ def run_with_appoptics
71
+ unless AppOpticsAPM.tracing?
72
+ context = AppOpticsAPM::Context.toString
73
+ queued_requests.map do |request|
74
+ blacklisted = AppOpticsAPM::API.blacklisted?(request.base_url)
75
+ request.options[:headers]['X-Trace'] = context if AppOpticsAPM::XTrace.valid?(context) && !blacklisted
76
+ end
77
+ return run_without_appoptics
78
+ end
79
+
80
+ kvs = {}
81
+
82
+ kvs[:queued_requests] = queued_requests.count
83
+ kvs[:max_concurrency] = max_concurrency
84
+ kvs[:Async] = 1
85
+ kvs[:Backtrace] = AppOpticsAPM::API.backtrace if AppOpticsAPM::Config[:typhoeus][:collect_backtraces]
86
+
87
+ # FIXME: Until we figure out a strategy to deal with libcurl internal
88
+ # threading and Ethon's use of easy handles, here we just do a simple
89
+ # trace of the hydra run.
90
+ AppOpticsAPM::API.trace(:typhoeus_hydra, kvs) do
91
+ queued_requests.map do |request|
92
+ blacklisted = AppOpticsAPM::API.blacklisted?(request.base_url)
93
+ request.options[:headers]['X-Trace'] = AppOpticsAPM::Context.toString unless blacklisted
94
+ end
95
+
96
+ run_without_appoptics
97
+ end
98
+ end
99
+ end
100
+
101
+ end
102
+ end
103
+
104
+ if defined?(Typhoeus) && AppOpticsAPM::Config[:typhoeus][:enabled]
105
+ AppOpticsAPM.logger.info '[appoptics_apm/loading] Instrumenting typhoeus' if AppOpticsAPM::Config[:verbose]
106
+ AppOpticsAPM::Util.send_include(Typhoeus::Request::Operations, AppOpticsAPM::Inst::TyphoeusRequestOps)
107
+ AppOpticsAPM::Util.send_include(Typhoeus::Hydra, AppOpticsAPM::Inst::TyphoeusHydraRunnable)
108
+ end
@@ -0,0 +1,22 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ module AppOpticsAPM
5
+ ##
6
+ # The Inst module holds all of the instrumentation extensions for various
7
+ # libraries such as Redis, Dalli and Resque.
8
+ module Inst
9
+ def self.load_instrumentation
10
+ # Load the general instrumentation
11
+ pattern = File.join(File.dirname(__FILE__), 'inst', '*.rb')
12
+ Dir.glob(pattern) do |f|
13
+ begin
14
+ require f
15
+ rescue => e
16
+ AppOpticsAPM.logger.error "[appoptics_apm/loading] Error loading instrumentation file '#{f}' : #{e}"
17
+ AppOpticsAPM.logger.debug "[appoptics_apm/loading] #{e.backtrace.first}"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,90 @@
1
+ # Copyright (c) 2016 SolarWinds, LLC.
2
+ # All rights reserved.
3
+
4
+ ##
5
+ # Provides the methods necessary for method profiling. Profiling
6
+ # results are sent to the AppOptics dashboard.
7
+ #
8
+ # Example usage:
9
+ # class MyApp
10
+ # include AppOpticsAPMMethodProfiling
11
+ #
12
+ # def process_request()
13
+ # # The hard work
14
+ # end
15
+ #
16
+ # # call syntax: profile_method <method>, <profile_name>
17
+ # profile_method :process_request, 'request_processor'
18
+ # end
19
+ module AppOpticsAPMMethodProfiling
20
+ def self.included(klass)
21
+ klass.extend ClassMethods
22
+ end
23
+
24
+ module ClassMethods
25
+ def profile_method_noop(*args)
26
+ nil
27
+ end
28
+
29
+ def profile_method_real(method_name, profile_name, store_args = false, store_return = false, *_)
30
+ begin
31
+ # this only gets file and line where profiling is turned on, presumably
32
+ # right after the function definition.
33
+ file = ''
34
+ line = ''
35
+ info = instance_method(method_name).source_location
36
+ unless info.nil?
37
+ file = info[0].to_s
38
+ line = info[1].to_s
39
+ end
40
+
41
+ # Safety: Make sure there are no quotes or double quotes to break the class_eval
42
+ file = file.gsub(/[\'\"]/, '')
43
+ line = line.gsub(/[\'\"]/, '')
44
+
45
+ # profiling via ruby-prof, is it possible to get return value of profiled code?
46
+ code = "def _appoptics_profiled_#{method_name}(*args, &block)
47
+ entry_kvs = {}
48
+ entry_kvs['Language'] = 'ruby'
49
+ entry_kvs['ProfileName'] = '#{AppOpticsAPM::Util.prettify(profile_name)}'
50
+ entry_kvs['FunctionName'] = '#{AppOpticsAPM::Util.prettify(method_name)}'
51
+ entry_kvs['File'] = '#{file}'
52
+ entry_kvs['LineNumber'] = '#{line}'
53
+ entry_kvs['Args'] = AppOpticsAPM::API.pps(*args) if #{store_args}
54
+ entry_kvs.merge!(AppOpticsAPM::API.get_class_name(self))
55
+
56
+ AppOpticsAPM::API.log(nil, 'profile_entry', entry_kvs)
57
+
58
+ ret = _appoptics_orig_#{method_name}(*args, &block)
59
+
60
+ exit_kvs = {}
61
+ exit_kvs['Language'] = 'ruby'
62
+ exit_kvs['ProfileName'] = '#{AppOpticsAPM::Util.prettify(profile_name)}'
63
+ exit_kvs['ReturnValue'] = AppOpticsAPM::API.pps(ret) if #{store_return}
64
+
65
+ AppOpticsAPM::API.log(nil, 'profile_exit', exit_kvs)
66
+ ret
67
+ end"
68
+ rescue => e
69
+ AppOpticsAPM.logger.warn "[appoptics_apm/warn] profile_method: #{e.inspect}"
70
+ end
71
+
72
+ begin
73
+ class_eval code, __FILE__, __LINE__
74
+ alias_method "_appoptics_orig_#{method_name}", method_name
75
+ alias_method method_name, "_appoptics_profiled_#{method_name}"
76
+ rescue => e
77
+ AppOpticsAPM.logger.warn "[appoptics_apm/warn] Fatal error profiling method (#{method_name}): #{e.inspect}" if AppOpticsAPM::Config[:verbose]
78
+ end
79
+ end
80
+
81
+ # This allows this module to be included and called even if the gem is in
82
+ # no-op mode (no base libraries).
83
+ if AppOpticsAPM.loaded
84
+ alias :profile_method :profile_method_real
85
+ else
86
+ alias :profile_method :profile_method_noop
87
+ end
88
+
89
+ end
90
+ end