module StackifyRubyAPM::Spies

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

@api private

Public Class Methods

class_exists?(class_name) click to toggle source
# File lib/stackify_apm/spies.rb, line 87
def self.class_exists?(class_name)
  if class_name
    klass = Module.const_get(class_name)
    return klass.is_a?(Class)
  end
rescue NameError
  return false
end
hook_into(name) click to toggle source
# File lib/stackify_apm/spies.rb, line 58
def self.hook_into(name)
  return unless (registration = require_hooks[name])
  return unless safe_defined?(registration.const_name)

  installed[registration.const_name] = registration
  registration.install

  registration.require_paths.each do |path|
    require_hooks.delete path
  end
end
installed() click to toggle source
# File lib/stackify_apm/spies.rb, line 37
def self.installed
  @installed ||= {}
end
ismethod_exists(class_name, current_method) click to toggle source

rubocop:disable Metrics/CyclomaticComplexity rubocop:disable Metrics/PerceivedComplexity

# File lib/stackify_apm/spies.rb, line 98
def self.ismethod_exists(class_name, current_method)
  module_consget = Module.const_get(class_name)
  current_method_without_apm = "_without_apm_#{current_method}"
  self_current_method_without_apm = "_self_without_apm_#{current_method}"

  with_flag = module_consget.instance_methods(false).include?(:"#{current_method_without_apm}")
  self_with_flag = module_consget.instance_methods(false).include?(:"#{self_current_method_without_apm}")

  current_method_exists = module_consget.instance_methods(false).include?(:"#{current_method}")
  self_current_method_exists = module_consget.instance_methods(false).include?(:"self.#{current_method}")

  exists = false
  if (self_with_flag == false && with_flag == false) &&
     (current_method_exists == true || self_current_method_exists == true)
    exists = true
  end

  if (module_consget.methods.include?(:"#{current_method_without_apm}") == false &&
     module_consget.methods.include?(:"#{self_current_method_without_apm}") == false) &&
     (module_consget.methods.include?(:"#{current_method}") || module_consget.methods.include?(:"self.#{current_method}"))
    exists = true
  end
  exists
end
parse_json_config(file) click to toggle source
# File lib/stackify_apm/spies.rb, line 74
def self.parse_json_config(file)
  jsondata = {}
  if ENV['STACKIFY_RUBY_ENV'] == 'rspec'
    file = 'spec/integration/stackify_spec.json'
  end
  return unless File.exist?(file)

  File.open(file) do |f|
    jsondata = JSON.parse(f.read)
  end
  jsondata
end
register(*args) click to toggle source
# File lib/stackify_apm/spies.rb, line 41
def self.register(*args)
  registration = Registration.new(*args)

  if safe_defined?(registration.const_name)
    registration.install
    installed[registration.const_name] = registration
  else
    register_require_hook registration
  end
end
register_require_hook(registration) click to toggle source
# File lib/stackify_apm/spies.rb, line 52
def self.register_require_hook(registration)
  registration.require_paths.each do |path|
    require_hooks[path] = registration
  end
end
require_hooks() click to toggle source
# File lib/stackify_apm/spies.rb, line 33
def self.require_hooks
  @require_hooks ||= {}
end
run_custom_instrumentation() click to toggle source
# File lib/stackify_apm/spies/custom_instrumenter.rb, line 16
def self.run_custom_instrumentation
  config = Config.new
  to_instrument = parse_json_config(config.json_config_file)
  if config.logger.nil?
    config.debug_logger
    config.logger.send(:info, '[StackifyRubyAPM] Error: config/stackify.json does not exist which is required for custom instrumentation!') if to_instrument.nil?
  end

  return unless !to_instrument.nil? && !to_instrument.empty? && defined?(to_instrument['instrumentation']) && (to_instrument['instrumentation'].count > 0)

  to_instrument['instrumentation'].each do |custom_spy|
    current_class = defined?(custom_spy['class']) ? custom_spy['class'] : nil
    current_module = defined?(custom_spy['module']) ? custom_spy['module'] : nil
    current_method = custom_spy['method']
    tracked_func = custom_spy['trackedFunction']
    tracked_func_name = defined?(custom_spy['trackedFunctionName']) ? custom_spy['trackedFunctionName'] : ''
    transaction = defined?(custom_spy['transaction']) ? custom_spy['transaction'] : nil
    file_path = defined?(custom_spy['file_path']) ? custom_spy['file_path'] : nil

    if current_class
      tracked_function_tpl = tracked_func_name.nil? ? '{{ClassName}}.{{MethodName}}' : tracked_func_name
      tracked_function_name = tracked_function_tpl.to_s.sub '{{ClassName}}', current_class
      tracked_function_name = tracked_function_name.to_s.sub '{{MethodName}}', current_method
    elsif current_module
      tracked_function_tpl = tracked_func_name.nil? ? '{{ModuleName}}.{{MethodName}}' : tracked_func_name
      tracked_function_name = tracked_function_tpl.to_s.sub '{{ModuleName}}', current_module
      tracked_function_name = tracked_function_name.to_s.sub '{{MethodName}}', current_method
    end

    begin
      if current_module
        if file_path.nil?
          config.logger.send(:info, "[StackifyRubyAPM] Error: Missing file_path in module which is required in custom instrumentation.")
        else
          require file_path
          StackifyRubyAPM::InstrumenterHelper.patched_module(tracked_func, current_module, file_path,
            current_method: current_method, tracked_function_name: tracked_function_name, is_transaction: transaction)
        end
      end
    rescue => e
      throw e
    end

    if !class_exists?(current_class) && !file_path.nil?
      begin
        require file_path
      rescue LoadError => e
        debug "[StackifyRubyAPM] File path doesn't exist."
        debug e.inspect
      end
    end

    # rubocop:disable Style/Next
    if class_exists?(current_class)
      mod_constant = Module.const_get(current_class.to_s)
      klass_method_flag = mod_constant.method_defined?(current_method.to_s)
      singleton_method_flag = mod_constant.respond_to?(current_method.to_s)
      klass_private_method_flag = mod_constant.private_method_defined?(current_method.to_s)
      klass_protected_method_flag = mod_constant.protected_method_defined?(current_method.to_s)

      class_location = mod_constant.instance_methods(false).map do |m|
        mod_constant.instance_method(m).source_location.first
      end.uniq

      class_path = class_location.last

      if klass_method_flag || klass_private_method_flag
        StackifyRubyAPM::InstrumenterHelper.m_class(
          tracked_func,
          current_class,
          class_path,
          current_method: current_method,
          tracked_function_name: tracked_function_name,
          is_transaction: transaction,
          is_private_method: klass_private_method_flag,
          is_protected_method: klass_protected_method_flag
        )
      elsif singleton_method_flag
        StackifyRubyAPM::InstrumenterHelper.m_singleton(tracked_func, current_class, class_path, current_method: current_method, tracked_function_name: tracked_function_name, is_transaction: transaction)
      end
    end
    # rubocop:enable Style/Next
  end
end
safe_defined?(const_name) click to toggle source
# File lib/stackify_apm/spies.rb, line 70
def self.safe_defined?(const_name)
  Util::Inflector.safe_constantize(const_name)
end