class ScoutApm::Agent
The entry-point for the ScoutApm
Agent
.
Only one Agent
instance is created per-Ruby process, and it coordinates the lifecycle of the monitoring.
- initializes various data stores - coordinates configuration & logging - starts background threads, running periodically - installs shutdown hooks
Constants
- ERROR_SEND_FREQUENCY
seconds to batch error reports
Attributes
Public Class Methods
All access to the agent is thru this class method to ensure multiple Agent
instances are not initialized per-Ruby process.
# File lib/scout_apm/agent.rb, line 20 def self.instance(options = {}) @@instance ||= self.new(options) end
First call of the agent. Does very little so that the object can be created, and exist.
# File lib/scout_apm/agent.rb, line 25 def initialize(options = {}) @options = options @context = ScoutApm::AgentContext.new end
Public Instance Methods
# File lib/scout_apm/agent.rb, line 197 def background_worker_running? @background_worker_thread && @background_worker_thread.alive? && @background_worker && @background_worker.running? end
# File lib/scout_apm/agent.rb, line 217 def error_service_background_worker_running? @error_service_background_worker_thread && @error_service_background_worker_thread.alive? && @error_service_background_worker && @error_service_background_worker.running? end
If true, the agent will start regardless of safety checks.
# File lib/scout_apm/agent.rb, line 123 def force? @options[:force] end
Finishes setting up the instrumentation, configuration, and attempts to start the agent.
# File lib/scout_apm/agent.rb, line 35 def install(force=false) context.config = ScoutApm::Config.with_file(context, context.config.value("config_file")) logger.info "Scout Agent [#{ScoutApm::VERSION}] Initialized" if should_load_instruments? || force instrument_manager.install! install_background_job_integrations install_app_server_integration else logger.info "Not Loading Instruments" end logger.info "Scout Agent [#{ScoutApm::VERSION}] Installed" context.installed! @preconditions = ScoutApm::Agent::Preconditions.new if @preconditions.check?(context) || force start end end
This sets up the background worker thread to run at the correct time, either immediately, or after a fork into the actual unicorn/puma/etc worker
# File lib/scout_apm/agent.rb, line 117 def install_app_server_integration context.environment.app_server_integration.install logger.info "Installed Application Server Integration [#{context.environment.app_server}]." end
Attempts to install all background job integrations. This can come up if an app has both Resque and Sidekiq - we want both to be installed if possible, it's no harm to have the “wrong” one also installed while running.
# File lib/scout_apm/agent.rb, line 107 def install_background_job_integrations context.environment.background_job_integrations.each do |int| int.install logger.info "Installed Background Job Integration [#{int.name}]" end end
# File lib/scout_apm/agent.rb, line 92 def log_environment bg_names = context.environment.background_job_integrations.map{|bg| bg.name }.join(", ") logger.info( "Scout Agent [#{ScoutApm::VERSION}] starting for [#{context.environment.application_name}] " + "Framework [#{context.environment.framework}] " + "App Server [#{context.environment.app_server}] " + "Background Job Framework [#{bg_names}] " + "Hostname [#{context.environment.hostname}]" ) end
# File lib/scout_apm/agent.rb, line 30 def logger context.logger end
monitor is the key configuration here. If it is true, then we want the instruments. If it is false, we mostly don't want them, unless you're asking for devtrace (ie. not reporting to apm servers as a real app, but only for local browsers).
# File lib/scout_apm/agent.rb, line 140 def should_load_instruments? return true if context.config.value('dev_trace') context.config.value('monitor') end
Unconditionally starts the agent. This includes verifying instruments are installed, and starting the background worker.
The monitor precondition is checked explicitly, and we will never start with monitor = false
This does not attempt to start twice
# File lib/scout_apm/agent.rb, line 64 def start(opts={}) return unless context.config.value('monitor') if context.started? start_background_worker unless background_worker_running? start_error_service_background_worker unless error_service_background_worker_running? return end install unless context.installed? instrument_manager.install! if should_load_instruments? context.started! log_environment # Save it into a variable to prevent it from ever running twice @app_server_load ||= AppServerLoad.new(context).run start_background_worker start_error_service_background_worker end
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.
> true if thread & worker got started¶ ↑
> false if it wasn't started (either due to already running, or other preconditions)¶ ↑
# File lib/scout_apm/agent.rb, line 153 def start_background_worker(quiet=false) if !context.config.value('monitor') logger.debug "Not starting background worker as monitoring isn't enabled." unless quiet return false end if background_worker_running? logger.info "Not starting background worker, already started" unless quiet return false end if context.shutting_down? logger.info "Not starting background worker, already in process of shutting down" unless quiet return false end logger.info "Initializing worker thread." ScoutApm::Agent::ExitHandler.new(context).install periodic_work = ScoutApm::PeriodicWork.new(context) @background_worker = ScoutApm::BackgroundWorker.new(context) @background_worker_thread = Thread.new do @background_worker.start { periodic_work.run } end return true end
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. In this case, the agent is started in the forked process.
# File lib/scout_apm/agent.rb, line 131 def start_background_worker? return true if force? return !context.environment.forking? end
# File lib/scout_apm/agent.rb, line 206 def start_error_service_background_worker periodic_work = ScoutApm::ErrorService::PeriodicWork.new(context) @error_service_background_worker = ScoutApm::BackgroundWorker.new(context, ERROR_SEND_FREQUENCY) @error_service_background_worker_thread = Thread.new do @error_service_background_worker.start { periodic_work.run } end end
# File lib/scout_apm/agent.rb, line 185 def stop_background_worker if @background_worker logger.info("Stopping background worker") @background_worker.stop context.store.write_to_layaway(context.layaway, :force) if @background_worker_thread.alive? @background_worker_thread.wakeup @background_worker_thread.join end end end