class Object

Constants

OA_CAS_CLIENT_METHODS
OA_CURB_MINIMUM_VERSION
OA_EXCON_MIDDLEWARE_MINIMUM_VERSION
OA_EXCON_MINIMUM_VERSION

We have two ways of instrumenting Excon:

  • For newer versions, use the middleware mechanism Excon exposes

  • For older versions, monkey-patch Excon::Connection#request

OA_EXCON_MINIMUM_VERSION is the minimum version we attempt to instrument at all. OA_EXCON_MIDDLEWARE_MINIMUM_VERSION is the min version we use the newer

instrumentation for.

Note that middlewares were added to Excon prior to 0.19, but we don't use middleware-based instrumentation prior to that version because it didn't expose a way for middlewares to know about request failures.

Why don't we use Excon.defaults? While this might seem a perfect fit, it unfortunately isn't suitable in current form. Someone might reasonably set the default instrumentor to something else after we install our instrumentation. Ideally, excon would itself conform to the subscribe interface of ActiveSupport::Notifications, so we could safely subscribe and not be clobbered by future subscribers, but alas, it does not yet.

OA_HTTPCLIENT_MINIMUM_VERSION

Public Class Methods

new(*args) click to toggle source
Calls superclass method
# File lib/one_apm/inst/background_job/resque.rb, line 58
def self.new(*args)
  super(*args).extend OneApm::Agent::Instrumentation::ResqueInstrumentationInstaller
end

Public Instance Methods

_send_to_one_apm(args, elapsed) click to toggle source
# File lib/one_apm/inst/nosql/redis.rb, line 74
def _send_to_one_apm(args, elapsed)
  if OneApm::Manager.config[:"transaction_tracer.record_sql"] == "obfuscated"
    args.map! do |arg|
      if arg.empty?
        arg
      else
        [arg.first] + ["?"] * (arg.count - 1)
      end
    end
  end
  OneApm::Agent::Datastore.notice_statement(args.inspect, elapsed)
end
call_pipelined_with_oneapm_trace(commands, *rest) click to toggle source
# File lib/one_apm/inst/nosql/redis.rb, line 48
def call_pipelined_with_oneapm_trace(commands, *rest)
  # Report each command as a metric suffixed with _pipelined, so the
  # user can at least see what all the commands were.
  additional = commands.map do |c|
    name = c.kind_of?(Array) ? c[0] : c
    "Datastore/operation/#{oneapm_product}/#{name.to_s.downcase}_pipelined"
  end

  callback = proc do |result, metric, elapsed|
    _send_to_one_apm(commands, elapsed)
    additional.each do |additional_metric|
      OneApm::Support::MethodTracer.trace_execution_scoped(additional_metric) do
        # No-op, just getting them as placeholders in the trace tree
      end
    end
  end

  OneApm::Agent::Datastore.wrap(oneapm_product, 'pipelined', nil, callback) do
    call_pipelined_without_oneapm_trace commands, *rest
  end
end
call_with_one_apm(env) click to toggle source
# File lib/one_apm/inst/framework/grape.rb, line 85
def call_with_one_apm(env)
  begin
    response = call_without_one_apm(env)
  ensure
    begin
      endpoint = env[::OneApm::Agent::Instrumentation::Grape::OA_API_ENDPOINT]
      ::OneApm::Agent::Instrumentation::Grape.handle_transaction(endpoint, self.class.name)
    rescue => e
      OneApm::Manager.logger.warn("Error in Grape instrumentation", e)
    end
  end

  response
end
call_with_oneapm_trace(*args, &blk) click to toggle source
# File lib/one_apm/inst/nosql/redis.rb, line 30
def call_with_oneapm_trace(*args, &blk)
  method_name = args[0].is_a?(Array) ? args[0][0] : args[0]
  filtered_command = filter method_name
  callback = proc do |result, metric, elapsed|
    _send_to_one_apm(args, elapsed)
  end
  OneApm::Agent::Datastore.wrap(oneapm_product, filtered_command, nil, callback) do
    call_without_oneapm_trace(*args, &blk)
  end
end
ensure_index_with_one_apm_trace(spec, opts = {}, &block) click to toggle source
# File lib/one_apm/inst/nosql/mongo.rb, line 99
def ensure_index_with_one_apm_trace(spec, opts = {}, &block)
  metrics = one_apm_generate_metrics(:ensureIndex)
  trace_execution_scoped(metrics) do
    t0 = Time.now

    result = OneApm::Manager.disable_all_tracing do
      ensure_index_without_one_apm_trace(spec, opts, &block)
    end

    spec = case spec
           when Array
             Hash[spec]
           when String, Symbol
             { spec => 1 }
           else
             spec.dup
           end

    one_apm_notice_statement(t0, spec, :ensureIndex)
    result
  end
end
filter(command) click to toggle source
# File lib/one_apm/inst/nosql/redis.rb, line 87
def filter command
  need_filtered = OneApm::Manager.config[:'redis.ignore_commands'].map(&:to_s).include?(command.to_s)
  if need_filtered
    OneApm::Manager.logger.debug "Redis filter command: #{command}"
    nil
  else
    command
  end
end
fork_with_oneapm(*args, &block) click to toggle source
# File lib/one_apm/inst/background_job/resque.rb, line 69
def fork_with_oneapm(*args, &block)
  OneApm::Manager.agent.synchronize_with_harvest do
    fork_without_oneapm(*args, &block)

    # Reached in parent, not expected in the child since Resque
    # uses the block form of fork
  end
end
hook_instrument_method(target_class) click to toggle source
# File lib/one_apm/inst/nosql/mongo.rb, line 36
def hook_instrument_method(target_class)
  target_class.class_eval do
    include OneApm::Support::MethodTracer

    # It's key that this method eats all exceptions, as it rests between the
    # Mongo operation the user called and us returning them the data. Be safe!
    def one_apm_notice_statement(t0, payload, name)
      statement = OneApm::Agent::Datastore::Mongo::StatementFormatter.format(payload, name)
      if statement
        OneApm::Manager.agent.transaction_sampler.notice_nosql_statement(statement, (Time.now - t0).to_f)
      end
    rescue => e
      OneApm::Manager.logger.debug("Exception during Mongo statement gathering", e)
    end

    def one_apm_generate_metrics(operation, payload = nil)
      payload ||= { :collection => self.name, :database => self.db.name }
      OneApm::Agent::Datastore::Mongo::MetricTranslator.metrics_for(operation, payload)
    end

    def instrument_with_one_apm_trace(name, payload = {}, &block)
      metrics = one_apm_generate_metrics(name, payload)

      trace_execution_scoped(metrics) do
        t0 = Time.now

        result = OneApm::Manager.disable_all_tracing do
          instrument_without_one_apm_trace(name, payload, &block)
        end

        one_apm_notice_statement(t0, payload, name)
        result
      end
    end

    alias_method :instrument_without_one_apm_trace, :instrument
    alias_method :instrument, :instrument_with_one_apm_trace
  end
end
hook_instrument_methods() click to toggle source
# File lib/one_apm/inst/nosql/mongo.rb, line 29
def hook_instrument_methods
  hook_instrument_method(::Mongo::Collection)
  hook_instrument_method(::Mongo::Connection)
  hook_instrument_method(::Mongo::Cursor)
  hook_instrument_method(::Mongo::CollectionWriter) if defined?(::Mongo::CollectionWriter)
end
initialize(*args)
Also aliased as: initialize_without_one_apm
initialize_with_one_apm(*args) click to toggle source
# File lib/one_apm/inst/background_job/delayed_job.rb, line 20
def initialize_with_one_apm(*args)
  initialize_without_one_apm(*args)
  worker_name = case
                when self.respond_to?(:name) then self.name
                when self.class.respond_to?(:default_name) then self.class.default_name
                end
  OneApm::DelayedJobInjection.worker_name = worker_name

  if defined?(::Delayed::Job) && ::Delayed::Job.method_defined?(:invoke_job)
    install_oneapm_job_tracer
    OneApm::Probe.instance.init_plugin :dispatcher => :delayed_job
  else
    OneApm::Manager.logger.warn("Did not find a Delayed::Job class responding to invoke_job, aborting DJ instrumentation")
  end
end
Also aliased as: initialize
initialize_without_one_apm(*args)
Alias for: initialize
install_excon_instrumentation(excon_version) click to toggle source
# File lib/one_apm/inst/http_clients/excon.rb, line 42
def install_excon_instrumentation(excon_version)
  require 'one_apm/agent/cross_app/cross_app_tracing'
  require 'one_apm/support/http_clients/excon_wrappers'

  if excon_version >= OA_EXCON_MIDDLEWARE_MINIMUM_VERSION
    install_middleware_excon_instrumentation
  else
    install_legacy_excon_instrumentation
  end
end
install_legacy_excon_instrumentation() click to toggle source
# File lib/one_apm/inst/http_clients/excon.rb, line 65
def install_legacy_excon_instrumentation
  OneApm::Manager.logger.info 'Installing legacy Excon instrumentation'
  require 'one_apm/inst/http_clients/excon/connection'
  ::Excon::Connection.install_oneapm_instrumentation
end
install_middleware_excon_instrumentation() click to toggle source
# File lib/one_apm/inst/http_clients/excon.rb, line 53
def install_middleware_excon_instrumentation
  OneApm::Manager.logger.info 'Installing middleware-based Excon instrumentation'
  require 'one_apm/inst/http_clients/excon/middleware'
  defaults = Excon.defaults

  if defaults[:middlewares]
    defaults[:middlewares] << ::Excon::Middleware::OneApmCrossAppTracing
  else
    OneApm::Manager.logger.warn("Did not find :middlewares key in Excon.defaults, skipping Excon instrumentation")
  end
end
install_mongo_command_subscriber() click to toggle source
# File lib/one_apm/inst/nosql/mongo2.rb, line 111
def install_mongo_command_subscriber
  require 'one_apm/agent/datastore/metric_helper'
  require 'one_apm/agent/datastore/mongo/command_formatter'
  ::Mongo::Monitoring::Global.subscribe(
    ::Mongo::Monitoring::COMMAND,
    OneApm::Agent::Instrumentation::MongoCommandSubscriber.new
  )
end
install_mongo_instrumentation() click to toggle source
# File lib/one_apm/inst/nosql/mongo.rb, line 20
def install_mongo_instrumentation
  require 'one_apm/agent/datastore/mongo/metric_translator'
  require 'one_apm/agent/datastore/mongo/statement_formatter'

  hook_instrument_methods
  instrument_save
  instrument_ensure_index
end
install_oneapm_job_tracer() click to toggle source
# File lib/one_apm/inst/background_job/delayed_job.rb, line 39
def install_oneapm_job_tracer
  Delayed::Job.class_eval do
    include OneApm::Agent::Instrumentation::TransactionBase
    if self.instance_methods.include?('name') || self.instance_methods.include?(:name)
      add_transaction_tracer "invoke_job", :category => 'OtherTransaction/DelayedJob', :path => '#{self.name}'
    else
      add_transaction_tracer "invoke_job", :category => 'OtherTransaction/DelayedJob'
    end
  end
end
instrument_call() click to toggle source
# File lib/one_apm/inst/framework/grape.rb, line 83
def instrument_call
  ::Grape::API.class_eval do
    def call_with_one_apm(env)
      begin
        response = call_without_one_apm(env)
      ensure
        begin
          endpoint = env[::OneApm::Agent::Instrumentation::Grape::OA_API_ENDPOINT]
          ::OneApm::Agent::Instrumentation::Grape.handle_transaction(endpoint, self.class.name)
        rescue => e
          OneApm::Manager.logger.warn("Error in Grape instrumentation", e)
        end
      end

      response
    end

    alias_method :call_without_one_apm, :call
    alias_method :call, :call_with_one_apm
  end
end
instrument_ensure_index() click to toggle source
# File lib/one_apm/inst/nosql/mongo.rb, line 97
def instrument_ensure_index
  ::Mongo::Collection.class_eval do
    def ensure_index_with_one_apm_trace(spec, opts = {}, &block)
      metrics = one_apm_generate_metrics(:ensureIndex)
      trace_execution_scoped(metrics) do
        t0 = Time.now

        result = OneApm::Manager.disable_all_tracing do
          ensure_index_without_one_apm_trace(spec, opts, &block)
        end

        spec = case spec
               when Array
                 Hash[spec]
               when String, Symbol
                 { spec => 1 }
               else
                 spec.dup
               end

        one_apm_notice_statement(t0, spec, :ensureIndex)
        result
      end
    end

    alias_method :ensure_index_without_one_apm_trace, :ensure_index
    alias_method :ensure_index, :ensure_index_with_one_apm_trace
  end
end
instrument_save() click to toggle source
# File lib/one_apm/inst/nosql/mongo.rb, line 76
def instrument_save
  ::Mongo::Collection.class_eval do
    def save_with_one_apm_trace(doc, opts = {}, &block)
      metrics = one_apm_generate_metrics(:save)
      trace_execution_scoped(metrics) do
        t0 = Time.now

        result = OneApm::Manager.disable_all_tracing do
          save_without_one_apm_trace(doc, opts, &block)
        end

        one_apm_notice_statement(t0, doc, :save)
        result
      end
    end

    alias_method :save_without_one_apm_trace, :save
    alias_method :save, :save_with_one_apm_trace
  end
end
instrument_with_one_apm_trace(name, payload = {}, &block) click to toggle source
# File lib/one_apm/inst/nosql/mongo.rb, line 56
def instrument_with_one_apm_trace(name, payload = {}, &block)
  metrics = one_apm_generate_metrics(name, payload)

  trace_execution_scoped(metrics) do
    t0 = Time.now

    result = OneApm::Manager.disable_all_tracing do
      instrument_without_one_apm_trace(name, payload, &block)
    end

    one_apm_notice_statement(t0, payload, name)
    result
  end
end
instrument_with_oneapm(name, payload = {}, &block) click to toggle source
# File lib/one_apm/inst/rails3/action_controller.rb, line 160
def instrument_with_oneapm(name, payload = {}, &block)
  identifier = payload[:identifier]
  scope_name = "View/#{OneApm::Agent::Instrumentation::Rails3::ActionView::OneApm.template_metric(identifier)}/Partial"
  trace_execution_scoped(scope_name) do
    instrument_without_oneapm(name, payload, &block)
  end
end
oa_puma_hooks() click to toggle source
# File lib/one_apm/inst/dispatcher/puma.rb, line 6
def oa_puma_hooks
  Puma.cli_config.options[:worker_boot] || Puma.cli_config.options[:before_worker_boot]
end
one_apm_generate_metrics(operation, payload = nil) click to toggle source
# File lib/one_apm/inst/nosql/mongo.rb, line 51
def one_apm_generate_metrics(operation, payload = nil)
  payload ||= { :collection => self.name, :database => self.db.name }
  OneApm::Agent::Datastore::Mongo::MetricTranslator.metrics_for(operation, payload)
end
one_apm_notice_statement(t0, payload, name) click to toggle source

It's key that this method eats all exceptions, as it rests between the Mongo operation the user called and us returning them the data. Be safe!

# File lib/one_apm/inst/nosql/mongo.rb, line 42
def one_apm_notice_statement(t0, payload, name)
  statement = OneApm::Agent::Datastore::Mongo::StatementFormatter.format(payload, name)
  if statement
    OneApm::Manager.agent.transaction_sampler.notice_nosql_statement(statement, (Time.now - t0).to_f)
  end
rescue => e
  OneApm::Manager.logger.debug("Exception during Mongo statement gathering", e)
end
oneapm_metric_path(action_name_override = nil) click to toggle source

determine the path that is used in the metric name for the called controller action

# File lib/one_apm/inst/rails/action_controller.rb, line 108
def oneapm_metric_path(action_name_override = nil)
  action_part = action_name_override || action_name
  if action_name_override || self.class.action_methods.include?(action_part)
    "#{self.class.controller_path}/#{action_part}"
  else
    "#{self.class.controller_path}/(other)"
  end
end
oneapm_notice_error(exception, custom_params = {}) click to toggle source

Make a note of an exception associated with the currently executing controller action. Note that this used to be available on Object but we replaced that global method with OneApm::Agent#notice_error. Use that one outside of controller actions.

# File lib/one_apm/inst/rails/errors.rb, line 26
def oneapm_notice_error(exception, custom_params = {})
  OneApm::Transaction.notice_error exception, :custom_params => custom_params, :request => request
end
oneapm_product() click to toggle source
# File lib/one_apm/inst/nosql/redis.rb, line 25
def oneapm_product 
  @product ||= OneApm::Agent::Datastore.oneapm_product("Redis", host,  port)
end
perform_action_with_oneapm_trace_wrapper() click to toggle source
# File lib/one_apm/inst/rails/action_controller.rb, line 96
def perform_action_with_oneapm_trace_wrapper
  options = {}
  options[:params] = (respond_to?(:filter_parameters)) ? filter_parameters(params) : params
  perform_action_with_oneapm_trace(options) { perform_action_without_oneapm_trace }
end
receive_message(result_klass)
receive_message_with_oneapm(result_klass) click to toggle source
# File lib/one_apm/inst/http_clients/thrift.rb, line 107
def receive_message_with_oneapm(result_klass)
  begin
    op = operator(result_klass)
    op_started = started_time(op)
    result = receive_message_without_oneapm(result_klass)

    state = OneApm::TransactionState.tl_get
    
    OneApm::Agent::CrossAppTracingMessage.finish_trace(state, op_started, segment, rpc_request, metrics(op))
    result
  rescue => e
    OneApm::Manager.logger.error "Thrift receive_message error: #{e}"
  ensure
    rpc_reset!
    result
  end
end
Also aliased as: receive_message
receive_message_without_oneapm(result_klass)
Alias for: receive_message
render_with_oneapm(*args, &block) click to toggle source
# File lib/one_apm/inst/rails3/action_controller.rb, line 100
def render_with_oneapm(*args, &block)
  options = if @virtual_path && @virtual_path.starts_with?('/') # file render
    {:file => true }
  else
    {}
  end
  template_metric = OneApm::Agent::Instrumentation::Rails3::ActionView::OneApm.template_metric(@identifier, options)
  render_type = OneApm::Agent::Instrumentation::Rails3::ActionView::OneApm.render_type(@identifier)
  str = "View/#{template_metric}/#{render_type}"
  trace_execution_scoped str do
    render_without_oneapm(*args, &block)
  end
end
rescue_action_with_oneapm_trace(exception) click to toggle source
# File lib/one_apm/inst/rails/errors.rb, line 30
def rescue_action_with_oneapm_trace(exception)
  rescue_action_without_oneapm_trace exception
  OneApm::Transaction.notice_error exception, :request => request
end
save_with_one_apm_trace(doc, opts = {}, &block) click to toggle source
# File lib/one_apm/inst/nosql/mongo.rb, line 78
def save_with_one_apm_trace(doc, opts = {}, &block)
  metrics = one_apm_generate_metrics(:save)
  trace_execution_scoped(metrics) do
    t0 = Time.now

    result = OneApm::Manager.disable_all_tracing do
      save_without_one_apm_trace(doc, opts, &block)
    end

    one_apm_notice_statement(t0, doc, :save)
    result
  end
end
send_frame(frame, signal_activity = true)
Also aliased as: send_frame_without_oneapm
send_frame_with_oneapm(frame, signal_activity = true) click to toggle source
# File lib/one_apm/inst/background_job/rabbitmq.rb, line 28
def send_frame_with_oneapm(frame, signal_activity = true)
  perform_action_with_oneapm_trace(trace_args(frame)) do
    send_frame_without_oneapm(frame, signal_activity)
  end
end
Also aliased as: send_frame
send_frame_without_oneapm(frame, signal_activity = true)
Alias for: send_frame
send_frame_without_timeout(frame, signal_activity = true)
send_frame_without_timeout_wihout_oneapm(frame, signal_activity = true)
send_frame_without_timeout_with_oneapm(frame, signal_activity = true) click to toggle source
# File lib/one_apm/inst/background_job/rabbitmq.rb, line 44
def send_frame_without_timeout_with_oneapm(frame, signal_activity = true)
   perform_action_with_oneapm_trace(trace_args(frame)) do
     send_frame_without_timeout_wihout_oneapm(frame, signal_activity)
  end
end
Also aliased as: send_frame_without_timeout
send_frameset(frames, channel)
Also aliased as: send_frameset_without_oneapm
send_frameset_with_oneapm(frames, channel) click to toggle source
# File lib/one_apm/inst/background_job/rabbitmq.rb, line 36
def send_frameset_with_oneapm(frames, channel)
  perform_action_with_oneapm_trace(trace_args(frames[0])) do
    send_frameset_without_oneapm(frames, channel)
  end
end
Also aliased as: send_frameset
send_frameset_without_oneapm(frames, channel)
Alias for: send_frameset
send_message(name, args_class, args = {})
Also aliased as: send_message_without_oneapm
send_message_with_oneapm(name, args_class, args = {}) click to toggle source
# File lib/one_apm/inst/http_clients/thrift.rb, line 79
def send_message_with_oneapm(name, args_class, args = {})
  state   = OneApm::TransactionState.tl_get
  t0      = Time.now
  self.segment = OneApm::Agent::CrossAppTracingMessage.start_trace(state, t0, rpc_request)
  operations[name] = { :started_time => t0 }
  send_message_without_oneapm(name, args_class, args)
end
Also aliased as: send_message
send_message_without_oneapm(name, args_class, args = {})
Alias for: send_message
send_oneway_message(name, args_class, args = {})
send_oneway_message_with_oneapm(name, args_class, args = {}) click to toggle source
# File lib/one_apm/inst/http_clients/thrift.rb, line 89
def send_oneway_message_with_oneapm(name, args_class, args = {})
  begin
    state   = OneApm::TransactionState.tl_get
    t0      = Time.now
    segment = OneApm::Agent::CrossAppTracingMessage.start_trace(state, t0, rpc_request)
    result  = send_oneway_message_without_oneapm(name, args_class, args)
    OneApm::Agent::CrossAppTracingMessage.finish_trace(state, t0, segment, rpc_request, metrics(name))
    result
   rescue => e
    OneApm::Manager.logger.error "Thrift receive_message error: #{e}"
  ensure
    rpc_reset!
    result
  end
end
Also aliased as: send_oneway_message
send_oneway_message_without_oneapm(name, args_class, args = {})
Alias for: send_oneway_message
supported_sequel_version?() click to toggle source
# File lib/one_apm/inst/orm/sequel.rb, line 14
def supported_sequel_version?
  Sequel.const_defined?( :MAJOR ) &&
    ( Sequel::MAJOR > 3 ||
      Sequel::MAJOR == 3 && Sequel::MINOR >= 37 )
end
trace_args(frame) click to toggle source
# File lib/one_apm/inst/background_job/rabbitmq.rb, line 19
def trace_args(frame)
  class_name = frame.respond_to?(:method_class) ? frame.method_class.name : frame.class.name.downcase
  {
    :name => 'perform',
    :class_name => class_name,
    :category => 'OtherTransaction/RabbitMQ'
  }
end