Module: NewRelic::Agent::Instrumentation::ControllerInstrumentation::ClassMethods

Included in:
NewRelic::Agent::Instrumentation::ControllerInstrumentation
Defined in:
lib/new_relic/agent/instrumentation/controller_instrumentation.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.add_transaction_tracer(method, options = {}) ⇒ Object

Add transaction tracing to the given method. This will treat the given method as a main entrypoint for instrumentation, just like controller actions are treated by default. Useful especially for background tasks.

Example for background job:

class Job
  include NewRelic::Agent::Instrumentation::ControllerInstrumentation
  def run(task)
     ...
  end
  # Instrument run so tasks show up under task.name.  Note single
  # quoting to defer eval to runtime.
  add_transaction_tracer :run, :name => '#{args[0].name}'
end

Here’s an example of a controller that uses a dispatcher action to invoke operations which you want treated as top level actions, so they aren’t all lumped into the invoker action.

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']
    send op
  end
  # Ignore the invoker to avoid double counting
  newrelic_ignore :only => 'invoke_operation'
  # Instrument the operations:
  add_transaction_tracer :print
  add_transaction_tracer :show
  add_transaction_tracer :forward
end

Here’s an example of how to pass contextual information into the transaction so it will appear in transaction traces:

class Job
  include NewRelic::Agent::Instrumentation::ControllerInstrumentation
  def process(account)
     ...
  end
  # Include the account name in the transaction details.  Note the single
  # quotes to defer eval until call time.
  add_transaction_tracer :process, :params => '{ :account_name => args[0].name }'
end

See NewRelic::Agent::Instrumentation::ControllerInstrumentation#perform_action_with_newrelic_trace for the full list of available options.



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/new_relic/agent/instrumentation/controller_instrumentation.rb', line 168

def add_transaction_tracer(method, options = {})
  NewRelic::Agent.record_api_supportability_metric(:add_transaction_tracer)

  traced_method, punctuation = parse_punctuation(method)
  with_method_name, without_method_name = build_method_names(traced_method, punctuation)

  if already_added_transaction_tracer?(self, with_method_name)
    ::NewRelic::Agent.logger.warn("Transaction tracer already in place for class = #{self.name}, method = #{method.to_s}, skipping")
    return
  end

  # The metric path:
  options[:name] ||= method.to_s

  code_info = NewRelic::Agent::MethodTracerHelpers.code_information(self, method)
  argument_list = generate_argument_list(options.merge(code_info))

  class_eval(<<~EOC)
    def #{with_method_name}(*args, &block)
      perform_action_with_newrelic_trace(#{argument_list.join(',')}) do
        #{without_method_name}(*args, &block)
       end
    end
    ruby2_keywords(:#{with_method_name}) if respond_to?(:ruby2_keywords, true)
  EOC

  visibility = NewRelic::Helper.instance_method_visibility(self, method)

  alias_method(without_method_name, method.to_s)
  alias_method(method.to_s, with_method_name)
  send(visibility, method)
  send(visibility, with_method_name)
  ::NewRelic::Agent.logger.debug("Traced transaction: class = #{self.name}, method = #{method.to_s}, options = #{options.inspect}")
end

.newrelic_ignore(specifiers = {}) ⇒ Object

Have NewRelic ignore actions in this controller. Specify the actions as hash options using :except and :only. If no actions are specified, all actions are ignored.



60
61
62
63
# File 'lib/new_relic/agent/instrumentation/controller_instrumentation.rb', line 60

def newrelic_ignore(specifiers = {})
  NewRelic::Agent.record_api_supportability_metric(:newrelic_ignore)
  newrelic_ignore_aspect(NR_DO_NOT_TRACE_KEY, specifiers)
end

.newrelic_ignore_apdex(specifiers = {}) ⇒ Object

Have NewRelic omit apdex measurements on the given actions. Typically used for actions that are not user facing or that skew your overall apdex measurement. Accepts :except and :only options, as with #newrelic_ignore.



72
73
74
75
# File 'lib/new_relic/agent/instrumentation/controller_instrumentation.rb', line 72

def newrelic_ignore_apdex(specifiers = {})
  NewRelic::Agent.record_api_supportability_metric(:newrelic_ignore_apdex)
  newrelic_ignore_aspect(NR_IGNORE_APDEX_KEY, specifiers)
end

.newrelic_ignore_enduser(specifiers = {}) ⇒ Object



79
80
81
82
# File 'lib/new_relic/agent/instrumentation/controller_instrumentation.rb', line 79

def newrelic_ignore_enduser(specifiers = {})
  NewRelic::Agent.record_api_supportability_metric(:newrelic_ignore_enduser)
  newrelic_ignore_aspect(NR_IGNORE_ENDUSER_KEY, specifiers)
end

Instance Method Details

#already_added_transaction_tracer?(target, with_method_name) ⇒ Boolean

Returns:

  • (Boolean)


226
227
228
# File 'lib/new_relic/agent/instrumentation/controller_instrumentation.rb', line 226

def already_added_transaction_tracer?(target, with_method_name)
  NewRelic::Helper.instance_methods_include?(target, with_method_name)
end

#build_method_names(traced_method, punctuation) ⇒ Object



221
222
223
224
# File 'lib/new_relic/agent/instrumentation/controller_instrumentation.rb', line 221

def build_method_names(traced_method, punctuation)
  ["#{traced_method.to_s}_with_newrelic_transaction_trace#{punctuation}",
    "#{traced_method.to_s}_without_newrelic_transaction_trace#{punctuation}"]
end

#generate_argument_list(options) ⇒ Object



207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/new_relic/agent/instrumentation/controller_instrumentation.rb', line 207

def generate_argument_list(options)
  options.map do |key, value|
    value = if value.is_a?(Symbol)
      value.inspect
    elsif key == :params
      value.to_s
    else
      %Q("#{value.to_s}")
    end

    %Q(:#{key} => #{value})
  end
end

#newrelic_ignore_aspect(property, specifiers = {}) ⇒ Object

:nodoc:



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/new_relic/agent/instrumentation/controller_instrumentation.rb', line 84

def newrelic_ignore_aspect(property, specifiers = {}) # :nodoc:
  if specifiers.empty?
    self.newrelic_write_attr(property, true)
  elsif !(Hash === specifiers)
    ::NewRelic::Agent.logger.error("newrelic_#{property} takes an optional hash with :only and :except lists of actions (illegal argument type '#{specifiers.class}')")
  else
    # symbolize the incoming values
    specifiers = specifiers.inject({}) do |memo, (key, values)|
      if values.is_a?(Array)
        memo[key] = values.map(&:to_sym)
      else
        memo[key] = values.to_sym
      end
      memo
    end
    self.newrelic_write_attr(property, specifiers)
  end
end

#newrelic_read_attr(attr_name) ⇒ Object

:nodoc:



109
110
111
# File 'lib/new_relic/agent/instrumentation/controller_instrumentation.rb', line 109

def newrelic_read_attr(attr_name) # :nodoc:
  instance_variable_get(attr_name) if instance_variable_defined?(attr_name)
end

#newrelic_write_attr(attr_name, value) ⇒ Object

Should be monkey patched into the controller class implemented with the inheritable attribute mechanism.



105
106
107
# File 'lib/new_relic/agent/instrumentation/controller_instrumentation.rb', line 105

def newrelic_write_attr(attr_name, value) # :nodoc:
  instance_variable_set(attr_name, value)
end

#parse_punctuation(method) ⇒ Object



203
204
205
# File 'lib/new_relic/agent/instrumentation/controller_instrumentation.rb', line 203

def parse_punctuation(method)
  [method.to_s.sub(/([?!=])$/, ''), $1]
end