module NewRelic::Agent::Instrumentation::ControllerInstrumentation
NewRelic instrumentation for controller actions and tasks¶ ↑
This module can also be used to capture performance information for background tasks and other non-web transactions, including detailed transaction traces and traced errors.
For details on how to instrument background tasks see {ClassMethods#add_transaction_tracer} and {#perform_action_with_newrelic_trace}
@api public
Constants
- NR_DEFAULT_OPTIONS
- NR_DO_NOT_TRACE_KEY
- NR_IGNORE_APDEX_KEY
- NR_IGNORE_ENDUSER_KEY
Public Instance Methods
Yield to the given block with NewRelic tracing. Used by default instrumentation on controller actions in Rails and Merb. But it can also be used in custom instrumentation of controller methods and background tasks.
This is the method invoked by instrumentation added by the
ClassMethods#add_transaction_tracer
.
Here's a more verbose version of the example shown in
ClassMethods#add_transaction_tracer
using this method instead
of add_transaction_tracer.
Below is a controller with an invoke_operation
action which
dispatches to more specific operation methods based on a parameter (very
dangerous, btw!). With this instrumentation, the
invoke_operation
action is ignored but the operation methods
show up in New Relic as if they were first class controller actions
MyController < ActionController::Base include NewRelic::Agent::Instrumentation::ControllerInstrumentation # dispatch the given op to the method given by the service parameter. def invoke_operation op = params['operation'] perform_action_with_newrelic_trace(:name => op) do send op, params['message'] end end # Ignore the invoker to avoid double counting newrelic_ignore :only => 'invoke_operation' end
When invoking this method explicitly as in the example above, pass in a block to measure with some combination of options:
-
:category => :controller
indicates that this is a controller action and will appear with all the other actions. This is the default. -
:category => :task
indicates that this is a background task and will show up in New Relic with other background tasks instead of in the controllers list -
:category => :middleware
if you are instrumenting a rack middleware call. The:name
is optional, useful if you have more than one potential transaction in the call. -
:category => :uri
indicates that this is a web transaction whose name is a normalized URI, where 'normalized' means the URI does not have any elements with data in them such as in many REST URIs. -
:name => action_name
is used to specify the action name used as part of the metric name -
:params => {...}
to provide information about the context of the call, used in transaction trace display, for example::params => { :account => @account.name, :file => file.name }
These are treated similarly to request parameters in web transactions.
Seldomly used options:
-
:class_name => aClass.name
is used to override the name of the class when used inside the metric name. Default is the current class. -
:path => metric_path
is deprecated in the public API. It allows you to set the entire metric after the category part. Overrides all the other options. -
:request => Rack::Request#new(env)
is used to pass in a request object that may respond to path and referer.
@api public
# File lib/new_relic/agent/instrumentation/controller_instrumentation.rb, line 338 def perform_action_with_newrelic_trace(*args, &block) #THREAD_LOCAL_ACCESS state = NewRelic::Agent::TransactionState.tl_get state.request = newrelic_request(args) skip_tracing = do_not_trace? || !state.is_execution_traced? if skip_tracing state.current_transaction.ignore! if state.current_transaction NewRelic::Agent.disable_all_tracing { return yield } end # This method has traditionally taken a variable number of arguments, but the # only one that is expected / used is a single options hash. We are preserving # the *args method signature to ensure backwards compatibility. trace_options = args.last.is_a?(Hash) ? args.last : NR_DEFAULT_OPTIONS category = trace_options[:category] || :controller txn_options = create_transaction_options(trace_options, category, state) begin txn = Transaction.start(state, category, txn_options) begin yield rescue => e NewRelic::Agent.notice_error(e) raise end ensure if txn txn.ignore_apdex! if ignore_apdex? txn.ignore_enduser! if ignore_enduser? end Transaction.stop(state) end end
Protected Instance Methods
overrideable method to determine whether to trace an action or not - you may override this in your controller and supply your own logic for ignoring transactions.
# File lib/new_relic/agent/instrumentation/controller_instrumentation.rb, line 406 def do_not_trace? _is_filtered?(NR_DO_NOT_TRACE_KEY) end
overrideable method to determine whether to trace an action for purposes of apdex measurement - you can use this to ignore things like api calls or other fast non-user-facing actions
# File lib/new_relic/agent/instrumentation/controller_instrumentation.rb, line 414 def ignore_apdex? _is_filtered?(NR_IGNORE_APDEX_KEY) end
# File lib/new_relic/agent/instrumentation/controller_instrumentation.rb, line 418 def ignore_enduser? _is_filtered?(NR_IGNORE_ENDUSER_KEY) end
# File lib/new_relic/agent/instrumentation/controller_instrumentation.rb, line 378 def newrelic_request(args) opts = args.first # passed as a parameter to add_transaction_tracer if opts.respond_to?(:keys) && opts.respond_to?(:[]) && opts[:request] opts[:request] # in a Rails app elsif self.respond_to?(:request) self.request end end
# File lib/new_relic/agent/instrumentation/controller_instrumentation.rb, line 392 def newrelic_request_headers(state) request = state.request if request if request.respond_to?(:headers) request.headers elsif request.respond_to?(:env) request.env end end end
Should be implemented in the dispatcher class
# File lib/new_relic/agent/instrumentation/controller_instrumentation.rb, line 390 def newrelic_response_code; end
Private Instance Methods
Filter out a request if it matches one of our parameters for ignoring it - the key is either NR_DO_NOT_TRACE_KEY or NR_IGNORE_APDEX_KEY
# File lib/new_relic/agent/instrumentation/controller_instrumentation.rb, line 437 def _is_filtered?(key) name = if respond_to?(:action_name) action_name else :'[action_name_missing]' end NewRelic::Agent::Instrumentation::IgnoreActions.is_filtered?( key, self.class, name) end
# File lib/new_relic/agent/instrumentation/controller_instrumentation.rb, line 424 def create_transaction_options(trace_options, category, state) txn_options = {} txn_options[:request] = trace_options[:request] txn_options[:request] ||= request if respond_to?(:request) # params should have been filtered before calling perform_action_with_newrelic_trace txn_options[:filtered_params] = trace_options[:params] txn_options[:transaction_name] = TransactionNamer.name_for(nil, self, category, trace_options) txn_options[:apdex_start_time] = detect_queue_start_time(state) txn_options end
# File lib/new_relic/agent/instrumentation/controller_instrumentation.rb, line 450 def detect_queue_start_time(state) headers = newrelic_request_headers(state) QueueTime.parse_frontend_timestamp(headers) if headers end