scout_apm 1.1.0.pre1 → 1.2.0.pre1

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.markdown +6 -0
  3. data/lib/scout_apm/agent/reporting.rb +67 -77
  4. data/lib/scout_apm/agent.rb +56 -9
  5. data/lib/scout_apm/background_job_integrations/delayed_job.rb +19 -0
  6. data/lib/scout_apm/background_job_integrations/sidekiq.rb +60 -0
  7. data/lib/scout_apm/bucket_name_splitter.rb +2 -2
  8. data/lib/scout_apm/capacity.rb +2 -1
  9. data/lib/scout_apm/context.rb +1 -5
  10. data/lib/scout_apm/environment.rb +16 -1
  11. data/lib/scout_apm/instruments/action_controller_rails_2.rb +13 -3
  12. data/lib/scout_apm/instruments/action_controller_rails_3.rb +20 -20
  13. data/lib/scout_apm/instruments/active_record.rb +5 -8
  14. data/lib/scout_apm/instruments/delayed_job.rb +56 -0
  15. data/lib/scout_apm/instruments/middleware.rb +44 -0
  16. data/lib/scout_apm/instruments/mongoid.rb +1 -1
  17. data/lib/scout_apm/instruments/moped.rb +2 -2
  18. data/lib/scout_apm/instruments/net_http.rb +1 -2
  19. data/lib/scout_apm/instruments/process/process_cpu.rb +5 -1
  20. data/lib/scout_apm/instruments/process/process_memory.rb +5 -1
  21. data/lib/scout_apm/instruments/sinatra.rb +14 -2
  22. data/lib/scout_apm/layaway.rb +33 -79
  23. data/lib/scout_apm/layaway_file.rb +2 -1
  24. data/lib/scout_apm/layer.rb +115 -0
  25. data/lib/scout_apm/layer_converter.rb +196 -0
  26. data/lib/scout_apm/metric_meta.rb +24 -4
  27. data/lib/scout_apm/metric_stats.rb +14 -4
  28. data/lib/scout_apm/request_manager.rb +26 -0
  29. data/lib/scout_apm/request_queue_time.rb +54 -0
  30. data/lib/scout_apm/serializers/payload_serializer.rb +8 -1
  31. data/lib/scout_apm/serializers/payload_serializer_to_json.rb +1 -0
  32. data/lib/scout_apm/slow_transaction.rb +3 -0
  33. data/lib/scout_apm/store.rb +122 -190
  34. data/lib/scout_apm/tracer.rb +54 -83
  35. data/lib/scout_apm/tracked_request.rb +168 -0
  36. data/lib/scout_apm/version.rb +1 -1
  37. data/lib/scout_apm.rb +18 -5
  38. metadata +11 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6241efa65950ed1d898ba3d688c0d0e442ed4871
4
- data.tar.gz: 166976b4a15c5e4481bb171abba5dcf2f155e70f
3
+ metadata.gz: 908063c42f604e0073f59f50d2fab04966553a20
4
+ data.tar.gz: 0bc50d1533419d27d62ca3692472d08e764536f7
5
5
  SHA512:
6
- metadata.gz: d0f7893d4df03ff5216bac6334ffa04e7e0bfa0e50e9eb14be6cb8a3d8e58b40afe140ed0691b3e324b311349119917f686f152e885dfbebbe20376c0d66c571
7
- data.tar.gz: 4f6c3c86c84349989dfb406711a35791bd7d9f550b9139d67432fd7e37f512a9d879cfd7f08bbca6f8c30d9eb730fe8c469e7b7790138d87f02d0a25ce697993
6
+ metadata.gz: f72beb94361832f4cd64faf3e618a83bef3989232ee68deb16472508ec82ef58b2cfea0973f95225d57b68c995a0724ded878acfa424c0c30b90f4b2d3cb63c0
7
+ data.tar.gz: 4dca8b6565dac5f29abe8deafa353da9512a5508981098e096b7cafb47585391cd77f8cb58a1f5c8a7e04250ff339e82d9c34d726af91d195b17bd2b11325ea0
data/CHANGELOG.markdown CHANGED
@@ -1,3 +1,9 @@
1
+ # 1.2.0
2
+
3
+ * Middleware tracing - Track time in the Rack middleware that Rails sets up
4
+ * Queue Time tracking - Track how much time is spent in the load balancer
5
+ * Major refactor of internals to allow more flexibility for future features
6
+
1
7
  # 1.0.0
2
8
 
3
9
  * General Availability
@@ -6,76 +6,80 @@ module ScoutApm
6
6
  @reporter ||= ScoutApm::Reporter.new(:checkin, config, logger)
7
7
  end
8
8
 
9
- # Called in the worker thread. Merges in-memory metrics w/those on disk and reports metrics
10
- # to the server.
9
+ # The data moves through a treadmill of reporting, coordinating several Rails processes by using an external file.
10
+ # * During the minute it is being recorded by the instruments, it gets
11
+ # recorded into the ram of each process (in the Store class).
12
+ # * The minute after, each process writes its own metrics to a shared LayawayFile
13
+ # * The minute after that, the first process to wake up pushes the combined
14
+ # data to the server, and wipes it. Next processes don't have anything to do.
15
+ #
16
+ # At any given point, there is data in each of those steps, moving its way through the process
11
17
  def process_metrics
12
- logger.debug "Processing metrics"
13
- run_samplers
14
- capacity.process
15
- payload = layaway.deposit_and_deliver
18
+ # First we write the previous minute's data to the shared-across-process layaway file.
19
+ store.write_to_layaway(layaway)
16
20
 
17
- metrics = payload[:metrics]
18
- slow_transactions = payload[:slow_transactions]
19
-
20
- if payload.any?
21
- add_metric_ids(metrics)
22
-
23
- logger.warn "Metric Size is at Limit, truncating" if metrics.size == ScoutApm::Store::MAX_SIZE
24
-
25
- # for debugging, count the total number of requests
26
- total_request_count = 0
27
-
28
- metrics.each do |meta,stats|
29
- if meta.metric_name =~ /\AController/
30
- total_request_count += stats.call_count
31
- end
32
- end
33
-
34
- metadata = {
35
- :app_root => ScoutApm::Environment.instance.root.to_s,
36
- :unique_id => ScoutApm::Utils::UniqueId.simple,
37
- :agent_version => ScoutApm::VERSION,
38
- }
39
-
40
- logger.debug("Metrics: #{metrics}")
41
- logger.debug("SlowTrans: #{slow_transactions}")
42
- logger.debug("Metadata: #{metadata.inspect}")
43
-
44
-
45
- payload = ScoutApm::Serializers::PayloadSerializer.serialize(metadata, metrics, slow_transactions)
46
- slow_transactions_kb = Marshal.dump(slow_transactions).size/1024 # just for performance debugging
47
-
48
- logger.info "Delivering #{metrics.length} Metrics for #{total_request_count} requests and #{slow_transactions.length} Slow Transaction Traces"
21
+ # Then attempt to send 2 minutes ago's data up to the server. This
22
+ # only acctually occurs if this process is the first to wake up this
23
+ # minute.
24
+ report_to_server
25
+ end
49
26
 
50
- logger.debug "Total payload [#{payload.size/1024} KB] for #{total_request_count} requests and Slow Transactions [#{slow_transactions_kb} KB] for #{slow_transactions.size} transactions of durations: #{slow_transactions.map(&:total_call_time).join(',')}."
27
+ # In a running app, one process will get one period ready for delivery, the others will see 0.
28
+ def report_to_server
29
+ reporting_periods = layaway.periods_ready_for_delivery
30
+ reporting_periods.each do |rp|
31
+ deliver_period(rp)
32
+ end
33
+ end
51
34
 
52
- if ScoutApm::Agent.instance.config.value("report_format") == 'json'
53
- headers = {'Content-Type' => 'application/json'}
54
- else
55
- headers = {}
56
- end
35
+ def deliver_period(reporting_period)
36
+ metrics = reporting_period.metrics_payload
37
+ slow_transactions = reporting_period.slow_transactions_payload
38
+ metadata = {
39
+ :app_root => ScoutApm::Environment.instance.root.to_s,
40
+ :unique_id => ScoutApm::Utils::UniqueId.simple,
41
+ :agent_version => ScoutApm::VERSION,
42
+ :agent_time => reporting_period.timestamp.to_s,
43
+ }
44
+
45
+ log_deliver(metrics, slow_transactions, metadata)
46
+
47
+ payload = ScoutApm::Serializers::PayloadSerializer.serialize(metadata, metrics, slow_transactions)
48
+ response = reporter.report(payload, headers)
49
+ unless response && response.is_a?(Net::HTTPSuccess)
50
+ logger.warn "Error on checkin to #{reporter.uri.to_s}: #{response.inspect}"
51
+ end
52
+ rescue => e
53
+ logger.warn "Error on checkin to #{reporter.uri.to_s}"
54
+ logger.info e.message
55
+ logger.debug e.backtrace
56
+ end
57
57
 
58
- response = reporter.report(payload, headers)
58
+ def log_deliver(metrics, slow_transactions, metadata)
59
+ total_request_count = metrics.
60
+ select { |meta,stats| meta.metric_name =~ /\AController/ }.
61
+ inject(0) {|sum, (_, stat)| sum + stat.call_count }
59
62
 
60
- if response and response.is_a?(Net::HTTPSuccess)
61
- directives = ScoutApm::Serializers::DirectiveSerializer.deserialize(response.body)
63
+ logger.info "Delivering #{metrics.length} Metrics for #{total_request_count} requests and #{slow_transactions.length} Slow Transaction Traces"
64
+ logger.debug("Metrics: #{metrics.pretty_inspect}\nSlowTrans: #{slow_transactions.pretty_inspect}\nMetadata: #{metadata.inspect.pretty_inspect}")
65
+ end
62
66
 
63
- self.metric_lookup.merge!(directives[:metric_lookup])
64
- if directives[:reset]
65
- logger.debug "Resetting metric_lookup."
66
- self.metric_lookup = Hash.new
67
- end
68
- logger.debug "Metric Cache Size: #{metric_lookup.size}"
69
- elsif response
70
- logger.warn "Error on checkin to #{reporter.uri.to_s}: #{response.inspect}"
71
- end
67
+ # TODO: Move this into PayloadSerializer?
68
+ def headers
69
+ if ScoutApm::Agent.instance.config.value("report_format") == 'json'
70
+ headers = {'Content-Type' => 'application/json'}
71
+ else
72
+ headers = {}
72
73
  end
73
- rescue
74
- logger.warn "Error on checkin to #{reporter.uri.to_s}"
75
- logger.info $!.message
76
- logger.debug $!.backtrace
77
74
  end
78
75
 
76
+ # def process_metrics
77
+ # rescue
78
+ # logger.warn "Error on checkin to #{reporter.uri.to_s}"
79
+ # logger.info $!.message
80
+ # logger.debug $!.backtrace
81
+ # end
82
+
79
83
  # Before reporting, lookup metric_id for each MetricMeta. This speeds up
80
84
  # reporting on the server-side.
81
85
  def add_metric_ids(metrics)
@@ -85,21 +89,7 @@ module ScoutApm
85
89
  end
86
90
  end
87
91
  end
88
-
89
- # Called from #process_metrics, which is run via the background worker.
90
- def run_samplers
91
- @samplers.each do |sampler|
92
- begin
93
- result = sampler.run
94
- store.track!(sampler.metric_name, result, {:scope => nil}) if result
95
- rescue => e
96
- logger.info "Error reading #{sampler.human_name}"
97
- logger.debug e.message
98
- logger.debug e.backtrace.join("\n")
99
- end
100
- end
101
- end
102
- end # module Reporting
92
+ end
103
93
  include Reporting
104
- end # class Agent
105
- end # module ScoutApm
94
+ end
95
+ end
@@ -58,8 +58,8 @@ module ScoutApm
58
58
  return false
59
59
  end
60
60
 
61
- if !environment.app_server_integration(true).found? && !options[:skip_app_server_check]
62
- logger.warn "Couldn't find a supported app server. Not starting agent."
61
+ if app_server_missing?(options) && background_job_missing?
62
+ logger.warn "Couldn't find a supported app server or background job framework. Not starting agent."
63
63
  return false
64
64
  end
65
65
 
@@ -90,9 +90,15 @@ module ScoutApm
90
90
 
91
91
  return false unless preconditions_met?(options)
92
92
 
93
+
93
94
  @started = true
94
95
 
95
- logger.info "Starting monitoring for [#{environment.application_name}]. Framework [#{environment.framework}] App Server [#{environment.app_server}]."
96
+ logger.info "Starting monitoring for [#{environment.application_name}]. Framework [#{environment.framework}] App Server [#{environment.app_server}] Background Job Framework [#{environment.background_job_name}]."
97
+
98
+ # We need agent initialized to do this, so do it here instead.
99
+ # Clean up any old data in the layaway file, allows us to change the file
100
+ # structure / contents without worrying.
101
+ layaway.verify_layaway_file_contents
96
102
 
97
103
  load_instruments if should_load_instruments?(options)
98
104
 
@@ -110,6 +116,9 @@ module ScoutApm
110
116
  start_background_worker
111
117
  handle_exit
112
118
  logger.info "Scout Agent [#{ScoutApm::VERSION}] Initialized"
119
+ elsif environment.background_job_integration
120
+ environment.background_job_integration.install
121
+ logger.info "Scout Agent [#{ScoutApm::VERSION}] loaded in [#{environment.background_job_name}] master process. Monitoring will start after background job framework forks its workers."
113
122
  else
114
123
  environment.app_server_integration.install
115
124
  logger.info "Scout Agent [#{ScoutApm::VERSION}] loaded in [#{environment.app_server}] master process. Monitoring will start after server forks its workers."
@@ -173,22 +182,38 @@ module ScoutApm
173
182
  logger.info "Initializing worker thread."
174
183
  @background_worker = ScoutApm::BackgroundWorker.new
175
184
  @background_worker_thread = Thread.new do
176
- @background_worker.start { process_metrics }
185
+ @background_worker.start {
186
+ # First, run periodic samplers. These should run once a minute,
187
+ # rather than per-request. "CPU Load" and similar.
188
+ run_samplers
189
+ capacity.process
190
+
191
+ ScoutApm::Agent.instance.process_metrics
192
+ }
177
193
  end
178
194
  end
179
195
 
180
196
  # If we want to skip the app_server_check, then we must load it.
181
197
  def should_load_instruments?(options={})
182
198
  return true if options[:skip_app_server_check]
183
- environment.app_server_integration.found?
199
+ environment.app_server_integration.found? || !background_job_missing?
184
200
  end
185
201
 
186
202
  # Loads the instrumention logic.
187
203
  def load_instruments
188
- case environment.framework
189
- when :rails then install_instrument(ScoutApm::Instruments::ActionControllerRails2)
190
- when :rails3_or_4 then install_instrument(ScoutApm::Instruments::ActionControllerRails3)
191
- when :sinatra then install_instrument(ScoutApm::Instruments::Sinatra)
204
+ if !background_job_missing?
205
+ case environment.background_job_name
206
+ when :delayed_job
207
+ install_instrument(ScoutApm::Instruments::DelayedJob)
208
+ end
209
+ else
210
+ case environment.framework
211
+ when :rails then install_instrument(ScoutApm::Instruments::ActionControllerRails2)
212
+ when :rails3_or_4 then
213
+ install_instrument(ScoutApm::Instruments::ActionControllerRails3)
214
+ install_instrument(ScoutApm::Instruments::Middleware)
215
+ when :sinatra then install_instrument(ScoutApm::Instruments::Sinatra)
216
+ end
192
217
  end
193
218
 
194
219
  install_instrument(ScoutApm::Instruments::ActiveRecord)
@@ -216,5 +241,27 @@ module ScoutApm
216
241
  def deploy_integration
217
242
  environment.deploy_integration
218
243
  end
244
+
245
+ # TODO: Extract a proper class / registery for these. They don't really belong here
246
+ def run_samplers
247
+ @samplers.each do |sampler|
248
+ begin
249
+ result = sampler.run
250
+ store.track_one!(sampler.metric_type, sampler.metric_name, result) if result
251
+ rescue => e
252
+ logger.info "Error reading #{sampler.human_name}"
253
+ logger.debug e.message
254
+ logger.debug e.backtrace.join("\n")
255
+ end
256
+ end
257
+ end
258
+
259
+ def app_server_missing?(options)
260
+ !environment.app_server_integration(true).found? && !options[:skip_app_server_check]
261
+ end
262
+
263
+ def background_job_missing?
264
+ environment.background_job_integration.nil?
265
+ end
219
266
  end
220
267
  end
@@ -0,0 +1,19 @@
1
+ module ScoutApm
2
+ module BackgroundJobIntegrations
3
+ class DelayedJob
4
+ attr_reader :logger
5
+
6
+ def name
7
+ :delayed_job
8
+ end
9
+
10
+ def present?
11
+ defined?(::Delayed::Job) && (File.basename($0) =~ /\Adelayed_job/)
12
+ end
13
+
14
+ def forking?
15
+ false
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,60 @@
1
+ module ScoutApm
2
+ module BackgroundJobIntegrations
3
+ class Sidekiq
4
+ attr_reader :logger
5
+
6
+ def name
7
+ :sidekiq
8
+ end
9
+
10
+ def present?
11
+ defined?(::Sidekiq) && (File.basename($0) =~ /\Asidekiq/)
12
+ end
13
+
14
+ def forking?
15
+ true
16
+ end
17
+
18
+ def install
19
+ # ScoutApm::Tracer is not available when this class is defined
20
+ SidekiqMiddleware.class_eval do
21
+ include ScoutApm::Tracer
22
+ end
23
+ ::Sidekiq.configure_server do |config|
24
+ config.server_middleware do |chain|
25
+ chain.add SidekiqMiddleware
26
+ end
27
+ end
28
+ require 'sidekiq/processor' # sidekiq v4 has not loaded this file by this point
29
+ ::Sidekiq::Processor.class_eval do
30
+ old = instance_method(:initialize)
31
+ define_method(:initialize) do |boss|
32
+ ScoutApm::Agent.instance.start_background_worker
33
+ old.bind(self).call(boss)
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ class SidekiqMiddleware
40
+ def call(worker, msg, queue)
41
+ msg_args = msg["args"].first
42
+ job_class = msg_args["job_class"]
43
+ latency = (Time.now.to_f - (msg['enqueued_at'] || msg['created_at'])) * 1000
44
+
45
+ ScoutApm::Agent.instance.store.track_one!("Queue", queue, 0, {:extra_metrics => {:latency => latency}})
46
+ req = ScoutApm::RequestManager.lookup
47
+ req.start_layer( ScoutApm::Layer.new("Job", job_class) )
48
+
49
+ begin
50
+ yield
51
+ rescue
52
+ req.error!
53
+ raise
54
+ ensure
55
+ req.stop_layer
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -13,8 +13,8 @@ module ScoutApm
13
13
  end
14
14
 
15
15
  private
16
- def split_metric_name(name)
17
- name.to_s.split(/\//, 2)
16
+ def split_metric_name(metric_name)
17
+ metric_name.to_s.split(/\//, 2)
18
18
  end
19
19
 
20
20
  def scope_hash
@@ -48,7 +48,8 @@ module ScoutApm
48
48
  window = 1.0 if window <= 0.0 # prevent divide-by-zero if clock adjusted.
49
49
  capacity = time_spent / window
50
50
  ScoutApm::Agent.instance.logger.debug "Instance/Capacity: #{capacity}"
51
- ScoutApm::Agent.instance.store.track!("Instance/Capacity",capacity,:scope => nil)
51
+ ScoutApm::Agent.instance.store.track_one!("Instance", "Capacity", capacity)
52
+
52
53
  @processing_start_time = process_time
53
54
  end
54
55
  end
@@ -17,11 +17,7 @@ module ScoutApm
17
17
  end
18
18
 
19
19
  def self.current
20
- Thread.current[:scout_context] ||= new
21
- end
22
-
23
- def self.clear!
24
- Thread.current[:scout_context] = nil
20
+ RequestManager.lookup.context
25
21
  end
26
22
 
27
23
  # Add context
@@ -23,6 +23,11 @@ module ScoutApm
23
23
  ScoutApm::ServerIntegrations::Null.new(STDOUT_LOGGER), # must be last
24
24
  ]
25
25
 
26
+ BACKGROUND_JOB_INTEGRATIONS = [
27
+ ScoutApm::BackgroundJobIntegrations::Sidekiq.new,
28
+ ScoutApm::BackgroundJobIntegrations::DelayedJob.new
29
+ ]
30
+
26
31
  FRAMEWORK_INTEGRATIONS = [
27
32
  ScoutApm::FrameworkIntegrations::Rails2.new,
28
33
  ScoutApm::FrameworkIntegrations::Rails3Or4.new,
@@ -120,7 +125,17 @@ module ScoutApm
120
125
  # If forking, don't start worker thread in the master process. Since it's
121
126
  # started as a Thread, it won't survive the fork.
122
127
  def forking?
123
- app_server_integration.forking?
128
+ app_server_integration.forking? || (background_job_integration && background_job_integration.forking?)
129
+ end
130
+
131
+ def background_job_integration
132
+ @background_job_integration ||= BACKGROUND_JOB_INTEGRATIONS.detect {|integration| integration.present?}
133
+ #### Temporary Disable
134
+ nil
135
+ end
136
+
137
+ def background_job_name
138
+ background_job_integration && background_job_integration.name
124
139
  end
125
140
 
126
141
  def deploy_integration
@@ -34,7 +34,7 @@ module ScoutApm
34
34
  ScoutApm::Agent.instance.logger.info "Instrumenting ActionView::Template"
35
35
  ::ActionView::Template.class_eval do
36
36
  include ::ScoutApm::Tracer
37
- instrument_method :render, :metric_name => 'View/#{path[%r{^(/.*/)?(.*)$},2]}/Rendering', :scope => true
37
+ instrument_method :render, :type => "View", :name => '#{path[%r{^(/.*/)?(.*)$},2]}/Rendering', :scope => true
38
38
  end
39
39
  end
40
40
 
@@ -57,9 +57,19 @@ module ScoutApm
57
57
  # applied to metrics recorded during this transaction. This lets us associate ActiveRecord calls with
58
58
  # specific controller actions.
59
59
  def perform_action_with_scout_instruments(*args, &block)
60
- scout_controller_action = "Controller/#{controller_path}/#{action_name}"
61
- self.class.scout_apm_trace(scout_controller_action, :uri => request.request_uri, :ip => request.remote_ip) do
60
+ req = ScoutApm::RequestManager.lookup
61
+ req.annotate_request(:uri => request.fullpath)
62
+ req.context.add_user(:ip => request.remote_ip)
63
+ req.set_headers(request.headers)
64
+ req.start_layer( ScoutApm::Layer.new("Controller", "#{controller_path}/#{action_name}") )
65
+
66
+ begin
62
67
  perform_action_without_scout_instruments(*args, &block)
68
+ rescue
69
+ req.error!
70
+ raise
71
+ ensure
72
+ req.stop_layer
63
73
  end
64
74
  end
65
75
  end
@@ -20,7 +20,7 @@ module ScoutApm
20
20
  if defined?(::ActionController) && defined?(::ActionController::Metal)
21
21
  ScoutApm::Agent.instance.logger.info "Instrumenting ActionController::Metal"
22
22
  ::ActionController::Metal.class_eval do
23
- include ScoutApm::Tracer
23
+ # include ScoutApm::Tracer
24
24
  include ScoutApm::Instruments::ActionControllerRails3Instruments
25
25
  end
26
26
  end
@@ -29,12 +29,15 @@ module ScoutApm
29
29
  ScoutApm::Agent.instance.logger.info "Instrumenting ActionView::PartialRenderer"
30
30
  ::ActionView::PartialRenderer.class_eval do
31
31
  include ScoutApm::Tracer
32
+
32
33
  instrument_method :render_partial,
33
- :metric_name => 'View/#{@template.virtual_path rescue "Unknown Partial"}/Rendering',
34
+ :type => "View",
35
+ :name => '#{@template.virtual_path rescue "Unknown Partial"}/Rendering',
34
36
  :scope => true
35
37
 
36
38
  instrument_method :collection_with_template,
37
- :metric_name => 'View/#{@template.virtual_path rescue "Unknown Collection"}/Rendering',
39
+ :type => "View",
40
+ :name => '#{@template.virtual_path rescue "Unknown Collection"}/Rendering',
38
41
  :scope => true
39
42
  end
40
43
 
@@ -42,7 +45,8 @@ module ScoutApm
42
45
  ::ActionView::TemplateRenderer.class_eval do
43
46
  include ScoutApm::Tracer
44
47
  instrument_method :render_template,
45
- :metric_name => 'View/#{args[0].virtual_path rescue "Unknown"}/Rendering',
48
+ :type => "View",
49
+ :name => '#{args[0].virtual_path rescue "Unknown"}/Rendering',
46
50
  :scope => true
47
51
  end
48
52
  end
@@ -50,24 +54,20 @@ module ScoutApm
50
54
  end
51
55
 
52
56
  module ActionControllerRails3Instruments
53
- # Instruments the action and tracks errors.
54
57
  def process_action(*args)
55
- scout_controller_action = "Controller/#{controller_path}/#{action_name}"
58
+ req = ScoutApm::RequestManager.lookup
59
+ req.annotate_request(:uri => request.fullpath)
60
+ req.context.add_user(:ip => request.remote_ip)
61
+ req.set_headers(request.headers)
56
62
 
57
- self.class.scout_apm_trace(scout_controller_action, :uri => request.fullpath, :ip => request.remote_ip) do
58
- Thread::current[:scout_apm_prof] = nil
59
- StackProf.start(:mode => :wall, :interval => ScoutApm::Agent.instance.config.value("stackprof_interval"))
60
-
61
- begin
62
- super
63
- rescue Exception
64
- ScoutApm::Agent.instance.store.track!("Errors/Request", 1, :scope => nil)
65
- raise
66
- ensure
67
- Thread::current[:scout_apm_scope_name] = nil
68
- StackProf.stop
69
- Thread::current[:scout_apm_prof] = StackProf.results
70
- end
63
+ req.start_layer( ScoutApm::Layer.new("Controller", "#{controller_path}/#{action_name}") )
64
+ begin
65
+ super
66
+ rescue
67
+ req.error!
68
+ raise
69
+ ensure
70
+ req.stop_layer
71
71
  end
72
72
  end
73
73
  end
@@ -58,12 +58,12 @@ module ScoutApm
58
58
 
59
59
  def log_with_scout_instruments(*args, &block)
60
60
  sql, name = args
61
- self.class.instrument(scout_ar_metric_name(sql,name), :desc => Utils::SqlSanitizer.new(sql).to_s) do
61
+ self.class.instrument("ActiveRecord", scout_ar_metric_name(sql,name), :desc => Utils::SqlSanitizer.new(sql).to_s ) do
62
62
  log_without_scout_instruments(sql, name, &block)
63
63
  end
64
64
  end
65
65
 
66
- def scout_ar_metric_name(sql,name)
66
+ def scout_ar_metric_name(sql, name)
67
67
  # sql: SELECT "places".* FROM "places" ORDER BY "places"."position" ASC
68
68
  # name: Place Load
69
69
  if name && (parts = name.split " ") && parts.size == 2
@@ -79,16 +79,13 @@ module ScoutApm
79
79
  operation
80
80
  end
81
81
  end
82
- metric = "ActiveRecord/#{model}/#{metric_name}" if metric_name
83
- metric = "ActiveRecord/SQL/other" if metric.nil?
82
+ metric = "#{model}/#{metric_name}" if metric_name
83
+ metric = "SQL/other" if metric.nil?
84
84
  else
85
- metric = "ActiveRecord/SQL/Unknown"
85
+ metric = "SQL/Unknown"
86
86
  end
87
87
  metric
88
88
  end
89
89
  end # module ActiveRecordInstruments
90
90
  end
91
91
  end
92
-
93
-
94
-
@@ -0,0 +1,56 @@
1
+ module ScoutApm
2
+ module Instruments
3
+ class DelayedJob
4
+ attr_reader :logger
5
+
6
+ def initialize(logger=ScoutApm::Agent.instance.logger)
7
+ @logger = logger
8
+ @installed = false
9
+ end
10
+
11
+ def installed?
12
+ @installed
13
+ end
14
+
15
+ def install
16
+ @installed = true
17
+ if defined?(::Delayed::Worker)
18
+ ::Delayed::Worker.class_eval do
19
+ include ScoutApm::Tracer
20
+ include ScoutApm::Instruments::DelayedJobInstruments
21
+ alias run_without_scout_instruments run
22
+ alias run run_with_scout_instruments
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ module DelayedJobInstruments
29
+ def run_with_scout_instruments(job)
30
+ scout_method_name = method_from_handler(job.handler)
31
+ queue = job.queue
32
+ latency = (Time.now.to_f - job.created_at.to_f) * 1000
33
+
34
+ ScoutApm::Agent.instance.store.track_one!("Queue", queue, 0, {:extra_metrics => {:latency => latency}})
35
+ req = ScoutApm::RequestManager.lookup
36
+ req.start_layer( ScoutApm::Layer.new("Job", scout_method_name) )
37
+
38
+ begin
39
+ run_without_scout_instruments(job)
40
+ rescue
41
+ req.error!
42
+ raise
43
+ ensure
44
+ req.stop_layer
45
+ end
46
+ end
47
+
48
+ def method_from_handler(handler)
49
+ job_handler = YAML.load(handler)
50
+ klass = job_handler.object.name
51
+ method = job_handler.method_name
52
+ "#{klass}##{method}"
53
+ end
54
+ end
55
+ end
56
+ end