scout_apm 3.0.0.pre8 → 3.0.0.pre9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 63c9b4319eecbefb59fdb10fb0fbc0d264dfb7a0
4
- data.tar.gz: 821271be9ef95416e72b659a825895513d80017d
3
+ metadata.gz: ed6963870425b875415255f3aa5bb51144b5fabd
4
+ data.tar.gz: f31e6e8780992531d3a87733b3f4ef5bc3e11ab8
5
5
  SHA512:
6
- metadata.gz: 330d8721be955d8676285665d7db211919b1867da0720b1b24c663119f434cb7aaffa9a55cec27d8286633d40d924b337283fea0c45195d872900f54a3c6c041
7
- data.tar.gz: 0eef9b024471d03e689aaceffd56402c32a5f0904e6c9124209bb60e4d06173f281fc25b3562225303b5c8292be7daab2f5233618a098454c646da597d4913fa
6
+ metadata.gz: bb4b61f29fc72f0c1eeec3c9bc2ac86b57a66605a5d8dba43612dc686d95b75c6658665d8c2106ae637c794f2a7af689796163aa31e4c8e561610a16f2064778
7
+ data.tar.gz: d7a9137fcdd64016a75cb74f1c4f02c653c38dff5621b6d23baaefa1c4a5657fbd13dc8d7ccd25844ee0c7ab44771becb4982754e6a48589c92624cfad012919
data/CHANGELOG.markdown CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  * ScoutProf BETA
4
4
 
5
+ # 2.1.22
6
+
7
+ * Add DevTrace support for newest 4.2.x and 5.x versions of Rails
8
+
9
+ # 2.1.21
10
+
11
+ * Fix edge case, causing DevTrace to fail
12
+ * Add debug tooling, allowing custom functions to be inserted into the agent at
13
+ key points.
14
+
15
+ # 2.1.20
16
+
17
+ * Add a `detailed_middleware` boolean configuration option to capture
18
+ per-middleware data, as opposed to the default of aggregating all middleware
19
+ together. This has a small amount of additional overhead, approximately
20
+ 10-15ms per request.
21
+
5
22
  # 2.1.19
6
23
 
7
24
  * Log all configuration settings at start when log level is debug
@@ -72,7 +72,7 @@ module ScoutApm
72
72
  log_deliver(metrics, slow_transactions, metadata, slow_jobs, histograms)
73
73
 
74
74
  payload = ScoutApm::Serializers::PayloadSerializer.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms)
75
- # logger.debug("Payload: #{payload}")
75
+ logger.debug("Sending payload w/ Headers: #{headers.inspect}")
76
76
 
77
77
  reporter.report(payload, headers)
78
78
  rescue => e
@@ -265,6 +265,7 @@ module ScoutApm
265
265
  @background_worker = ScoutApm::BackgroundWorker.new
266
266
  @background_worker_thread = Thread.new do
267
267
  @background_worker.start {
268
+ ScoutApm::Debug.instance.call_periodic_hooks
268
269
  ScoutApm::Agent.instance.process_metrics
269
270
  clean_old_percentiles
270
271
  }
@@ -293,8 +294,13 @@ module ScoutApm
293
294
  install_instrument(ScoutApm::Instruments::ActionControllerRails2)
294
295
  when :rails3_or_4 then
295
296
  install_instrument(ScoutApm::Instruments::ActionControllerRails3Rails4)
296
- install_instrument(ScoutApm::Instruments::MiddlewareSummary)
297
297
  install_instrument(ScoutApm::Instruments::RailsRouter)
298
+
299
+ if config.value("detailed_middleware")
300
+ install_instrument(ScoutApm::Instruments::MiddlewareDetailed)
301
+ else
302
+ install_instrument(ScoutApm::Instruments::MiddlewareSummary)
303
+ end
298
304
  end
299
305
 
300
306
  install_instrument(ScoutApm::Instruments::ActiveRecord)
@@ -37,6 +37,7 @@ module ScoutApm
37
37
  'compress_payload',
38
38
  'config_file',
39
39
  'data_file',
40
+ 'detailed_middleware',
40
41
  'dev_trace',
41
42
  'direct_host',
42
43
  'disabled_instruments',
@@ -45,8 +46,8 @@ module ScoutApm
45
46
  'hostname',
46
47
  'ignore',
47
48
  'key',
48
- 'log_level',
49
49
  'log_file_path',
50
+ 'log_level',
50
51
  'monitor',
51
52
  'name',
52
53
  'profile',
@@ -129,6 +130,7 @@ module ScoutApm
129
130
  "monitor" => BooleanCoercion.new,
130
131
  "enable_background_jobs" => BooleanCoercion.new,
131
132
  "dev_trace" => BooleanCoercion.new,
133
+ "detailed_middleware" => BooleanCoercion.new,
132
134
  "ignore" => JsonCoercion.new,
133
135
  }
134
136
 
@@ -203,6 +205,7 @@ module ScoutApm
203
205
  class ConfigDefaults
204
206
  DEFAULTS = {
205
207
  'compress_payload' => true,
208
+ 'detailed_middleware' => false,
206
209
  'dev_trace' => false,
207
210
  'direct_host' => 'https://apm.scoutapp.com',
208
211
  'disabled_instruments' => [],
@@ -0,0 +1,37 @@
1
+ module ScoutApm
2
+ class Debug
3
+ # see self.instance
4
+ @@instance = nil
5
+
6
+ def self.instance
7
+ @@instance ||= new
8
+ end
9
+
10
+ def register_periodic_hook(&hook)
11
+ @periodic_hooks << hook
12
+ end
13
+
14
+ def call_periodic_hooks
15
+ @periodic_hooks.each do |hook|
16
+ begin
17
+ hook.call
18
+ rescue => e
19
+ logger.info("Periodic debug hook failed to run: #{e}\n\t#{e.backtrace.join("\n\t")}")
20
+ end
21
+ end
22
+ rescue
23
+ # Something went super wrong for the inner rescue to not catch this. Just
24
+ # swallow the error. The debug tool should never crash the app.
25
+ end
26
+
27
+ private
28
+
29
+ def initialize
30
+ @periodic_hooks = []
31
+ end
32
+
33
+ def logger
34
+ ScoutApm::Agent.instance.logger
35
+ end
36
+ end
37
+ end
@@ -3,7 +3,7 @@
3
3
  (function(){var open=window.XMLHttpRequest.prototype.open;var send=window.XMLHttpRequest.prototype.send;function openReplacement(method,url,async,user,password){this._url=url;return open.apply(this,arguments);}
4
4
  function sendReplacement(data){if(this.onload){this._onload=this.onload;}
5
5
  this.onload=onLoadReplacement;return send.apply(this,arguments);}
6
- function onLoadReplacement(){if(this._url.startsWith(window.location.protocol+"//"+window.location.host)||!this._url.startsWith("http")){try{traceText=this.getResponseHeader("X-scoutapminstant");if(traceText){setTimeout(function(){window.scoutInstant("addTrace",traceText)},0);}}catch(e){console.debug("Problem getting X-scoutapminstant header");}}
6
+ function onLoadReplacement(){if(this._url.startsWith(window.location.protocol+"//"+window.location.host)||!this._url.startsWith("http")){try{var traceText=this.getResponseHeader("X-scoutapminstant");if(traceText){setTimeout(function(){window.scoutInstant("addTrace",traceText)},0);}}catch(e){console.debug("Problem getting X-scoutapminstant header");}}
7
7
  if(this._onload){return this._onload.apply(this,arguments);}}
8
8
  window.XMLHttpRequest.prototype.open=openReplacement;window.XMLHttpRequest.prototype.send=sendReplacement;})();
9
- </script>
9
+ </script>
@@ -5,6 +5,11 @@ module ScoutApm
5
5
  class Page
6
6
  def initialize(html)
7
7
  @html = html
8
+
9
+ if html.is_a?(Array)
10
+ @html = html.inject("") { |memo, str| memo + str }
11
+ end
12
+
8
13
  @to_add_to_head = []
9
14
  @to_add_to_body = []
10
15
  end
@@ -45,66 +50,200 @@ module ScoutApm
45
50
  end
46
51
 
47
52
  def call(env)
48
- status, headers, response = @app.call(env)
49
- path, content_type = env['PATH_INFO'], headers['Content-Type']
50
- if ScoutApm::Agent.instance.config.value('dev_trace')
51
- if response.respond_to?(:body)
52
- req = ScoutApm::RequestManager.lookup
53
-
54
- return [status, headers, response] if req.ignoring_request?
55
-
56
- slow_converter = LayerConverters::SlowRequestConverter.new(req)
57
- trace = slow_converter.call
58
- if trace
59
- metadata = {
60
- :app_root => ScoutApm::Environment.instance.root.to_s,
61
- :unique_id => env['action_dispatch.request_id'], # note, this is a different unique_id than what "normal" payloads use
62
- :agent_version => ScoutApm::VERSION,
63
- :platform => "ruby",
64
- }
65
- hash = ScoutApm::Serializers::PayloadSerializerToJson.rearrange_slow_transaction(trace)
66
- hash.merge!(:metadata => metadata)
67
- payload = ScoutApm::Serializers::PayloadSerializerToJson.jsonify_hash(hash)
68
-
69
- if env['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' || content_type.include?("application/json")
70
- ScoutApm::Agent.instance.logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body. This is either AJAX or JSON. Path=#{path}; ContentType=#{content_type}")
71
- # Add the payload as a header if it's an AJAX call or JSON
72
- headers['X-scoutapminstant'] = payload
73
- [status, headers, response]
74
- else
75
- # otherwise, attempt to add it inline in the page, along with the appropriate JS & CSS. Note, if page doesn't have a head or body,
76
- #duration = (req.root_layer.total_call_time*1000).to_i
77
- apm_host=ScoutApm::Agent.instance.config.value("direct_host")
78
- page = ScoutApm::Instant::Page.new(response.body)
79
- page.add_to_head(ScoutApm::Instant::Util.read_asset("xmlhttp_instrumentation.html")) # This monkey-patches XMLHttpRequest. It could possibly be part of the main scout_instant.js too. Putting it here so it runs as soon as possible.
80
- page.add_to_head("<link href='#{apm_host}/instant/scout_instant.css?cachebust=#{Time.now.to_i}' media='all' rel='stylesheet' />")
81
- page.add_to_body("<script src='#{apm_host}/instant/scout_instant.js?cachebust=#{Time.now.to_i}'></script>")
82
- page.add_to_body("<script>var scoutInstantPageTrace=#{payload};window.scoutInstant=window.scoutInstant('#{apm_host}', scoutInstantPageTrace)</script>")
83
-
84
- if response.is_a?(ActionDispatch::Response)
85
- ScoutApm::Agent.instance.logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body. This appears to be an HTML page and an ActionDispatch::Response. Path=#{path}; ContentType=#{content_type}")
86
- # preserve the ActionDispatch::Response when applicable
87
- response.body=[page.res]
88
- [status, headers, response]
89
- else
90
- ScoutApm::Agent.instance.logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body. This appears to be an HTML page but not an ActionDispatch::Response. Path=#{path}; ContentType=#{content_type}")
91
- # otherwise, just return an array
92
- [status, headers, [page.res]]
93
- end
94
- end
95
- else
96
- ScoutApm::Agent.instance.logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body, but no trace was found. Path=#{path}; ContentType=#{content_type}")
97
- [status, headers, response]
98
- end
99
- else
100
- # don't log anything here - this is the path for all assets served in development, and the log would get noisy
101
- [status, headers, response]
102
- end
53
+ rack_response = @app.call(env)
54
+ begin
55
+ DevTraceResponseManipulator.new(env, rack_response).call
56
+ rescue Exception => e
57
+ # If anything went wrong at all, just bail out and return the unmodified response.
58
+ ScoutApm::Agent.instance.logger.debug("DevTrace: Raised an exception: #{e.message}, #{e.backtrace}")
59
+ rack_response
60
+ end
61
+ end
62
+ end
63
+
64
+ class DevTraceResponseManipulator
65
+ attr_reader :rack_response
66
+ attr_reader :rack_status, :rack_headers, :rack_body
67
+ attr_reader :env
68
+
69
+ def initialize(env, rack_response)
70
+ @env = env
71
+ @rack_response = rack_response
72
+
73
+ @rack_status = rack_response[0]
74
+ @rack_headers = rack_response[1]
75
+ @rack_body = rack_response[2]
76
+ end
77
+
78
+ def call
79
+ return rack_response unless preconditions_met?
80
+
81
+ if ajax_request?
82
+ ScoutApm::Agent.instance.logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body. This is either AJAX or JSON. Path=#{path}; ContentType=#{content_type}")
83
+ adjust_ajax_header
84
+ else
85
+ adjust_html_response
86
+ end
87
+
88
+ rebuild_rack_response
89
+ end
90
+
91
+ ###########################
92
+ # Precondition checking #
93
+ ###########################
94
+
95
+ def preconditions_met?
96
+ if dev_trace_disabled?
97
+ logger.debug("DevTrace: isn't activated via config. Try: SCOUT_DEV_TRACE=true rails server")
98
+ return false
99
+ end
100
+
101
+ # Don't attempt to instrument assets.
102
+ # Don't log this case, since it would be very noisy
103
+ logger.debug("DevTrace: dev asset ignored") and return false if development_asset?
104
+
105
+ # If we didn't have a tracked_request object, or we explicitly ignored
106
+ # this request, don't do any work.
107
+ logger.debug("DevTrace: no tracked request") and return false if tracked_request.nil? || tracked_request.ignoring_request?
108
+
109
+ # If we didn't get a trace, we can't show a trace...
110
+ if trace.nil?
111
+ logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body, but no trace was found. Path=#{path}; ContentType=#{content_type}")
112
+ return false
113
+ end
114
+
115
+ true
116
+ end
117
+
118
+ def dev_trace_disabled?
119
+ ! ScoutApm::Agent.instance.config.value('dev_trace')
120
+ end
121
+
122
+ ########################
123
+ # Response Injection #
124
+ ########################
125
+
126
+ def rebuild_rack_response
127
+ [rack_status, rack_headers, rack_body]
128
+ end
129
+
130
+ def adjust_ajax_header
131
+ rack_headers['X-scoutapminstant'] = payload
132
+ end
133
+
134
+ def adjust_html_response
135
+ case true
136
+ when older_rails_response? then adjust_older_rails_response
137
+ when newer_rails_response? then adjust_newer_rails_response
138
+ when rack_proxy_response? then adjust_rack_proxy_response
103
139
  else
104
- ScoutApm::Agent.instance.logger.debug("DevTrace: isn't activated via config. Try: SCOUT_DEV_TRACE=true rails server")
105
- [status, headers, response]
140
+ # No action taken, we only adjust if we know exactly what we have.
141
+ end
142
+ end
143
+
144
+ def older_rails_response?
145
+ if defined?(ActionDispatch::Response)
146
+ return true if rack_body.is_a?(ActionDispatch::Response)
147
+ end
148
+ end
149
+
150
+ def newer_rails_response?
151
+ if defined?(ActionDispatch::Response::RackBody)
152
+ return true if rack_body.is_a?(ActionDispatch::Response::RackBody)
106
153
  end
107
154
  end
155
+
156
+ def rack_proxy_response?
157
+ rack_body.is_a?(Rack::BodyProxy)
158
+ end
159
+
160
+ def adjust_older_rails_response
161
+ logger.debug("DevTrace: in middleware, dev_trace is active, and response has a (older) body. This appears to be an HTML page and an ActionDispatch::Response. Path=#{path}; ContentType=#{content_type}")
162
+ rack_body.body = [ html_manipulator.res ]
163
+ end
164
+
165
+ # Preserve the ActionDispatch::Response object we're working with
166
+ def adjust_newer_rails_response
167
+ logger.debug("DevTrace: in middleware, dev_trace is active, and response has a (newer) body. This appears to be an HTML page and an ActionDispatch::Response. Path=#{path}; ContentType=#{content_type}")
168
+ @rack_body = [ html_manipulator.res ]
169
+ end
170
+
171
+ def adjust_rack_proxy_response
172
+ logger.debug("DevTrace: in middleware, dev_trace is active, and response has a body. This appears to be an HTML page and an Rack::BodyProxy. Path=#{path}; ContentType=#{content_type}")
173
+ @rack_body = [ html_manipulator.res ]
174
+ @rack_headers.delete("Content-Length")
175
+ end
176
+
177
+ def html_manipulator
178
+ @html_manipulator ||=
179
+ begin
180
+ page = ScoutApm::Instant::Page.new(rack_body.body)
181
+
182
+ # This monkey-patches XMLHttpRequest. It could possibly be part of the main scout_instant.js too. Putting it here so it runs as soon as possible.
183
+ page.add_to_head(ScoutApm::Instant::Util.read_asset("xmlhttp_instrumentation.html"))
184
+
185
+ # Add a link to CSS, then JS
186
+ page.add_to_head("<link href='#{apm_host}/instant/scout_instant.css?cachebust=#{Time.now.to_i}' media='all' rel='stylesheet' />")
187
+ page.add_to_body("<script src='#{apm_host}/instant/scout_instant.js?cachebust=#{Time.now.to_i}'></script>")
188
+ page.add_to_body("<script>var scoutInstantPageTrace=#{payload};window.scoutInstant=window.scoutInstant('#{apm_host}', scoutInstantPageTrace)</script>")
189
+
190
+ page
191
+ end
192
+ end
193
+
194
+ def ajax_request?
195
+ env['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' || content_type.include?("application/json")
196
+ end
197
+
198
+ def development_asset?
199
+ !rack_body.respond_to?(:body)
200
+ end
201
+
202
+ def path
203
+ env['PATH_INFO']
204
+ end
205
+
206
+ def content_type
207
+ rack_headers['Content-Type']
208
+ end
209
+
210
+ ##############################
211
+ # APM Helpers & Shorthands #
212
+ ##############################
213
+
214
+ def logger
215
+ ScoutApm::Agent.instance.logger
216
+ end
217
+
218
+ def tracked_request
219
+ @tracked_request ||= ScoutApm::RequestManager.lookup
220
+ end
221
+
222
+ def apm_host
223
+ ScoutApm::Agent.instance.config.value("direct_host")
224
+ end
225
+
226
+ def trace
227
+ @trace ||= LayerConverters::SlowRequestConverter.new(tracked_request).call
228
+ end
229
+
230
+ def payload
231
+ @payload ||=
232
+ begin
233
+ metadata = {
234
+ :app_root => ScoutApm::Environment.instance.root.to_s,
235
+ :unique_id => env['action_dispatch.request_id'], # note, this is a different unique_id than what "normal" payloads use
236
+ :agent_version => ScoutApm::VERSION,
237
+ :platform => "ruby",
238
+ }
239
+
240
+ hash = ScoutApm::Serializers::PayloadSerializerToJson.
241
+ rearrange_slow_transaction(trace).
242
+ merge!(:metadata => metadata)
243
+ ScoutApm::Serializers::PayloadSerializerToJson.jsonify_hash(hash)
244
+ end
245
+ end
246
+
108
247
  end
109
248
  end
110
249
  end
@@ -1,13 +1,11 @@
1
1
  # Inserts a new middleware between each actual middleware in the application,
2
2
  # so as to trace the time for each one.
3
3
  #
4
- # Currently disabled due to the overhead of this approach (~10-15ms per request
5
- # in practice). Instead, middleware as a whole are instrumented via the
6
- # MiddlewareSummary class.
4
+ # Currently disabled by default due to the overhead of this approach (~10-15ms
5
+ # per request in practice). Instead, middleware as a whole are instrumented
6
+ # via the MiddlewareSummary class.
7
7
  #
8
- # There will likely be a configuration flag to turn this on in favor of the
9
- # summary tracing in a future version of the agent, but this is not yet
10
- # implemented.
8
+ # Turn this on with the configuration setting `detailed_middleware` set to true
11
9
  module ScoutApm
12
10
  module Instruments
13
11
  class MiddlewareDetailed
@@ -1,3 +1,3 @@
1
1
  module ScoutApm
2
- VERSION = "3.0.0.pre8"
2
+ VERSION = "3.0.0.pre9"
3
3
  end
data/lib/scout_apm.rb CHANGED
@@ -26,6 +26,7 @@ require 'rusage'
26
26
  #####################################
27
27
  require 'scout_apm/version'
28
28
 
29
+ require 'scout_apm/debug'
29
30
  require 'scout_apm/tracked_request'
30
31
  require 'scout_apm/layer'
31
32
  require 'scout_apm/limited_layer'
@@ -76,7 +77,7 @@ require 'scout_apm/instruments/active_record'
76
77
  require 'scout_apm/instruments/action_controller_rails_2'
77
78
  require 'scout_apm/instruments/action_controller_rails_3_rails4'
78
79
  require 'scout_apm/instruments/middleware_summary'
79
- # require 'scout_apm/instruments/middleware_detailed' # Currently disabled functionality, see the file for details.
80
+ require 'scout_apm/instruments/middleware_detailed' # Disabled by default, see the file for more details
80
81
  require 'scout_apm/instruments/rails_router'
81
82
  require 'scout_apm/instruments/grape'
82
83
  require 'scout_apm/instruments/sinatra'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scout_apm
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0.pre8
4
+ version: 3.0.0.pre9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derek Haynes
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-12-29 00:00:00.000000000 Z
12
+ date: 2017-02-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
@@ -148,6 +148,7 @@ files:
148
148
  - lib/scout_apm/call_set.rb
149
149
  - lib/scout_apm/config.rb
150
150
  - lib/scout_apm/context.rb
151
+ - lib/scout_apm/debug.rb
151
152
  - lib/scout_apm/environment.rb
152
153
  - lib/scout_apm/fake_store.rb
153
154
  - lib/scout_apm/framework_integrations/rails_2.rb
@@ -292,34 +293,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
292
293
  version: 1.3.1
293
294
  requirements: []
294
295
  rubyforge_project: scout_apm
295
- rubygems_version: 2.5.2
296
+ rubygems_version: 2.2.2
296
297
  signing_key:
297
298
  specification_version: 4
298
299
  summary: Ruby application performance monitoring
299
- test_files:
300
- - test/data/config_test_1.yml
301
- - test/test_helper.rb
302
- - test/unit/agent_test.rb
303
- - test/unit/background_job_integrations/sidekiq_test.rb
304
- - test/unit/config_test.rb
305
- - test/unit/context_test.rb
306
- - test/unit/environment_test.rb
307
- - test/unit/git_revision_test.rb
308
- - test/unit/histogram_test.rb
309
- - test/unit/ignored_uris_test.rb
310
- - test/unit/instruments/active_record_instruments_test.rb
311
- - test/unit/instruments/net_http_test.rb
312
- - test/unit/instruments/percentile_sampler_test.rb
313
- - test/unit/layaway_test.rb
314
- - test/unit/layer_children_set_test.rb
315
- - test/unit/limited_layer_test.rb
316
- - test/unit/metric_set_test.rb
317
- - test/unit/scored_item_set_test.rb
318
- - test/unit/serializers/payload_serializer_test.rb
319
- - test/unit/slow_job_policy_test.rb
320
- - test/unit/slow_request_policy_test.rb
321
- - test/unit/sql_sanitizer_test.rb
322
- - test/unit/store_test.rb
323
- - test/unit/utils/active_record_metric_name_test.rb
324
- - test/unit/utils/backtrace_parser_test.rb
325
- - test/unit/utils/numbers_test.rb
300
+ test_files: []