scout_apm 1.1.0.pre1 → 1.2.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
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