0
@@ -4,6 +4,7 @@ require 'logger'
0
+require 'newrelic/version'
0
require 'newrelic/stats'
0
require 'newrelic/agent/worker_loop'
0
@@ -20,6 +21,8 @@ module NewRelic::Agent
0
# an exception that forces an agent to stop reporting until its mongrel is restarted
0
class ForceDisconnectException < Exception; end
0
+ class IgnoreSilentlyException < StandardError; end
0
# add some convenience methods for easy access to the Agent singleton.
0
# the following static methods all point to the same Agent instance:
0
@@ -57,8 +60,8 @@ module NewRelic::Agent
0
# environment - the name of the environment. used for logging only
0
# port - the name of this instance. shows up in the RPM UI screens. can be any String
0
- def manual_start(environment, port)
0
- agent.manual_start(environment, port)
0
+ def manual_start(environment, identifier)
0
+ agent.manual_start(environment, identifier)
0
# This method sets the block sent to this method as a sql obfuscator.
0
@@ -103,6 +106,25 @@ module NewRelic::Agent
0
agent.transaction_sampler.add_request_parameters(params)
0
+ # This method disables the recording of transaction traces in the given
0
+ def disable_transaction_tracing
0
+ state = agent.set_record_tt(false)
0
+ agent.set_record_tt(state)
0
+ # This method allows a filter to be applied to errors that RPM will track.
0
+ # The block should return the exception to track (which could be different from
0
+ # the original exception) or nil to ignore this exception
0
+ def ignore_error_filter(&block)
0
+ agent.error_collector.ignore_error_filter &block
0
@@ -124,6 +146,8 @@ module NewRelic::Agent
0
DEFAULT_HOST = 'localhost'
0
attr_reader :obfuscator
0
attr_reader :stats_engine
0
attr_reader :transaction_sampler
0
@@ -131,32 +155,50 @@ module NewRelic::Agent
0
attr_reader :worker_loop
0
attr_reader :license_key
0
attr_reader :remote_host
0
attr_reader :remote_port
0
attr_reader :record_sql
0
- attr_reader :
local_port0
+ attr_reader :
identifier0
+ # This method is deprecated. Use start.
0
+ def manual_start(environment, identifier)
0
+ start(environment, identifier, true)
0
# Start up the agent, which will connect to the newrelic server and start
0
# reporting performance information. Typically this is done from the
0
- # environment configuration file
0
+ # environment configuration file.
0
+ # environment identifies the host environment, like mongrel, thin, or take.
0
+ # identifier is an identifier which uniquely identifies the process hosting
0
+ # the agent. It should be ideally something like a server port, like 3000,
0
+ # a handler thread name, or a script name. It should not be a PID because
0
+ # from invocation to invocation. For something like rake, you could use
0
+ # Return false if the agent was not started
0
+ def start(environment, identifier, force=false)
0
+ @config ||= ::NR_CONFIG_FILE
0
log! "Agent Started Already!"
0
raise "Duplicate attempt to start the NewRelic agent"
0
- @local_port = determine_environment_and_port
0
- start_reporting if @local_port
0
+ @environment = environment
0
+ @identifier = identifier && identifier.to_s
0
+ start_reporting(force)
0
# this method makes sure that the agent is running. it's important
0
# for passenger where processes are forked and the agent is dormant
0
- return unless @prod_mode_enabled
0
+ return unless @prod_mode_enabled
&& !@invalid_license0
if @worker_thread.nil? || !@worker_thread.alive?
0
@stats_engine.spawn_sampler_thread
0
@@ -168,11 +210,22 @@ module NewRelic::Agent
0
+ if @environment == :passenger
0
+ log.warn "Phusion Passenger has been detected. Some RPM memory statistics may have inaccuracies due to short process lifespans"
0
@worker_loop = WorkerLoop.new(@log)
0
@license_key = config.fetch('license_key', nil)
0
+ ignore_errors = config.fetch('ignore_errors', "")
0
+ ignore_errors = ignore_errors.split(",")
0
+ ignore_errors.each { |error| error.strip! }
0
+ @error_collector.ignore(ignore_errors)
0
+ @capture_params = config.fetch('capture_params', false)
0
sampler_config = config.fetch('transaction_tracer', {})
0
@use_transaction_sampler = sampler_config.fetch('enabled', false)
0
@@ -181,7 +234,7 @@ module NewRelic::Agent
0
@explain_threshold = sampler_config.fetch('explain_threshold', '0.5').to_f
0
@explain_enabled = sampler_config.fetch('explain_enabled', true)
0
- log.info "Transaction trac
er enabled: #{@use_transaction_sampler}"0
+ log.info "Transaction trac
ing is enabled in the agent" if @use_transaction_sampler0
log.warn "Agent is configured to send raw SQL to RPM service" if @record_sql == :raw
0
@use_ssl = config.fetch('ssl', false)
0
@@ -197,6 +250,11 @@ module NewRelic::Agent
0
@prod_mode_enabled = force_enable || config['enabled']
0
+ # Initialize transaction sampler
0
+ @transaction_sampler.capture_params = @capture_params
0
+ @error_collector.capture_params = @capture_params
0
# make sure the license key exists and is likely to be really a license key
0
# by checking it's string length (license keys are 40 character strings.)
0
if @prod_mode_enabled && (!@license_key || @license_key.length != 40)
0
@@ -204,22 +262,45 @@ module NewRelic::Agent
0
- instrument_rails
if force_enable || ::RPM_TRACERS_ENABLED0
- install_at_exit_handler
0
+ # When the VM shuts down, attempt to send a message to the server that
0
+ # this agent run is stopping, assuming it has successfully connected
0
- def manual_start(environment, port)
0
- @environment = environment
0
+ # Attempt a graceful shutdown of the agent.
0
+ log.debug "Starting Agent shutdown"
0
+ # if litespeed, then ignore all future SIGUSR1 - it's litespeed trying to shut us down
0
+ if @environment == :litespeed
0
+ Signal.trap("SIGUSR1", "IGNORE")
0
+ Signal.trap("SIGTERM", "IGNORE")
0
+ # only call graceful_disconnect if we successfully stop the worker thread (since a transaction may be in flight)
0
+ if @worker_thread.join(30)
0
+ log.debug "ERROR - could not stop worker thread"
0
+ log.debug e.backtrace.join("\n")
0
@stats_engine.start_transaction
0
@@ -232,6 +313,13 @@ module NewRelic::Agent
0
+ def set_record_tt(should_record)
0
+ prev = Thread::current[:record_tt]
0
+ Thread::current[:record_tt] = should_record
0
def set_sql_obfuscator(type, &block)
0
@obfuscator = ChainedCall.new(block, @obfuscator)
0
@@ -244,32 +332,55 @@ module NewRelic::Agent
0
+ # Collect the Rails::Info into an associative array as well as the list of plugins
0
+ require 'builtin/rails_info/rails/info'
0
+ i += Rails::Info.properties
0
+ log.debug "Unable to get the Rails info: #{e.inspect}"
0
+ log.debug e.backtrace.join("\n")
0
+ # Would like to get this from config, but how?
0
+ plugins = Dir[File.join(File.expand_path(__FILE__+"/../../../../.."),"/*")].collect { |p| File.basename p }
0
+ i << ['Plugin List', plugins]
0
+ # Look for a capistrano file indicating the current revision:
0
+ rev_file = File.expand_path(File.join(RAILS_ROOT, "REVISION"))
0
+ if File.readable?(rev_file) && File.size(rev_file) < 64
0
+ File.open(rev_file) { | file | i << ['Revision', file.read] } rescue nil
0
@launch_time = Time.now
0
@environment = :unknown
0
@stats_engine = StatsEngine.new
0
@transaction_sampler = TransactionSampler.new(self)
0
@error_collector = ErrorCollector.new(self)
0
@request_timeout = 15 * 60
0
+ @invalid_license = false
0
- log_file = "#{RAILS_ROOT}/log/newrelic_agent.#{@local_port}.log"
0
- log_file = "#{RAILS_ROOT}/log/newrelic_agent.log"
0
+ log_path = ::RAILS_DEFAULT_LOGGER.instance_eval do
0
+ File.dirname(@log.path) rescue File.dirname(@logdev.filename)
0
+ end rescue "#{RAILS_ROOT}/log"
0
+ log_path = File.expand_path(log_path)
0
+ identifier_part = identifier && identifier[/[\.\w]*$/]
0
+ log_file = "#{RAILS_ROOT}/log/newrelic_agent.#{identifier_part ? identifier_part + "." : "" }log"
0
@log = Logger.new log_file
0
- @log.level = Logger::INFO
0
# change the format just for our logger
0
@@ -290,16 +401,16 @@ module NewRelic::Agent
0
log! "New Relic RPM Agent Initialized: pid = #{$$}"
0
-
to_stderr "Agent Log is found in #{log_file}"
0
+
log! "Agent Log is found in #{log_file}"
0
log.info "Runtime environment: #{@environment.to_s.titleize}"
0
def launch_worker_thread
0
- # we don't launch the worker_thread for passenger due to the way it spawns processes
0
- return if (@environment == :passenger && @initalized_pid == $$)
0
+ if (@environment == :passenger && $0 =~ /ApplicationSpawner/)
0
+ log.info "Process is passenger spawner - don't connect to RPM service"
0
@worker_thread = Thread.new do
0
@worker_thread_started = true
0
@@ -340,46 +451,15 @@ module NewRelic::Agent
0
- def install_at_exit_handler
0
- # When the VM shuts down, attempt to send a message to the server that
0
- # this agent run is stopping, assuming it has successfully connected
0
- log.debug "Starting Agent shutdown"
0
- # if litespeed, then ignore all future SIGUSR1 - it's litespeed trying to shut us down
0
- if @environment == :litespeed
0
- Signal.trap("SIGUSR1", "IGNORE")
0
- Signal.trap("SIGTERM", "IGNORE")
0
- # only call graceful_disconnect if we successfully stop the worker thread (since a transaction may be in flight)
0
- if @worker_thread.join(30)
0
- log.debug "ERROR - could not stop worker thread"
0
- log.debug e.backtrace.join("\n")
0
@connect_retry_period ||= 5
0
@connect_attempts ||= 0
0
# wait a few seconds for the web server to boot
0
sleep @connect_retry_period.to_i
0
@agent_id = invoke_remote :launch, @local_host,
0
- @
local_port, determine_home_directory, $$, @launch_time.to_f0
+ @
identifier, determine_home_directory, $$, @launch_time.to_f, NewRelic::VERSION::STRING, gather_info0
log! "Connected to NewRelic Service at #{@remote_host}:#{@remote_port}."
0
log.debug "Agent ID = #{@agent_id}."
0
@@ -390,7 +470,7 @@ module NewRelic::Agent
0
# Ask for mermission to collect error data
0
@should_send_errors = invoke_remote :should_collect_errors, @agent_id
0
- log! "Transaction trace
r enabled from RPM service: #{@should_send_samples}"0
+ log! "Transaction trace
s will be sent to the RPM service" if @use_transaction_sampler && @should_send_samples0
@last_harvest_time = Time.now
0
@@ -400,6 +480,7 @@ module NewRelic::Agent
0
log! "Visit NewRelic.com to obtain a valid license key, or to upgrade your account."
0
log! "Turning New Relic Agent off."
0
+ @invalid_license = true
0
rescue Timeout::Error, StandardError => e
0
@@ -437,85 +518,14 @@ module NewRelic::Agent
0
- # determine the environment we are running in (one of :webrick,
0
- # :mongrel, :thin, or :unknown) and if the process is listening
0
- # on a port, return the port # that we are listening on. When
0
- # this returns nil for the port, then the agent will not run.
0
- def determine_environment_and_port
0
- # Note: log won't be available yet.
0
- @environment = :unknown
0
- # OPTIONS is set by script/server
0
- port = OPTIONS.fetch :port, DEFAULT_PORT
0
- @environment = :webrick
0
- end # continue on if this didn't succeed...
0
- # this case covers starting by mongrel_rails
0
- if defined? Mongrel::HttpServer
0
- ObjectSpace.each_object(Mongrel::HttpServer) do |mongrel|
0
- @environment = :mongrel
0
- if defined? Thin::Server
0
- # This case covers the thin web server
0
- # Same issue as above- we assume only one instance per process
0
- ObjectSpace.each_object(Thin::Server) do |thin_server|
0
- backend = thin_server.backend
0
- # We need a way to uniquely identify and distinguish agents. The port
0
- # works for this. When using sockets, use the socket file name.
0
- if backend.respond_to? :port
0
- elsif backend.respond_to? :socket
0
- end # each thin instance
0
- if RUBY_PLATFORM =~ /java/
0
- # Check for JRuby environment. Not sure how this works in different appservers
0
- return java.lang.System.identityHashCode(JRuby.runtime)
0
- if caller.pop =~ /fcgi-bin\/RailsRunner\.rb/
0
- @environment = :litespeed
0
- if defined? Passenger::AbstractServer
0
- @environment = :passenger
0
- if config['monitor_daemons']
0
- @environment = :daemon
0
- # return the base part of the file name
0
- return File.basename($0).split(".").first
0
- # if no real environment was found
0
def determine_home_directory
0
File.expand_path(RAILS_ROOT)
0
- @instrumented ||= false
0
return if @instrumented
0
Module.method_tracer_log = log
0
@@ -604,19 +614,19 @@ module NewRelic::Agent
0
- def handle_messages(messages)
0
- messages.each do |message|
0
- message = Marshal.load(message)
0
- log.debug("Received Message: #{message.to_yaml}")
0
- log.error "Error handling message: #{e}"
0
- log.debug e.backtrace.join("\n")