lib/new_relic/agent/agent.rb in newrelic_rpm-3.6.6.147 vs lib/new_relic/agent/agent.rb in newrelic_rpm-3.6.7.152
- old
+ new
@@ -6,16 +6,18 @@
require 'net/https'
require 'net/http'
require 'logger'
require 'zlib'
require 'stringio'
+require 'new_relic/agent/sampled_buffer'
require 'new_relic/agent/autostart'
require 'new_relic/agent/new_relic_service'
require 'new_relic/agent/pipe_service'
require 'new_relic/agent/configuration/manager'
require 'new_relic/agent/database'
-require 'new_relic/agent/thread_profiler'
+require 'new_relic/agent/commands/agent_command_router'
+require 'new_relic/agent/commands/thread_profiler'
require 'new_relic/agent/event_listener'
require 'new_relic/agent/cross_app_monitor'
require 'new_relic/agent/request_sampler'
require 'new_relic/agent/sampler_collection'
require 'new_relic/environment_report'
@@ -29,17 +31,24 @@
# data to the NewRelic server.
class Agent
extend NewRelic::Agent::Configuration::Instance
def initialize
+ # FIXME: temporary work around for RUBY-839
+ # This should be handled with a configuration callback
+ if Agent.config[:monitor_mode]
+ @service = NewRelic::Agent::NewRelicService.new
+ end
+
@launch_time = Time.now
@events = NewRelic::Agent::EventListener.new
@stats_engine = NewRelic::Agent::StatsEngine.new
@transaction_sampler = NewRelic::Agent::TransactionSampler.new
@sql_sampler = NewRelic::Agent::SqlSampler.new
- @thread_profiler = NewRelic::Agent::ThreadProfiler.new
+ @thread_profiler = NewRelic::Agent::Commands::ThreadProfiler.new
+ @agent_command_router = NewRelic::Agent::Commands::AgentCommandRouter.new(@service, @thread_profiler)
@cross_app_monitor = NewRelic::Agent::CrossAppMonitor.new(@events)
@error_collector = NewRelic::Agent::ErrorCollector.new
@transaction_rules = NewRelic::Agent::RulesEngine.new
@metric_rules = NewRelic::Agent::RulesEngine.new
@request_sampler = NewRelic::Agent::RequestSampler.new(@events)
@@ -49,16 +58,10 @@
@connect_attempts = 0
@environment_report = nil
@last_harvest_time = Time.now
@obfuscator = lambda {|sql| NewRelic::Agent::Database.default_sql_obfuscator(sql) }
-
- # FIXME: temporary work around for RUBY-839
- # This should be handled with a configuration callback
- if Agent.config[:monitor_mode]
- @service = NewRelic::Agent::NewRelicService.new
- end
end
# contains all the class-level methods for NewRelic::Agent::Agent
module ClassMethods
# Should only be called by NewRelic::Control - returns a
@@ -79,10 +82,11 @@
# the transaction sampler that handles recording transactions
attr_reader :transaction_sampler
attr_reader :sql_sampler
# begins a thread profile session when instructed by agent commands
attr_reader :thread_profiler
+ attr_reader :agent_command_router
# error collector is a simple collection of recorded errors
attr_reader :error_collector
attr_reader :harvest_samplers
# whether we should record raw, obfuscated, or no sql
attr_reader :record_sql
@@ -264,41 +268,36 @@
# Sets a thread local variable as to whether we should or
# should not record sql in the current thread. Returns the
# previous value, if there is one
def set_record_sql(should_record)
- prev = Thread::current[:record_sql]
- Thread::current[:record_sql] = should_record
+ prev = TransactionState.get.record_sql
+ TransactionState.get.record_sql = should_record
prev.nil? || prev
end
# Sets a thread local variable as to whether we should or
# should not record transaction traces in the current
# thread. Returns the previous value, if there is one
def set_record_tt(should_record)
- prev = Thread::current[:record_tt]
- Thread::current[:record_tt] = should_record
+ prev = TransactionState.get.record_tt
+ TransactionState.get.record_tt = should_record
prev.nil? || prev
end
# Push flag indicating whether we should be tracing in this
# thread. This uses a stack which allows us to disable tracing
# children of a transaction without affecting the tracing of
# the whole transaction
def push_trace_execution_flag(should_trace=false)
- value = Thread.current[:newrelic_untraced]
- if (value.nil?)
- Thread.current[:newrelic_untraced] = []
- end
-
- Thread.current[:newrelic_untraced] << should_trace
+ TransactionState.get.push_traced(should_trace)
end
# Pop the current trace execution status. Restore trace execution status
# to what it was before we pushed the current flag.
def pop_trace_execution_flag
- Thread.current[:newrelic_untraced].pop if Thread.current[:newrelic_untraced]
+ TransactionState.get.pop_traced
end
# Herein lies the corpse of the former 'start' method. May
# its unmatched flog score rest in pieces.
module Start
@@ -333,12 +332,16 @@
# Logs the dispatcher to the log file to assist with
# debugging. When no debugger is present, logs this fact to
# assist with proper dispatcher detection
def log_dispatcher
dispatcher_name = Agent.config[:dispatcher].to_s
- return if log_if(dispatcher_name.empty?, :info, "No known dispatcher detected.")
- ::NewRelic::Agent.logger.info "Dispatcher: #{dispatcher_name}"
+
+ if dispatcher_name.empty?
+ ::NewRelic::Agent.logger.info 'No known dispatcher detected.'
+ else
+ ::NewRelic::Agent.logger.info "Dispatcher: #{dispatcher_name}"
+ end
end
def log_app_name
::NewRelic::Agent.logger.info "Application: #{Agent.config.app_names.join(", ")}"
end
@@ -398,39 +401,31 @@
# sure they're running a reasonable version
def log_version_and_pid
::NewRelic::Agent.logger.debug "New Relic Ruby Agent #{NewRelic::VERSION::STRING} Initialized: pid = #{$$}"
end
- # A helper method that logs a condition if that condition is
- # true. Mentally cleaner than having every method set a
- # local and log if it is true
- def log_if(boolean, level, message)
- ::NewRelic::Agent.logger.send(level, message) if boolean
- boolean
- end
-
- # A helper method that logs a condition unless that
- # condition is true. Mentally cleaner than having every
- # method set a local and log unless it is true
- def log_unless(boolean, level, message)
- ::NewRelic::Agent.logger.send(level, message) unless boolean
- boolean
- end
-
# Warn the user if they have configured their agent not to
# send data, that way we can see this clearly in the log file
def monitoring?
- log_unless(Agent.config[:monitor_mode], :warn,
- "Agent configured not to send data in this environment.")
+ if Agent.config[:monitor_mode]
+ true
+ else
+ ::NewRelic::Agent.logger.warn('Agent configured not to send data in this environment.')
+ false
+ end
end
# Tell the user when the license key is missing so they can
# fix it by adding it to the file
def has_license_key?
- log_unless(Agent.config[:license_key], :warn,
- "No license key found in newrelic.yml config. " +
- "This often means your newrelic.yml is missing a section for the running environment '#{NewRelic::Control.instance.env}'")
+ if Agent.config[:license_key] && Agent.config[:license_key].length > 0
+ true
+ else
+ ::NewRelic::Agent.logger.warn("No license key found in newrelic.yml config. " +
+ "This often means your newrelic.yml is missing a section for the running environment '#{NewRelic::Control.instance.env}'")
+ false
+ end
end
# A correct license key exists and is of the proper length
def has_correct_license_key?
has_license_key? && correct_license_length
@@ -438,19 +433,29 @@
# A license key is an arbitrary 40 character string,
# usually looks something like a SHA1 hash
def correct_license_length
key = Agent.config[:license_key]
- log_unless((key.length == 40), :error, "Invalid license key: #{key}")
+
+ if key.length == 40
+ true
+ else
+ ::NewRelic::Agent.logger.error("Invalid license key: #{key}")
+ false
+ end
end
# If we're using a dispatcher that forks before serving
# requests, we need to wait until the children are forked
# before connecting, otherwise the parent process sends odd data
def using_forking_dispatcher?
- log_if([:passenger, :rainbows, :unicorn].include?(Agent.config[:dispatcher]),
- :info, "Connecting workers after forking.")
+ if [:passenger, :rainbows, :unicorn].include? Agent.config[:dispatcher]
+ ::NewRelic::Agent.logger.info 'Connecting workers after forking.'
+ true
+ else
+ false
+ end
end
# Return true if we're using resque and it hasn't had a chance to (potentially)
# daemonize itself. This avoids hanging when there's a Thread started
# before Resque calls Process.daemon (Jira RUBY-857)
@@ -649,11 +654,11 @@
NewRelic::Agent.logger.info "Not starting Ruby Agent worker thread because :disable_harvest_thread is #{disable}"
return
end
::NewRelic::Agent.logger.debug "Creating Ruby Agent worker thread."
- @worker_thread = NewRelic::Agent::AgentThread.new('Worker Loop') do
+ @worker_thread = NewRelic::Agent::Threading::AgentThread.new('Worker Loop') do
deferred_work!(connection_options)
end
end
# A shorthand for NewRelic::Control.instance
@@ -975,25 +980,14 @@
# SQL. note that we explain only the sql statements whose
# segments' execution times exceed our threshold (to avoid
# unnecessary overhead of running explains on fast queries.)
def harvest_and_send_slowest_sample
harvest_transaction_traces
- unless @traces.empty?
- now = Time.now
- ::NewRelic::Agent.logger.debug "Sending (#{@traces.length}) transaction traces"
+ unless @traces.empty?
begin
- options = { :keep_backtraces => true }
- if !(NewRelic::Agent::Database.record_sql_method == :off)
- options[:record_sql] = NewRelic::Agent::Database.record_sql_method
- end
- if Agent.config[:'transaction_tracer.explain_enabled']
- options[:explain_sql] = Agent.config[:'transaction_tracer.explain_threshold']
- end
- traces = @traces.map {|trace| trace.prepare_to_send(options) }
- @service.transaction_sample_data(traces)
- ::NewRelic::Agent.logger.debug "Sent slowest sample (#{@service.agent_id}) in #{Time.now - now} seconds"
+ send_slowest_sample
rescue UnrecoverableServerException => e
::NewRelic::Agent.logger.debug e.message
end
end
@@ -1001,10 +995,29 @@
# the slowest sample around - it has been sent already and we
# can clear the collection and move on
@traces = nil
end
+ def send_slowest_sample
+ start_time = Time.now
+ ::NewRelic::Agent.logger.debug "Sending (#{@traces.length}) transaction traces"
+
+ options = { :keep_backtraces => true }
+ unless NewRelic::Agent::Database.record_sql_method == :off
+ options[:record_sql] = NewRelic::Agent::Database.record_sql_method
+ end
+
+ if Agent.config[:'transaction_tracer.explain_enabled']
+ options[:explain_sql] = Agent.config[:'transaction_tracer.explain_threshold']
+ end
+
+ traces = @traces.map {|trace| trace.prepare_to_send(options) }
+
+ @service.transaction_sample_data(traces)
+ ::NewRelic::Agent.logger.debug "Sent slowest sample (#{@service.agent_id}) in #{Time.now - start_time} seconds"
+ end
+
def harvest_and_send_thread_profile(disconnecting=false)
@thread_profiler.stop(true) if disconnecting
if @thread_profiler.finished?
profile = @thread_profiler.harvest
@@ -1046,23 +1059,14 @@
# Fetch samples from the RequestSampler and send them.
def harvest_and_send_analytic_event_data
samples = @request_sampler.samples
@service.analytic_event_data(samples) unless samples.empty?
@request_sampler.reset
- rescue => e
- NewRelic::Agent.logger.debug "Failed to sent analytics; throttling to conserve memory"
- @request_sampler.throttle
- raise
end
- def check_for_agent_commands
- commands = @service.get_agent_commands
- ::NewRelic::Agent.logger.debug "Received get_agent_commands = #{commands.inspect}"
-
- @thread_profiler.respond_to_commands(commands) do |command_id, error|
- @service.agent_command_results(command_id, error)
- end
+ def handle_agent_commands
+ @agent_command_router.handle_agent_commands
end
def transmit_data(disconnecting=false)
now = Time.now
::NewRelic::Agent.logger.debug "Sending data to New Relic Service"
@@ -1072,12 +1076,12 @@
harvest_and_send_errors
harvest_and_send_slowest_sample
harvest_and_send_slowest_sql
harvest_and_send_timeslice_data
harvest_and_send_analytic_event_data
- harvest_and_send_thread_profile(disconnecting)
- check_for_agent_commands
+ handle_agent_commands
+ harvest_and_send_thread_profile(disconnecting)
end
rescue EOFError => e
::NewRelic::Agent.logger.warn("EOFError after #{Time.now - now}s when transmitting data to New Relic Service.")
::NewRelic::Agent.logger.debug(e)
rescue => e