module NewRelic::Agent::MethodTracerHelpers
Constants
- MAX_ALLOWED_METRIC_DURATION
- SOURCE_CODE_INFORMATION_FAILURE_METRIC
- SOURCE_CODE_INFORMATION_PARAMETERS
-
These are code level metrics (CLM) attributes. For Ruby, they map like so:
filepath: full path to an .rb file on disk lineno: the line number a Ruby method is defined on within a given .rb file function: the name of the Ruby method namespace: the Ruby class' namespace as a string, ex: 'MyModule::MyClass'
Public Instance Methods
Source
# File lib/new_relic/agent/method_tracer_helpers.rb, line 42 def code_information(object, method_name) return ::NewRelic::EMPTY_HASH unless clm_enabled? && object && method_name @code_information ||= {} cache_key = "#{object.object_id}#{method_name}".freeze return @code_information[cache_key] if @code_information.key?(cache_key) info = namespace_and_location(object, method_name.to_sym) return ::NewRelic::EMPTY_HASH if info.empty? namespace, location, is_class_method = info @code_information[cache_key] = {filepath: location.first, lineno: location.last, function: "#{'self.' if is_class_method}#{method_name}", namespace: namespace}.freeze rescue StandardError => e ::NewRelic::Agent.logger.warn("Unable to determine source code info for '#{object}', " \ "method '#{method_name}' - #{e.class}: #{e.message}") ::NewRelic::Agent.increment_metric(SOURCE_CODE_INFORMATION_FAILURE_METRIC, 1) ::NewRelic::EMPTY_HASH end
Source
# File lib/new_relic/agent/method_tracer_helpers.rb, line 19 def trace_execution_scoped(metric_names, options = NewRelic::EMPTY_HASH) # THREAD_LOCAL_ACCESS return yield unless NewRelic::Agent::Tracer.state.is_execution_traced? metric_names = Array(metric_names) first_name = metric_names.shift return yield unless first_name segment = NewRelic::Agent::Tracer.start_segment( name: first_name, unscoped_metrics: metric_names ) segment.record_metrics = false if options[:metric] == false unless !options.key?(:code_information) || options[:code_information].nil? || options[:code_information].empty? segment.code_information = options[:code_information] end Tracer.capture_segment_error(segment) { yield } ensure ::NewRelic::Agent::Transaction::Segment.finish(segment) end
Private Instance Methods
Source
# File lib/new_relic/agent/method_tracer_helpers.rb, line 66 def clm_enabled? ::NewRelic::Agent.config[:'code_level_metrics.enabled'] end
Source
# File lib/new_relic/agent/method_tracer_helpers.rb, line 128 def controller_info(klass, name, is_class_method) path = Rails.root.join("app/controllers/#{klass.name.underscore}.rb") File.exist?(path) ? [name, [path.to_s, 1], is_class_method] : [] end
Source
# File lib/new_relic/agent/method_tracer_helpers.rb, line 121 def controller_without_method?(klass, method_name) defined?(Rails) && defined?(ApplicationController) && klass < ApplicationController && !klass.method_defined?(method_name) end
Rails controllers can be a special case because by default, controllers in Rails automatically render views with names that correspond to valid routes. This means that a controller method may not have a corresponding method in the controller class.
Source
# File lib/new_relic/agent/method_tracer_helpers.rb, line 73 def klass_name(object) name = Regexp.last_match(1) if object.to_s =~ /^#<Class:([\w:]+).*>$/ return name if name raise "Unable to glean a class name from string '#{object}'" end
The string representation of a singleton class looks like ‘#<Class:MyModule::MyClass>’, or ‘#<Class:MyModule::MyClass(id: integer, attribute: string)>’ Return the ‘MyModule::MyClass’ part of that string
Source
# File lib/new_relic/agent/method_tracer_helpers.rb, line 84 def klassify_singleton(object) Object.const_get(klass_name(object)) end
get at the underlying class from the singleton class
note: even with the regex hit from klass_name
(), ‘Object.const_get` is more performant than iterating through `ObjectSpace`
Source
# File lib/new_relic/agent/method_tracer_helpers.rb, line 102 def namespace_and_location(object, method_name) klass = object.singleton_class? ? klassify_singleton(object) : object name = klass.name || '(Anonymous)' is_class_method = false return controller_info(klass, name, is_class_method) if controller_without_method?(klass, method_name) method = if (klass.instance_methods + klass.private_instance_methods).include?(method_name) klass.instance_method(method_name) else is_class_method = true klass.method(method_name) end [name, method.source_location, is_class_method] end
determine the namespace (class name including all module names in scope) and source code location (file path and line number) for the given object and method name
traced class methods:
* object is a singleton class, `#<Class::MyClass>` * get at the underlying non-singleton class
traced instance methods and Rails controller methods:
* object is a class, `MyClass`
anonymous class based methods (‘c = Class.new { def method; end; }`:
* `#name` returns `nil`, so use '(Anonymous)' instead