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.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +6 -0
- data/lib/scout_apm/agent/reporting.rb +67 -77
- data/lib/scout_apm/agent.rb +56 -9
- data/lib/scout_apm/background_job_integrations/delayed_job.rb +19 -0
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +60 -0
- data/lib/scout_apm/bucket_name_splitter.rb +2 -2
- data/lib/scout_apm/capacity.rb +2 -1
- data/lib/scout_apm/context.rb +1 -5
- data/lib/scout_apm/environment.rb +16 -1
- data/lib/scout_apm/instruments/action_controller_rails_2.rb +13 -3
- data/lib/scout_apm/instruments/action_controller_rails_3.rb +20 -20
- data/lib/scout_apm/instruments/active_record.rb +5 -8
- data/lib/scout_apm/instruments/delayed_job.rb +56 -0
- data/lib/scout_apm/instruments/middleware.rb +44 -0
- data/lib/scout_apm/instruments/mongoid.rb +1 -1
- data/lib/scout_apm/instruments/moped.rb +2 -2
- data/lib/scout_apm/instruments/net_http.rb +1 -2
- data/lib/scout_apm/instruments/process/process_cpu.rb +5 -1
- data/lib/scout_apm/instruments/process/process_memory.rb +5 -1
- data/lib/scout_apm/instruments/sinatra.rb +14 -2
- data/lib/scout_apm/layaway.rb +33 -79
- data/lib/scout_apm/layaway_file.rb +2 -1
- data/lib/scout_apm/layer.rb +115 -0
- data/lib/scout_apm/layer_converter.rb +196 -0
- data/lib/scout_apm/metric_meta.rb +24 -4
- data/lib/scout_apm/metric_stats.rb +14 -4
- data/lib/scout_apm/request_manager.rb +26 -0
- data/lib/scout_apm/request_queue_time.rb +54 -0
- data/lib/scout_apm/serializers/payload_serializer.rb +8 -1
- data/lib/scout_apm/serializers/payload_serializer_to_json.rb +1 -0
- data/lib/scout_apm/slow_transaction.rb +3 -0
- data/lib/scout_apm/store.rb +122 -190
- data/lib/scout_apm/tracer.rb +54 -83
- data/lib/scout_apm/tracked_request.rb +168 -0
- data/lib/scout_apm/version.rb +1 -1
- data/lib/scout_apm.rb +18 -5
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 908063c42f604e0073f59f50d2fab04966553a20
|
4
|
+
data.tar.gz: 0bc50d1533419d27d62ca3692472d08e764536f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
#
|
10
|
-
#
|
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
|
-
|
13
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
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
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
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
|
-
|
61
|
-
|
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
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
105
|
-
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/scout_apm/agent.rb
CHANGED
@@ -58,8 +58,8 @@ module ScoutApm
|
|
58
58
|
return false
|
59
59
|
end
|
60
60
|
|
61
|
-
if
|
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 {
|
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
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
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
|
data/lib/scout_apm/capacity.rb
CHANGED
@@ -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.
|
51
|
+
ScoutApm::Agent.instance.store.track_one!("Instance", "Capacity", capacity)
|
52
|
+
|
52
53
|
@processing_start_time = process_time
|
53
54
|
end
|
54
55
|
end
|
data/lib/scout_apm/context.rb
CHANGED
@@ -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, :
|
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
|
-
|
61
|
-
|
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
|
-
:
|
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
|
-
:
|
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
|
-
:
|
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
|
-
|
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
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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 = "
|
83
|
-
metric = "
|
82
|
+
metric = "#{model}/#{metric_name}" if metric_name
|
83
|
+
metric = "SQL/other" if metric.nil?
|
84
84
|
else
|
85
|
-
metric = "
|
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
|