scout_rails_proxy_proxy 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.DS_Store +0 -0
- data/.gitignore +5 -0
- data/CHANGELOG.markdown +50 -0
- data/Gemfile +4 -0
- data/README.markdown +44 -0
- data/Rakefile +1 -0
- data/lib/scout_rails_proxy.rb +32 -0
- data/lib/scout_rails_proxy/agent.rb +319 -0
- data/lib/scout_rails_proxy/config.rb +34 -0
- data/lib/scout_rails_proxy/environment.rb +122 -0
- data/lib/scout_rails_proxy/instruments/active_record_instruments.rb +83 -0
- data/lib/scout_rails_proxy/instruments/net_http.rb +14 -0
- data/lib/scout_rails_proxy/instruments/process/process_cpu.rb +27 -0
- data/lib/scout_rails_proxy/instruments/process/process_memory.rb +40 -0
- data/lib/scout_rails_proxy/instruments/rails/action_controller_instruments.rb +46 -0
- data/lib/scout_rails_proxy/instruments/rails3/action_controller_instruments.rb +38 -0
- data/lib/scout_rails_proxy/instruments/sinatra_instruments.rb +33 -0
- data/lib/scout_rails_proxy/layaway.rb +76 -0
- data/lib/scout_rails_proxy/layaway_file.rb +70 -0
- data/lib/scout_rails_proxy/metric_meta.rb +34 -0
- data/lib/scout_rails_proxy/metric_stats.rb +49 -0
- data/lib/scout_rails_proxy/stack_item.rb +18 -0
- data/lib/scout_rails_proxy/store.rb +159 -0
- data/lib/scout_rails_proxy/tracer.rb +105 -0
- data/lib/scout_rails_proxy/transaction_sample.rb +10 -0
- data/lib/scout_rails_proxy/version.rb +3 -0
- data/scout_rails_proxy.gemspec +24 -0
- metadata +75 -0
data/.DS_Store
ADDED
Binary file
|
data/CHANGELOG.markdown
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# 1.0.3
|
2
|
+
|
3
|
+
* MetricMeta equality - downcase
|
4
|
+
* Suppressing "cat: /proc/cpuinfo: No such file or directory" error on distros that don't support it.
|
5
|
+
|
6
|
+
# 1.0.2
|
7
|
+
|
8
|
+
* Net::HTTP instrumentation
|
9
|
+
* ActionController::Metal instrumentation
|
10
|
+
* Determining number of processors for CPU % calculation
|
11
|
+
|
12
|
+
# 1.0.1
|
13
|
+
|
14
|
+
* Unicorn support (requires "preload_app true" in unicorn config file)
|
15
|
+
* Fix for Thin detection - ensure it's actually running
|
16
|
+
* Fixing name conflict btw Tracer#store and ActiveRecord::Store
|
17
|
+
|
18
|
+
# 1.0.0
|
19
|
+
|
20
|
+
* Release!
|
21
|
+
|
22
|
+
# 0.0.6.pre
|
23
|
+
|
24
|
+
* Rails 2 - Not collecting traces when an exception occurs
|
25
|
+
* Increased Transaction Sample Storage to 2 seconds from 1 second to decrease noise in UI
|
26
|
+
|
27
|
+
# 0.0.5
|
28
|
+
|
29
|
+
* Support for custom categories
|
30
|
+
* Not raising an exception w/an unbalanced stack
|
31
|
+
* Only allows controllers as the entry point for a transaction
|
32
|
+
|
33
|
+
# 0.0.4
|
34
|
+
|
35
|
+
* Transaction Sampling
|
36
|
+
|
37
|
+
# 0.0.3.pre
|
38
|
+
|
39
|
+
* Removed dynamic ActiveRecord caller instrumentation
|
40
|
+
* Fixed issue that prevents the app from loading if ActiveRecord isn't used.
|
41
|
+
* Using a metric hash for each request, then merging when complete. Ensures data associated w/requests that overlap a
|
42
|
+
minute boundary are correctly associated.
|
43
|
+
|
44
|
+
# 0.0.2
|
45
|
+
|
46
|
+
* Doesn't prevent app from loading if no configuration exists for the current environment.
|
47
|
+
|
48
|
+
# 0.0.1
|
49
|
+
|
50
|
+
* Boom! Initial Release.
|
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# ScoutRailsProxy
|
2
|
+
|
3
|
+
A Ruby gem for detailed Rails application performance analysis. Metrics are reported to [Scout](https://scoutapp.com), a hosted server and application monitoring service. For general server monitoring, see our [server monitoring agent](https://github.com/scoutapp/scout-client).
|
4
|
+
|
5
|
+
![Scout Rails Monitoring](https://img.skitch.com/20120714-frkr9i1pyjgn58uqrwqh55yfb8.jpg)
|
6
|
+
|
7
|
+
## Getting Started
|
8
|
+
|
9
|
+
Install the gem:
|
10
|
+
|
11
|
+
gem install scout_rails_proxy
|
12
|
+
|
13
|
+
Signup for a [Scout](https://scoutapp.com) account and copy the config file to `RAILS_ROOT/config/scout_rails_proxy.yml`.
|
14
|
+
|
15
|
+
Your config file should look like:
|
16
|
+
|
17
|
+
common: &defaults
|
18
|
+
name: YOUR_APPLICATION_NAME
|
19
|
+
key: YOUR_APPLICATION_KEY
|
20
|
+
monitor: true
|
21
|
+
|
22
|
+
production:
|
23
|
+
<<: *defaults
|
24
|
+
|
25
|
+
## Supported Frameworks
|
26
|
+
|
27
|
+
* Rails 2.2 and greater
|
28
|
+
|
29
|
+
## Supported Rubies
|
30
|
+
|
31
|
+
* Ruby 1.8.7
|
32
|
+
* Ruby 1.9.2
|
33
|
+
* Ruby 1.9.3
|
34
|
+
|
35
|
+
## Supported Application Servers
|
36
|
+
|
37
|
+
* Phusion Passenger
|
38
|
+
* Thin
|
39
|
+
* WEBrick
|
40
|
+
* Unicorn (make sure to add `preload_app true` to `config/unicorn.rb`)
|
41
|
+
|
42
|
+
## Help
|
43
|
+
|
44
|
+
See our [troubleshooting tips](https://scoutapp.com/info/support_app_monitoring) and/or email support@scoutapp.com if you need a hand.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ScoutRailsProxy
|
2
|
+
end
|
3
|
+
require 'socket'
|
4
|
+
require 'set'
|
5
|
+
require 'net/http'
|
6
|
+
require File.expand_path('../scout_rails_proxy/version.rb', __FILE__)
|
7
|
+
require File.expand_path('../scout_rails_proxy/agent.rb', __FILE__)
|
8
|
+
require File.expand_path('../scout_rails_proxy/layaway.rb', __FILE__)
|
9
|
+
require File.expand_path('../scout_rails_proxy/layaway_file.rb', __FILE__)
|
10
|
+
require File.expand_path('../scout_rails_proxy/config.rb', __FILE__)
|
11
|
+
require File.expand_path('../scout_rails_proxy/environment.rb', __FILE__)
|
12
|
+
require File.expand_path('../scout_rails_proxy/metric_meta.rb', __FILE__)
|
13
|
+
require File.expand_path('../scout_rails_proxy/metric_stats.rb', __FILE__)
|
14
|
+
require File.expand_path('../scout_rails_proxy/stack_item.rb', __FILE__)
|
15
|
+
require File.expand_path('../scout_rails_proxy/store.rb', __FILE__)
|
16
|
+
require File.expand_path('../scout_rails_proxy/tracer.rb', __FILE__)
|
17
|
+
require File.expand_path('../scout_rails_proxy/transaction_sample.rb', __FILE__)
|
18
|
+
require File.expand_path('../scout_rails_proxy/instruments/process/process_cpu.rb', __FILE__)
|
19
|
+
require File.expand_path('../scout_rails_proxy/instruments/process/process_memory.rb', __FILE__)
|
20
|
+
|
21
|
+
if defined?(Rails) and Rails.respond_to?(:version) and Rails.version =~ /^3/
|
22
|
+
module ScoutRailsProxy
|
23
|
+
class Railtie < Rails::Railtie
|
24
|
+
initializer "scout_rails_proxy.start" do |app|
|
25
|
+
ScoutRailsProxy::Agent.instance.start
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
else
|
30
|
+
ScoutRailsProxy::Agent.instance.start
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,319 @@
|
|
1
|
+
module ScoutRailsProxy
|
2
|
+
# The agent gathers performance data from a Ruby application. One Agent instance is created per-Ruby process.
|
3
|
+
#
|
4
|
+
# Each Agent object creates a worker thread (unless monitoring is disabled or we're forking).
|
5
|
+
# The worker thread wakes up every +Agent#period+, merges in-memory metrics w/those saved to disk,
|
6
|
+
# saves the merged data to disk, and sends it to the Scout server.
|
7
|
+
class Agent
|
8
|
+
# Headers passed up with all API requests.
|
9
|
+
HTTP_HEADERS = { "Agent-Hostname" => Socket.gethostname }
|
10
|
+
DEFAULT_HOST = 'scoutapp.com'
|
11
|
+
# see self.instance
|
12
|
+
@@instance = nil
|
13
|
+
|
14
|
+
# Accessors below are for associated classes
|
15
|
+
attr_accessor :store
|
16
|
+
attr_accessor :layaway
|
17
|
+
attr_accessor :config
|
18
|
+
attr_accessor :environment
|
19
|
+
|
20
|
+
attr_accessor :logger
|
21
|
+
attr_accessor :log_file # path to the log file
|
22
|
+
attr_accessor :options # options passed to the agent when +#start+ is called.
|
23
|
+
attr_accessor :metric_lookup # Hash used to lookup metric ids based on their name and scope
|
24
|
+
|
25
|
+
# All access to the agent is thru this class method to ensure multiple Agent instances are not initialized per-Ruby process.
|
26
|
+
def self.instance(options = {})
|
27
|
+
@@instance ||= self.new(options)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Note - this doesn't start instruments or the worker thread. This is handled via +#start+ as we don't
|
31
|
+
# want to start the worker thread or install instrumentation if (1) disabled for this environment (2) a worker thread shouldn't
|
32
|
+
# be started (when forking).
|
33
|
+
def initialize(options = {})
|
34
|
+
@started = false
|
35
|
+
@options ||= options
|
36
|
+
@store = ScoutRailsProxy::Store.new
|
37
|
+
@layaway = ScoutRailsProxy::Layaway.new
|
38
|
+
@config = ScoutRailsProxy::Config.new(options[:config_path])
|
39
|
+
@metric_lookup = Hash.new
|
40
|
+
@process_cpu=ScoutRailsProxy::Instruments::Process::ProcessCpu.new(environment.processors)
|
41
|
+
@process_memory=ScoutRailsProxy::Instruments::Process::ProcessMemory.new
|
42
|
+
end
|
43
|
+
|
44
|
+
def environment
|
45
|
+
@environment ||= ScoutRailsProxy::Environment.new
|
46
|
+
end
|
47
|
+
|
48
|
+
# This is called via +ScoutRailsProxy::Agent.instance.start+ when ScoutRailsProxy is required in a Ruby application.
|
49
|
+
# It initializes the agent and starts the worker thread (if appropiate).
|
50
|
+
def start(options = {})
|
51
|
+
@options.merge!(options)
|
52
|
+
init_logger
|
53
|
+
logger.info "Attempting to start Scout Agent [#{ScoutRailsProxy::VERSION}] on [#{Socket.gethostname}]"
|
54
|
+
if !config.settings['monitor'] and !@options[:force]
|
55
|
+
logger.warn "Monitoring isn't enabled for the [#{environment.env}] environment."
|
56
|
+
return false
|
57
|
+
elsif !environment.app_server
|
58
|
+
logger.warn "Couldn't find a supported app server. Not starting agent."
|
59
|
+
return false
|
60
|
+
elsif started?
|
61
|
+
logger.warn "Already started agent."
|
62
|
+
return false
|
63
|
+
end
|
64
|
+
@started = true
|
65
|
+
logger.info "Starting monitoring. Framework [#{environment.framework}] App Server [#{environment.app_server}]."
|
66
|
+
start_instruments
|
67
|
+
if !start_worker_thread?
|
68
|
+
logger.debug "Not starting worker thread"
|
69
|
+
install_passenger_worker_process_event if environment.app_server == :passenger
|
70
|
+
install_unicorn_worker_loop if environment.app_server == :unicorn
|
71
|
+
return
|
72
|
+
end
|
73
|
+
start_worker_thread
|
74
|
+
handle_exit
|
75
|
+
logger.info "Scout Agent [#{ScoutRailsProxy::VERSION}] Initialized"
|
76
|
+
end
|
77
|
+
|
78
|
+
# Placeholder: store metrics locally on exit so those in memory aren't lost. Need to decide
|
79
|
+
# whether we'll report these immediately or just store locally and risk having stale data.
|
80
|
+
def handle_exit
|
81
|
+
if environment.sinatra? || environment.jruby? || environment.rubinius?
|
82
|
+
logger.debug "Exit handler not supported"
|
83
|
+
else
|
84
|
+
at_exit { at_exit { logger.debug "Shutdown!" } }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def started?
|
89
|
+
@started
|
90
|
+
end
|
91
|
+
|
92
|
+
def gem_root
|
93
|
+
File.expand_path(File.join("..","..",".."), __FILE__)
|
94
|
+
end
|
95
|
+
|
96
|
+
def init_logger
|
97
|
+
@log_file = "#{log_path}/scout_rails_proxy.log"
|
98
|
+
@logger = Logger.new(@log_file)
|
99
|
+
@logger.level = Logger::DEBUG
|
100
|
+
def logger.format_message(severity, timestamp, progname, msg)
|
101
|
+
prefix = "[#{timestamp.strftime("%m/%d/%y %H:%M:%S %z")} #{Socket.gethostname} (#{$$})] #{severity} : #{msg}\n"
|
102
|
+
end
|
103
|
+
@logger
|
104
|
+
end
|
105
|
+
|
106
|
+
# The worker thread will automatically start UNLESS:
|
107
|
+
# * A supported application server isn't detected (example: running via Rails console)
|
108
|
+
# * A supported application server is detected, but it forks (Passenger). In this case,
|
109
|
+
# the agent is started in the forked process.
|
110
|
+
def start_worker_thread?
|
111
|
+
!environment.forking? or environment.app_server == :thin
|
112
|
+
end
|
113
|
+
|
114
|
+
def install_passenger_worker_process_event
|
115
|
+
PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
116
|
+
logger.debug "Passenger is starting a worker process. Starting worker thread."
|
117
|
+
self.class.instance.start_worker_thread
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def install_unicorn_worker_loop
|
122
|
+
logger.debug "Installing Unicorn worker loop."
|
123
|
+
Unicorn::HttpServer.class_eval do
|
124
|
+
old = instance_method(:worker_loop)
|
125
|
+
define_method(:worker_loop) do |worker|
|
126
|
+
ScoutRailsProxy::Agent.instance.start_worker_thread
|
127
|
+
old.bind(self).call(worker)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def log_path
|
133
|
+
"#{environment.root}/log"
|
134
|
+
end
|
135
|
+
|
136
|
+
# in seconds, time between when the worker thread wakes up and runs.
|
137
|
+
def period
|
138
|
+
60
|
139
|
+
end
|
140
|
+
|
141
|
+
# Creates the worker thread. The worker thread is a loop that runs continuously. It sleeps for +Agent#period+ and when it wakes,
|
142
|
+
# processes data, either saving it to disk or reporting to Scout.
|
143
|
+
def start_worker_thread
|
144
|
+
logger.debug "Creating worker thread."
|
145
|
+
@worker_thread = Thread.new do
|
146
|
+
begin
|
147
|
+
logger.debug "Starting worker thread, running every #{period} seconds"
|
148
|
+
next_time = Time.now + period
|
149
|
+
while true do
|
150
|
+
now = Time.now
|
151
|
+
while now < next_time
|
152
|
+
sleep_time = next_time - now
|
153
|
+
sleep(sleep_time) if sleep_time > 0
|
154
|
+
now = Time.now
|
155
|
+
end
|
156
|
+
process_metrics
|
157
|
+
while next_time <= now
|
158
|
+
next_time += period
|
159
|
+
end
|
160
|
+
end
|
161
|
+
rescue
|
162
|
+
logger.debug "Worker Thread Exception!!!!!!!"
|
163
|
+
logger.debug $!.message
|
164
|
+
logger.debug $!.backtrace
|
165
|
+
end
|
166
|
+
end # thread new
|
167
|
+
logger.debug "Done creating worker thread."
|
168
|
+
end
|
169
|
+
|
170
|
+
# Writes each payload to a file for auditing.
|
171
|
+
def write_to_file(object)
|
172
|
+
logger.debug "Writing to file"
|
173
|
+
full_path = Pathname.new(RAILS_ROOT+'/log/audit/scout')
|
174
|
+
( full_path +
|
175
|
+
"#{Time.now.strftime('%Y-%m-%d_%H:%M:%S')}.json" ).open("w") do |f|
|
176
|
+
f.puts object.to_json
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Before reporting, lookup metric_id for each MetricMeta. This speeds up
|
181
|
+
# reporting on the server-side.
|
182
|
+
def add_metric_ids(metrics)
|
183
|
+
metrics.each do |meta,stats|
|
184
|
+
if metric_id = metric_lookup[meta]
|
185
|
+
meta.metric_id = metric_id
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Called from #process_metrics, which is run via the worker thread.
|
191
|
+
def run_samplers
|
192
|
+
begin
|
193
|
+
cpu_util=@process_cpu.run # returns a hash
|
194
|
+
logger.debug "Process CPU: #{cpu_util.inspect} [#{environment.processors} CPU(s)]"
|
195
|
+
store.track!("CPU/Utilization",cpu_util) if cpu_util
|
196
|
+
rescue => e
|
197
|
+
logger.info "Error reading ProcessCpu"
|
198
|
+
logger.debug e.message
|
199
|
+
logger.debug e.backtrace.join("\n")
|
200
|
+
end
|
201
|
+
|
202
|
+
begin
|
203
|
+
mem_usage=@process_memory.run # returns a single number, in MB
|
204
|
+
logger.debug "Process Memory: #{mem_usage}MB"
|
205
|
+
store.track!("Memory/Physical",mem_usage) if mem_usage
|
206
|
+
rescue => e
|
207
|
+
logger.info "Error reading ProcessMemory"
|
208
|
+
logger.debug e.message
|
209
|
+
logger.debug e.backtrace.join("\n")
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Called in the worker thread. Merges in-memory metrics w/those on disk and reports metrics
|
214
|
+
# to the server.
|
215
|
+
def process_metrics
|
216
|
+
logger.debug "Processing metrics"
|
217
|
+
run_samplers
|
218
|
+
metrics = layaway.deposit_and_deliver
|
219
|
+
if metrics.any?
|
220
|
+
add_metric_ids(metrics)
|
221
|
+
# for debugging, count the total number of requests
|
222
|
+
controller_count = 0
|
223
|
+
metrics.each do |meta,stats|
|
224
|
+
if meta.metric_name =~ /\AController/
|
225
|
+
controller_count += stats.call_count
|
226
|
+
end
|
227
|
+
end
|
228
|
+
logger.debug "#{config.settings['name']} Delivering metrics for #{controller_count} requests."
|
229
|
+
response = post( checkin_uri,
|
230
|
+
Marshal.dump(:metrics => metrics, :sample => store.sample),
|
231
|
+
"Content-Type" => "application/json" )
|
232
|
+
if response and response.is_a?(Net::HTTPSuccess)
|
233
|
+
directives = Marshal.load(response.body)
|
234
|
+
self.metric_lookup.merge!(directives[:metric_lookup])
|
235
|
+
store.transaction_sample_lock.synchronize do
|
236
|
+
store.sample = nil
|
237
|
+
end
|
238
|
+
logger.debug "Metric Cache Size: #{metric_lookup.size}"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
rescue
|
242
|
+
logger.info "Error on checkin to #{checkin_uri.to_s}"
|
243
|
+
logger.info $!.message
|
244
|
+
logger.debug $!.backtrace
|
245
|
+
end
|
246
|
+
|
247
|
+
def checkin_uri
|
248
|
+
URI.parse("http://#{config.settings['host'] || DEFAULT_HOST}/app/#{config.settings['key']}/checkin.scout?name=#{CGI.escape(config.settings['name'])}")
|
249
|
+
end
|
250
|
+
|
251
|
+
def post(url, body, headers = Hash.new)
|
252
|
+
response = nil
|
253
|
+
request(url) do |connection|
|
254
|
+
post = Net::HTTP::Post.new( url.path +
|
255
|
+
(url.query ? ('?' + url.query) : ''),
|
256
|
+
HTTP_HEADERS.merge(headers) )
|
257
|
+
post.body = body
|
258
|
+
response=connection.request(post)
|
259
|
+
end
|
260
|
+
response
|
261
|
+
end
|
262
|
+
|
263
|
+
def request(url, &connector)
|
264
|
+
response = nil
|
265
|
+
|
266
|
+
http_class = if config.settings['proxy']
|
267
|
+
Net::HTTP::Proxy(
|
268
|
+
config.settings['proxy']['host'],
|
269
|
+
config.settings['proxy']['port'],
|
270
|
+
config.settings['proxy']['user'],
|
271
|
+
config.settings['proxy']['password']
|
272
|
+
)
|
273
|
+
else
|
274
|
+
Net::HTTP
|
275
|
+
end
|
276
|
+
|
277
|
+
http = http_class.new url.host, url.port
|
278
|
+
response = http.start(&connector)
|
279
|
+
logger.debug "got response: #{response.inspect}"
|
280
|
+
case response
|
281
|
+
when Net::HTTPSuccess, Net::HTTPNotModified
|
282
|
+
logger.debug "/checkin OK"
|
283
|
+
when Net::HTTPBadRequest
|
284
|
+
logger.warn "/checkin FAILED: The Account Key [#{config.settings['key']}] is invalid."
|
285
|
+
else
|
286
|
+
logger.debug "/checkin FAILED: #{response.inspect}"
|
287
|
+
end
|
288
|
+
rescue Exception
|
289
|
+
logger.debug "Exception sending request to server: #{$!.message}"
|
290
|
+
ensure
|
291
|
+
response
|
292
|
+
end
|
293
|
+
|
294
|
+
# Loads the instrumention logic.
|
295
|
+
def load_instruments
|
296
|
+
case environment.framework
|
297
|
+
when :rails
|
298
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'instruments/rails/action_controller_instruments.rb'))
|
299
|
+
when :rails3
|
300
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'instruments/rails3/action_controller_instruments.rb'))
|
301
|
+
when :sinatra
|
302
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'instruments/sinatra_instruments.rb'))
|
303
|
+
end
|
304
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'instruments/active_record_instruments.rb'))
|
305
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'instruments/net_http.rb'))
|
306
|
+
rescue
|
307
|
+
logger.warn "Exception loading instruments:"
|
308
|
+
logger.warn $!.message
|
309
|
+
logger.warn $!.backtrace
|
310
|
+
end
|
311
|
+
|
312
|
+
# Injects instruments into the Ruby application.
|
313
|
+
def start_instruments
|
314
|
+
logger.debug "Installing instrumentation"
|
315
|
+
load_instruments
|
316
|
+
end
|
317
|
+
|
318
|
+
end # class Agent
|
319
|
+
end # module ScoutRailsProxy
|