Class: ScoutRails::Agent
- Inherits:
-
Object
- Object
- ScoutRails::Agent
- Defined in:
- lib/scout_rails/agent.rb,
lib/scout_rails/agent/logging.rb,
lib/scout_rails/agent/reporting.rb
Overview
The agent gathers performance data from a Ruby application. One Agent instance is created per-Ruby process.
Each Agent object creates a worker thread (unless monitoring is disabled or we’re forking). The worker thread wakes up every Agent#period, merges in-memory metrics w/those saved to disk, saves the merged data to disk, and sends it to the Scout server.
Defined Under Namespace
Constant Summary collapse
- HTTP_HEADERS =
Headers passed up with all API requests.
{ "Agent-Hostname" => Socket.gethostname }
- @@instance =
see self.instance
nil
Instance Attribute Summary collapse
-
#config ⇒ Object
Returns the value of attribute config.
-
#environment ⇒ Object
Returns the value of attribute environment.
-
#layaway ⇒ Object
Returns the value of attribute layaway.
-
#log_file ⇒ Object
path to the log file.
-
#logger ⇒ Object
Returns the value of attribute logger.
-
#metric_lookup ⇒ Object
Hash used to lookup metric ids based on their name and scope.
-
#options ⇒ Object
options passed to the agent when
#start
is called. -
#store ⇒ Object
Accessors below are for associated classes.
Class Method Summary collapse
-
.instance(options = {}) ⇒ Object
All access to the agent is thru this class method to ensure multiple Agent instances are not initialized per-Ruby process.
Instance Method Summary collapse
- #gem_root ⇒ Object
-
#handle_exit ⇒ Object
at_exit, calls Agent#shutdown to wrapup metric reporting.
-
#initialize(options = {}) ⇒ Agent
constructor
Note - this doesn’t start instruments or the worker thread.
- #install_passenger_events ⇒ Object
- #install_rainbows_worker_loop ⇒ Object
- #install_unicorn_worker_loop ⇒ Object
-
#load_instruments ⇒ Object
Loads the instrumention logic.
-
#run_samplers ⇒ Object
Called from #process_metrics, which is run via the background worker.
-
#shutdown ⇒ Object
Called via an at_exit handler, it (1) stops the background worker and (2) runs it a final time.
-
#start(options = {}) ⇒ Object
This is called via
ScoutRails::Agent.instance.start
when ScoutRails is required in a Ruby application. -
#start_background_worker ⇒ Object
Creates the worker thread.
-
#start_background_worker? ⇒ Boolean
The worker thread will automatically start UNLESS: * A supported application server isn’t detected (example: running via Rails console) * A supported application server is detected, but it forks (Passenger).
-
#start_instruments ⇒ Object
Injects instruments into the Ruby application.
- #started? ⇒ Boolean
Methods included from Reporting
#add_metric_ids, #checkin_uri, #http, #post, #process_metrics, #request
Methods included from Logging
#apply_log_format, #init_logger, #log_level, #log_path
Constructor Details
#initialize(options = {}) ⇒ Agent
Note - this doesn’t start instruments or the worker thread. This is handled via #start
as we don’t want to start the worker thread or install instrumentation if (1) disabled for this environment (2) a worker thread shouldn’t be started (when forking).
32 33 34 35 36 37 38 39 40 41 |
# File 'lib/scout_rails/agent.rb', line 32 def initialize( = {}) @started = false @options ||= @store = ScoutRails::Store.new @layaway = ScoutRails::Layaway.new @config = ScoutRails::Config.new([:config_path]) @metric_lookup = Hash.new @process_cpu=ScoutRails::Instruments::Process::ProcessCpu.new(environment.processors) @process_memory=ScoutRails::Instruments::Process::ProcessMemory.new end |
Instance Attribute Details
#config ⇒ Object
Returns the value of attribute config.
16 17 18 |
# File 'lib/scout_rails/agent.rb', line 16 def config @config end |
#environment ⇒ Object
Returns the value of attribute environment.
17 18 19 |
# File 'lib/scout_rails/agent.rb', line 17 def environment @environment end |
#layaway ⇒ Object
Returns the value of attribute layaway.
15 16 17 |
# File 'lib/scout_rails/agent.rb', line 15 def layaway @layaway end |
#log_file ⇒ Object
path to the log file
20 21 22 |
# File 'lib/scout_rails/agent.rb', line 20 def log_file @log_file end |
#logger ⇒ Object
Returns the value of attribute logger.
19 20 21 |
# File 'lib/scout_rails/agent.rb', line 19 def logger @logger end |
#metric_lookup ⇒ Object
Hash used to lookup metric ids based on their name and scope
22 23 24 |
# File 'lib/scout_rails/agent.rb', line 22 def metric_lookup @metric_lookup end |
#options ⇒ Object
options passed to the agent when #start
is called.
21 22 23 |
# File 'lib/scout_rails/agent.rb', line 21 def @options end |
#store ⇒ Object
Accessors below are for associated classes
14 15 16 |
# File 'lib/scout_rails/agent.rb', line 14 def store @store end |
Class Method Details
.instance(options = {}) ⇒ Object
All access to the agent is thru this class method to ensure multiple Agent instances are not initialized per-Ruby process.
25 26 27 |
# File 'lib/scout_rails/agent.rb', line 25 def self.instance( = {}) @@instance ||= self.new() end |
Instance Method Details
#gem_root ⇒ Object
111 112 113 |
# File 'lib/scout_rails/agent.rb', line 111 def gem_root File.(File.join("..","..",".."), __FILE__) end |
#handle_exit ⇒ Object
at_exit, calls Agent#shutdown to wrapup metric reporting.
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/scout_rails/agent.rb', line 79 def handle_exit if environment.sinatra? || environment.jruby? || environment.rubinius? logger.debug "Exit handler not supported" else at_exit do logger.debug "Shutdown!" # MRI 1.9 bug drops exit codes. # http://bugs.ruby-lang.org/issues/5218 if environment.ruby_19? status = $!.status if $!.is_a?(SystemExit) shutdown exit status if status else shutdown end end # at_exit end end |
#install_passenger_events ⇒ Object
123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/scout_rails/agent.rb', line 123 def install_passenger_events PhusionPassenger.on_event(:starting_worker_process) do |forked| logger.debug "Passenger is starting a worker process. Starting worker thread." self.class.instance.start_background_worker end # The agent's at_exit hook doesn't run when a Passenger process stops. # This does run when a process stops. PhusionPassenger.on_event(:stopping_worker_process) do logger.debug "Passenger is stopping a worker process, shutting down the agent." ScoutRails::Agent.instance.shutdown end end |
#install_rainbows_worker_loop ⇒ Object
147 148 149 150 151 152 153 154 155 156 |
# File 'lib/scout_rails/agent.rb', line 147 def install_rainbows_worker_loop logger.debug "Installing Rainbows worker loop." Rainbows::HttpServer.class_eval do old = instance_method(:worker_loop) define_method(:worker_loop) do |worker| ScoutRails::Agent.instance.start_background_worker old.bind(self).call(worker) end end end |
#install_unicorn_worker_loop ⇒ Object
136 137 138 139 140 141 142 143 144 145 |
# File 'lib/scout_rails/agent.rb', line 136 def install_unicorn_worker_loop logger.debug "Installing Unicorn worker loop." Unicorn::HttpServer.class_eval do old = instance_method(:worker_loop) define_method(:worker_loop) do |worker| ScoutRails::Agent.instance.start_background_worker old.bind(self).call(worker) end end end |
#load_instruments ⇒ Object
Loads the instrumention logic.
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/scout_rails/agent.rb', line 193 def load_instruments case environment.framework when :rails require File.(File.join(File.dirname(__FILE__),'instruments/rails/action_controller_instruments.rb')) when :rails3_or_4 require File.(File.join(File.dirname(__FILE__),'instruments/rails3_or_4/action_controller_instruments.rb')) end require File.(File.join(File.dirname(__FILE__),'instruments/active_record_instruments.rb')) require File.(File.join(File.dirname(__FILE__),'instruments/net_http.rb')) require File.(File.join(File.dirname(__FILE__),'instruments/moped_instruments.rb')) require File.(File.join(File.dirname(__FILE__),'instruments/mongoid_instruments.rb')) rescue logger.warn "Exception loading instruments:" logger.warn $!. logger.warn $!.backtrace end |
#run_samplers ⇒ Object
Called from #process_metrics, which is run via the background worker.
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/scout_rails/agent.rb', line 170 def run_samplers begin cpu_util=@process_cpu.run # returns a hash logger.debug "Process CPU: #{cpu_util.inspect} [#{environment.processors} CPU(s)]" store.track!("CPU/Utilization",cpu_util,:scope => nil) if cpu_util rescue => e logger.info "Error reading ProcessCpu" logger.debug e. logger.debug e.backtrace.join("\n") end begin mem_usage=@process_memory.run # returns a single number, in MB logger.debug "Process Memory: #{mem_usage}MB" store.track!("Memory/Physical",mem_usage,:scope => nil) if mem_usage rescue => e logger.info "Error reading ProcessMemory" logger.debug e. logger.debug e.backtrace.join("\n") end end |
#shutdown ⇒ Object
Called via an at_exit handler, it (1) stops the background worker and (2) runs it a final time. The final run ensures metrics are stored locally to the layaway / reported to scoutapp.com. Otherwise, in-memory metrics would be lost and a gap would appear on restarts.
101 102 103 104 105 |
# File 'lib/scout_rails/agent.rb', line 101 def shutdown return if !started? @background_worker.stop @background_worker.run_once end |
#start(options = {}) ⇒ Object
This is called via ScoutRails::Agent.instance.start
when ScoutRails is required in a Ruby application. It initializes the agent and starts the worker thread (if appropiate).
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/scout_rails/agent.rb', line 49 def start( = {}) @options.merge!() init_logger logger.info "Attempting to start Scout Agent [#{ScoutRails::VERSION}] on [#{Socket.gethostname}]" if !config.settings['monitor'] and !@options[:force] logger.warn "Monitoring isn't enabled for the [#{environment.env}] environment." return false elsif !environment.app_server logger.warn "Couldn't find a supported app server. Not starting agent." return false elsif started? logger.warn "Already started agent." return false end @started = true logger.info "Starting monitoring. Framework [#{environment.framework}] App Server [#{environment.app_server}]." start_instruments if !start_background_worker? logger.debug "Not starting worker thread" install_passenger_events if environment.app_server == :passenger install_unicorn_worker_loop if environment.app_server == :unicorn install_rainbows_worker_loop if environment.app_server == :rainbows return end start_background_worker handle_exit logger.info "Scout Agent [#{ScoutRails::VERSION}] Initialized" end |
#start_background_worker ⇒ Object
Creates the worker thread. The worker thread is a loop that runs continuously. It sleeps for Agent#period and when it wakes, processes data, either saving it to disk or reporting to Scout.
160 161 162 163 164 165 166 167 |
# File 'lib/scout_rails/agent.rb', line 160 def start_background_worker logger.debug "Creating worker thread." @background_worker = ScoutRails::BackgroundWorker.new @background_worker_thread = Thread.new do @background_worker.start { process_metrics } end # thread new logger.debug "Done creating worker thread." end |
#start_background_worker? ⇒ Boolean
The worker thread will automatically start UNLESS:
-
A supported application server isn’t detected (example: running via Rails console)
-
A supported application server is detected, but it forks (Passenger). In this case, the agent is started in the forked process.
119 120 121 |
# File 'lib/scout_rails/agent.rb', line 119 def start_background_worker? !environment.forking? or environment.app_server == :thin end |
#start_instruments ⇒ Object
Injects instruments into the Ruby application.
211 212 213 214 |
# File 'lib/scout_rails/agent.rb', line 211 def start_instruments logger.debug "Installing instrumentation" load_instruments end |
#started? ⇒ Boolean
107 108 109 |
# File 'lib/scout_rails/agent.rb', line 107 def started? @started end |