Module: OneApm::Support::MethodTracer::ClassMethods::AddMethodTracer

Included in:
OneApm::Support::MethodTracer::ClassMethods
Defined in:
lib/one_apm/support/method_tracer.rb

Overview

contains methods refactored out of the #add_method_tracer method

Constant Summary collapse

ALLOWED_KEYS =
[:force, :metric, :push_scope, :code_header, :code_footer].freeze
DEPRECATED_KEYS =
[:force, :scoped_metric_only, :deduct_call_time_from_parent].freeze
OA_DEFAULT_SETTINGS =
{:push_scope => true, :metric => true, :code_header => "", :code_footer => "" }.freeze

Instance Method Summary collapse

Instance Method Details

#assemble_code_header(method_name, metric_name_code, options) ⇒ Object

Returns a code snippet to be eval’d that skips tracing when the agent is not tracing execution. turns instrumentation into effectively one method call overhead when the agent is disabled



174
175
176
177
178
# File 'lib/one_apm/support/method_tracer.rb', line 174

def assemble_code_header(method_name, metric_name_code, options)
  header = "return #{_untraced_method_name(method_name, metric_name_code)}(*args, &block) unless OneApm::Manager.tl_is_execution_traced?\n"
  header += options[:code_header].to_s
  header
end

#check_for_illegal_keys!(method_name, options) ⇒ Object

raises an error when the OneApm::Support::MethodTracer::ClassMethods#add_method_tracer method is called with improper keys. This aids in debugging new instrumentation by failing fast



103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/one_apm/support/method_tracer.rb', line 103

def check_for_illegal_keys!(method_name, options)
  unrecognized_keys = options.keys - ALLOWED_KEYS
  deprecated_keys   = options.keys & DEPRECATED_KEYS

  if unrecognized_keys.any?
    raise "Unrecognized options when adding method tracer to #{method_name}: " +
          unrecognized_keys.join(', ')
  end

  if deprecated_keys.any?
    OneApm::Manager.logger.warn("Deprecated options when adding method tracer to #{method_name}: "+
      deprecated_keys.join(', '))
  end
end

#check_for_push_scope_and_metric(options) ⇒ Object

validity checking - add_method_tracer must receive either push scope or metric, or else it would record no data. Raises an error if this is the case



121
122
123
124
125
# File 'lib/one_apm/support/method_tracer.rb', line 121

def check_for_push_scope_and_metric(options)
  unless options[:push_scope] || options[:metric]
    raise "Can't add a tracer where push_scope is false and metric is false"
  end
end

#code_to_eval(method_name, metric_name_code, options) ⇒ Object

Decides which code snippet we should be eval’ing in this context, based on the options.



213
214
215
216
217
218
219
220
# File 'lib/one_apm/support/method_tracer.rb', line 213

def code_to_eval(method_name, metric_name_code, options)
  options = validate_options(method_name, options)
  if options[:push_scope]
    method_with_push_scope(method_name, metric_name_code, options)
  else
    method_without_push_scope(method_name, metric_name_code, options)
  end
end

#default_metric_name_code(method_name) ⇒ Object

Default to the class where the method is defined.

Example:

Foo.default_metric_name_code('bar') #=> "Custom/#{Foo.name}/bar"


147
148
149
# File 'lib/one_apm/support/method_tracer.rb', line 147

def default_metric_name_code(method_name)
  "Custom/#{self.name}/#{method_name.to_s}"
end

#method_with_push_scope(method_name, metric_name_code, options) ⇒ Object

returns an eval-able string that contains the tracing code for a fully traced metric including scoping



199
200
201
202
203
204
205
206
207
208
209
# File 'lib/one_apm/support/method_tracer.rb', line 199

def method_with_push_scope(method_name, metric_name_code, options)
  "def #{_traced_method_name(method_name, metric_name_code)}(*args, &block)
    #{options[:code_header]}
    result = ::OneApm::Support::MethodTracer::Helpers.trace_execution_scoped(\"#{metric_name_code}\",
              :metric => #{options[:metric]}) do
      #{_untraced_method_name(method_name, metric_name_code)}(*args, &block)
    end
    #{options[:code_footer]}
    result
  end"
end

#method_without_push_scope(method_name, metric_name_code, options) ⇒ Object

returns an eval-able string that contains the traced method code used if the agent is not creating a scope for use in scoped metrics.



183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/one_apm/support/method_tracer.rb', line 183

def method_without_push_scope(method_name, metric_name_code, options)
  "def #{_traced_method_name(method_name, metric_name_code)}(*args, &block)
    #{assemble_code_header(method_name, metric_name_code, options)}
    t0 = Time.now
    begin
      #{_untraced_method_name(method_name, metric_name_code)}(*args, &block)\n
    ensure
      duration = (Time.now - t0).to_f
      OneApm::Manager.record_metric(\"#{metric_name_code}\", duration)
      #{options[:code_footer]}
    end
  end"
end

#oneapm_method_exists?(method_name) ⇒ Boolean

Checks to see if the method we are attempting to trace actually exists or not. #add_method_tracer can’t do anything if the method doesn’t exist.

Returns:

  • (Boolean)


154
155
156
157
158
# File 'lib/one_apm/support/method_tracer.rb', line 154

def oneapm_method_exists?(method_name)
  exists = method_defined?(method_name) || private_method_defined?(method_name)
  OneApm::Manager.logger.error("Did not trace #{self.name}##{method_name} because that method does not exist") unless exists
  exists
end

#traced_method_exists?(method_name, metric_name_code) ⇒ Boolean

Checks to see if we have already traced a method with a given metric by checking to see if the traced method exists. Warns the user if methods are being double-traced to help with debugging custom instrumentation.

Returns:

  • (Boolean)


164
165
166
167
168
# File 'lib/one_apm/support/method_tracer.rb', line 164

def traced_method_exists?(method_name, metric_name_code)
  exists = method_defined?(_traced_method_name(method_name, metric_name_code))
  OneApm::Manager.logger.error("Attempt to trace a method twice with the same metric: Method = #{method_name}, Metric Name = #{metric_name_code}") if exists
  exists
end

#validate_options(method_name, options) ⇒ Object

Checks the provided options to make sure that they make sense. Raises an error if the options are incorrect to assist with debugging, so that errors occur at class construction time rather than instrumentation run time



133
134
135
136
137
138
139
140
141
# File 'lib/one_apm/support/method_tracer.rb', line 133

def validate_options(method_name, options)
  unless options.is_a?(Hash)
    raise TypeError.new("Error adding method tracer to #{method_name}: provided options must be a Hash")
  end
  check_for_illegal_keys!(method_name, options)
  options = OA_DEFAULT_SETTINGS.merge(options)
  check_for_push_scope_and_metric(options)
  options
end