class OneApm::Transaction

Constants

OA_APDEX_F
OA_APDEX_METRIC
OA_APDEX_S
OA_APDEX_T
OA_CONTROLLER_OA_MIDDLEWARE_PREFIX
OA_CONTROLLER_PREFIX
OA_EMPTY_SUMMARY_METRICS
OA_FAILED_TO_STOP_MESSAGE
OA_GRAPE_PREFIX
OA_JRUBY_CPU_TIME_ERROR
OA_MIDDLEWARE_PREFIX
OA_MIDDLEWARE_SUMMARY_METRICS
OA_NESTED_TRACE_STOP_OPTIONS
OA_OTHER_SUMMARY_METRIC
OA_OTHER_TRANSACTION_PREFIX
OA_QUEUE_TIME_METRIC
OA_RACK_PREFIX
OA_SINATRA_PREFIX
OA_SUBTRANSACTION_PREFIX
OA_TASK_PREFIX
OA_TRACE_IGNORE_OPTIONS
OA_TRACE_OPTIONS_SCOPED
OA_TRACE_OPTIONS_UNSCOPED
OA_TRANSACTION_NAMING_SOURCES
OA_UNKNOWN_METRIC
OA_WEB_SUMMARY_METRIC
OA_WEB_TRANSACTION_CATEGORIES
OA_WEB_TRANSACTION_PREFIX

Attributes

apdex_start[RW]
cat_path_hashes[R]
category[R]
database_metric_name[R]
exceptions[RW]
filtered_params[RW]
frame_stack[R]
frozen_name[R]
gc_start_snapshot[R]
guid[R]
http_response_code[RW]
ignore_frames[RW]
jruby_cpu_start[RW]
metrics[R]
process_cpu_start[RW]
request[RW]
start_time[RW]
transaction_trace[R]

Public Class Methods

apdex_bucket(duration, failed, apdex_t) click to toggle source
  • duration: response time

  • failed: the request is failed or not

  • apdex_s: satisfy

  • apdex_t: tolerate

  • apdex_f: frustrate

# File lib/one_apm/transaction/class_methods.rb, line 98
def apdex_bucket(duration, failed, apdex_t)
  case
  when failed
    :apdex_f  # frustrate if request failed
  when duration <= apdex_t
    :apdex_s  # satisfy if duration < tolerate
  when duration <= 4 * apdex_t
    :apdex_t  # tolerate if duration < 4 * tolerate
  else
    :apdex_f  # otherwise frustrate
  end
end
nested_transaction_name(name) click to toggle source
# File lib/one_apm/transaction/class_methods.rb, line 83
def nested_transaction_name(name)
  if name.start_with?(Transaction::OA_WEB_TRANSACTION_PREFIX) || name.start_with?(Transaction::OA_OTHER_TRANSACTION_PREFIX)
    "#{Transaction::OA_SUBTRANSACTION_PREFIX}#{name}"
  else
    name
  end
end
new(category, options) click to toggle source
# File lib/one_apm/transaction.rb, line 50
def initialize(category, options)
  @frame_stack = []
  @has_children = false

  self.default_name = options[:transaction_name]
  @overridden_name  = nil
  @frozen_name      = nil

  @category = category
  @start_time = Time.now
  @apdex_start = options[:apdex_start_time] || @start_time
  @jruby_cpu_start = jruby_cpu_time
  @process_cpu_start = process_cpu
  @gc_start_snapshot = OneApm::Collector::StatsEngine::GCProfiler.take_snapshot
  @filtered_params = options[:filtered_params] || {}
  @request = options[:request]
  @exceptions = {}
  @metrics = TransactionMetrics.new
  @guid = OneApm::Helper.generate_guid
  @cat_path_hashes = nil

  @ignore_this_transaction = false
  @ignore_apdex = false
  @ignore_enduser = false
  @ignore_frames = options[:ignore_frames] || []
end
referer_from_request(req) click to toggle source

Make a safe attempt to get the referer from a request object, generally successful when it's a Rack request.

# File lib/one_apm/transaction/class_methods.rb, line 125
def referer_from_request(req)
  if req && req.respond_to?(:referer)
    req.referer.to_s.split('?').first
  end
end
start(state, category, options) click to toggle source
# File lib/one_apm/transaction/class_methods.rb, line 15
def start(state, category, options)
  category ||= :controller
  txn = state.current_transaction
  if txn  
    txn.create_nested_frame(state, category, options)
  else
    txn = start_new_transaction(state, category, options)
  end

  txn
rescue => e
  OneApm::Manager.logger.error("Exception during Transaction.start", e)
  nil
end
start_new_transaction(state, category, options) click to toggle source
# File lib/one_apm/transaction/class_methods.rb, line 8
def start_new_transaction(state, category, options)
  txn = Transaction.new(category, options)
  state.reset(txn)
  txn.start(state)
  txn
end
stop(state, end_time=Time.now) click to toggle source
# File lib/one_apm/transaction/class_methods.rb, line 30
def stop(state, end_time=Time.now)
  txn = state.current_transaction
  if txn.nil?
    OneApm::Manager.logger.error(Transaction::OA_FAILED_TO_STOP_MESSAGE)
    return
  end

  nested_frame = txn.frame_stack.pop
  
  if txn.frame_stack.empty?
    txn.stop(state, end_time, nested_frame)
    state.reset
  else
    nested_name = nested_transaction_name(nested_frame.name)

    if nested_name.start_with?(Transaction::OA_MIDDLEWARE_PREFIX)
      summary_metrics = Transaction::OA_MIDDLEWARE_SUMMARY_METRICS
    else
      summary_metrics = Transaction::OA_EMPTY_SUMMARY_METRICS
    end

    options = Transaction::OA_NESTED_TRACE_STOP_OPTIONS
    options = Transaction::OA_TRACE_IGNORE_OPTIONS  if txn.ignore_frame?(nested_name)

    OneApm::Support::MethodTracer::Helpers.trace_execution_scoped_footer(
      state,
      nested_frame.start_time.to_f,
      nested_name,
      summary_metrics,
      nested_frame,
      options,
      end_time.to_f)
  end

  :transaction_stopped
rescue => e
  state.reset
  OneApm::Manager.logger.error("Exception during Transaction.stop", e)
  nil
end
uri_from_request(req) click to toggle source

Make a safe attempt to get the URI, without the host and query string.

# File lib/one_apm/transaction/class_methods.rb, line 112
def uri_from_request(req)
  approximate_uri = case
                   # when req.respond_to?(:fullpath   ) then req.fullpath
                   # when req.respond_to?(:path       ) then req.path
                    when req.respond_to?(:request_uri) then req.request_uri
                    when req.respond_to?(:uri        ) then req.uri
                    when req.respond_to?(:url        ) then req.url
                    end
  return approximate_uri[%r{^(https?://.*?)?(/[^?]*)}, 1] + approximate_uri[%r{^(https?://.*?)?(/[^?]*)}, 2] || '/' if approximate_uri
end
wrap(state, name, category, options = {}) { || ... } click to toggle source
# File lib/one_apm/transaction/class_methods.rb, line 71
def wrap(state, name, category, options = {})
  Transaction.start(state, category, options.merge(:transaction_name => name))
  begin
    yield
  rescue => e
    Transaction.notice_error(e)
    raise e
  ensure
    Transaction.stop(state)
  end
end

Public Instance Methods

abort_transaction!(state) click to toggle source

Call this to ensure that the current transaction is not saved

# File lib/one_apm/transaction.rb, line 171
def abort_transaction!(state)
  transaction_sampler.ignore_transaction(state)
end
apdex_bucket(duration) click to toggle source
# File lib/one_apm/transaction/transaction_apdex.rb, line 6
def apdex_bucket(duration)
  Transaction.apdex_bucket(duration, had_error?, apdex_t)
end
apdex_t() click to toggle source
# File lib/one_apm/transaction/transaction_apdex.rb, line 10
def apdex_t
  transaction_specific_apdex_t || OneApm::Manager.config[:apdex_t]
end
background_summary_metrics() click to toggle source
# File lib/one_apm/transaction/transaction_summary.rb, line 14
def background_summary_metrics
  segments = @frozen_name.split('/')
  if segments.size > 2
    ["OtherTransaction/#{segments[1]}/all", OA_OTHER_SUMMARY_METRIC]
  else
    []
  end
end
cat_path_hash(state) click to toggle source
# File lib/one_apm/transaction.rb, line 273
def cat_path_hash(state)
  referring_path_hash = cat_referring_path_hash(state) || '0'
  seed = referring_path_hash.to_i(16)
  result = agent.cross_app_monitor.path_hash(best_name, seed)
  record_cat_path_hash(result)
  result
end
cat_referring_path_hash(state) click to toggle source
# File lib/one_apm/transaction.rb, line 288
def cat_referring_path_hash(state)
  agent.cross_app_monitor.client_referring_transaction_path_hash(state)
end
cat_trip_id(state) click to toggle source
# File lib/one_apm/transaction.rb, line 269
def cat_trip_id(state)
  agent.cross_app_monitor.client_referring_transaction_trip_id(state) || guid
end
commit!(state, end_time, outermost_segment_name) click to toggle source
# File lib/one_apm/transaction.rb, line 127
def commit!(state, end_time, outermost_segment_name)
  record_transaction_cpu(state)
  record_gc(state, end_time)
  sql_sampler.on_finishing_transaction(state, @frozen_name)

  record_summary_metrics(outermost_segment_name, end_time)
  record_apdex(state, end_time) unless ignore_apdex?
  record_queue_time

  record_exceptions
  merge_metrics

  send_transaction_finished_event(state, start_time, end_time)
end
cpu_burn() click to toggle source
# File lib/one_apm/transaction/transaction_cpu.rb, line 6
def cpu_burn
  normal_cpu_burn || jruby_cpu_burn
end
create_nested_frame(state, category, options) click to toggle source
# File lib/one_apm/transaction.rb, line 243
def create_nested_frame(state, category, options)
  @has_children = true
  if options[:filtered_params] && !options[:filtered_params].empty?
    @filtered_params = options[:filtered_params]
  end
  frame_stack.push OneApm::Support::MethodTracer::Helpers.trace_execution_scoped_header(state, Time.now.to_f)
  name_last_frame(options[:transaction_name])

  set_default_transaction_name(options[:transaction_name], category)
end
ignore_frame?(tx_name) click to toggle source
# File lib/one_apm/transaction.rb, line 310
def ignore_frame? tx_name
  return false if ignore_frames.empty? 
  ignore_frames.any?{|iframe| tx_name.to_s.match(/#{iframe}/)}
end
instrumentation_state() click to toggle source
# File lib/one_apm/transaction.rb, line 235
def instrumentation_state
  @instrumentation_state ||= {}
end
jruby_cpu_burn() click to toggle source
# File lib/one_apm/transaction/transaction_jruby_functions.rb, line 34
def jruby_cpu_burn
  return unless @jruby_cpu_start
  jruby_cpu_time - @jruby_cpu_start
end
jruby_cpu_time() click to toggle source
# File lib/one_apm/transaction/transaction_jruby_functions.rb, line 20
def jruby_cpu_time
  return nil unless @@java_classes_loaded
  threadMBean = Java::JavaLangManagement::ManagementFactory.getThreadMXBean()

  return nil unless threadMBean.isCurrentThreadCpuTimeSupported
  java_utime = threadMBean.getCurrentThreadUserTime()  # ns

  -1 == java_utime ? 0.0 : java_utime/1e9
rescue => e
  OneApm::Manager.logger.log_once(:warn, :jruby_cpu_time, OA_JRUBY_CPU_TIME_ERROR, e)
  OneApm::Manager.logger.debug(OA_JRUBY_CPU_TIME_ERROR, e)
  nil
end
merge_metrics() click to toggle source
# File lib/one_apm/transaction.rb, line 230
def merge_metrics
  return if ignore_frame?(best_name)
  agent.stats_engine.merge_transaction_metrics!(@metrics, best_name)
end
needs_middleware_summary_metrics?(name) click to toggle source
# File lib/one_apm/transaction/transaction_summary.rb, line 23
def needs_middleware_summary_metrics?(name)
  name.start_with?(OA_MIDDLEWARE_PREFIX)
end
normal_cpu_burn() click to toggle source
# File lib/one_apm/transaction/transaction_cpu.rb, line 10
def normal_cpu_burn
  return unless @process_cpu_start
  process_cpu - @process_cpu_start
end
noticed_error_ids() click to toggle source
# File lib/one_apm/transaction.rb, line 239
def noticed_error_ids
  @noticed_error_ids ||= []
end
process_cpu() click to toggle source
# File lib/one_apm/transaction/transaction_cpu.rb, line 15
def process_cpu
  return nil if defined? JRuby
  p = Process.times
  p.stime + p.utime
end
record_apdex(state, end_time=Time.now) click to toggle source
# File lib/one_apm/transaction.rb, line 196
def record_apdex(state, end_time=Time.now)
  return unless recording_web_transaction? && state.is_execution_traced?

  freeze_name_and_execute_if_not_ignored do
    action_duration = end_time - start_time
    total_duration  = end_time - apdex_start

    apdex_bucket_global = apdex_bucket(total_duration)
    apdex_bucket_txn    = apdex_bucket(action_duration)

    @metrics.record_unscoped(OA_APDEX_METRIC, apdex_bucket_global, apdex_t)
    txn_apdex_metric = @frozen_name.gsub(/^[^\/]+\//, 'Apdex/')
    @metrics.record_unscoped(txn_apdex_metric, apdex_bucket_txn, apdex_t)
  end
end
record_cat_path_hash(hash) click to toggle source
# File lib/one_apm/transaction.rb, line 281
def record_cat_path_hash(hash)
  @cat_path_hashes ||= []
  if @cat_path_hashes.size < 10 && !@cat_path_hashes.include?(hash)
    @cat_path_hashes << hash
  end
end
record_exceptions() click to toggle source
# File lib/one_apm/transaction.rb, line 223
def record_exceptions
  @exceptions.each do |exception, options|
    options[:metric] = best_name
    agent.error_collector.notice_error(exception, options)
  end
end
record_gc(state, end_time) click to toggle source
# File lib/one_apm/transaction.rb, line 182
def record_gc(state, end_time)
  gc_stop_snapshot = OneApm::Collector::StatsEngine::GCProfiler.take_snapshot
  gc_delta = OneApm::Collector::StatsEngine::GCProfiler.record_delta(gc_start_snapshot, gc_stop_snapshot)
  @transaction_trace = transaction_sampler.on_finishing_transaction(state, self, end_time, gc_delta)
end
record_queue_time() click to toggle source
# File lib/one_apm/transaction.rb, line 212
def record_queue_time
  value = queue_time
  if value > 0.0
    if value < OneApm::Support::MethodTracer::Helpers::OA_MAX_ALLOWED_METRIC_DURATION
      @metrics.record_unscoped(OA_QUEUE_TIME_METRIC, value)
    else
      OneApm::Manager.logger.log_once(:warn, :too_high_queue_time, "Not recording unreasonably large queue time of #{value} s")
    end
  end
end
record_summary_metrics(outermost_segment_name, end_time) click to toggle source

The summary metrics recorded by this method all end up with a duration equal to the transaction itself, and an exclusive time of zero.

# File lib/one_apm/transaction.rb, line 190
def record_summary_metrics(outermost_segment_name, end_time)
  metrics = summary_metrics
  metrics << @frozen_name unless @frozen_name == outermost_segment_name
  @metrics.record_unscoped(metrics, end_time.to_f - start_time.to_f, 0)
end
record_transaction_cpu(state) click to toggle source
# File lib/one_apm/transaction.rb, line 175
def record_transaction_cpu(state)
  burn = cpu_burn
  if burn
    transaction_sampler.notice_transaction_cpu_time(state, burn)
  end
end
reqest_url() click to toggle source
# File lib/one_apm/transaction.rb, line 166
def reqest_url
  request.url rescue ''
end
send_transaction_finished_event(state, start_time, end_time) click to toggle source

This event is fired when the transaction is fully completed. The metric values and sampler can't be successfully modified from this event.

# File lib/one_apm/transaction.rb, line 144
def send_transaction_finished_event(state, start_time, end_time)
  duration = end_time.to_f - start_time.to_f
  payload = {
    :name             => @frozen_name,
    :start_timestamp  => start_time.to_f,
    :duration         => duration,
    :metrics          => @metrics,
    :custom_params    => custom_parameters,
    :guid             => guid,
    :request_url      => reqest_url
  }

  append_cat_info(state, duration, payload)
  append_apdex_perf_zone(duration, payload)
  append_synthetics_to(state, payload)
  append_referring_transaction_guid_to(state, payload)
  append_http_response_code(payload)
  append_metric_ids_to(payload)

  agent.events.notify(:transaction_finished, payload)
end
start(state) click to toggle source
# File lib/one_apm/transaction.rb, line 77
def start(state)
  return if !state.is_execution_traced?

  transaction_sampler.on_start_transaction(state, start_time, uri)
  sql_sampler.on_start_transaction(state, start_time, uri)
  agent.events.notify(:start_transaction)
  OneApm::Agent::BusyCalculator.dispatcher_start(start_time)

  frame_stack.push OneApm::Support::MethodTracer::Helpers.trace_execution_scoped_header(state, start_time.to_f)
  name_last_frame @default_name
end
stop(state, end_time, outermost_frame) click to toggle source
# File lib/one_apm/transaction.rb, line 89
def stop(state, end_time, outermost_frame)
  return if !state.is_execution_traced?
  freeze_name_and_execute_if_not_ignored
  ignore! if user_defined_rules_ignore?

  if @has_children
    name = Transaction.nested_transaction_name(outermost_frame.name)
    trace_options = OA_TRACE_OPTIONS_SCOPED
  else
    name = @frozen_name
    trace_options = OA_TRACE_OPTIONS_UNSCOPED
  end

  trace_options = OA_TRACE_IGNORE_OPTIONS if ignore_frame?(outermost_frame.name)
    
  # These metrics are recorded here instead of in record_summary_metrics
  # in order to capture the exclusive time associated with the outer-most
  # TT node.
  if needs_middleware_summary_metrics?(name)
    summary_metrics_with_exclusive_time = OA_MIDDLEWARE_SUMMARY_METRICS
  else
    summary_metrics_with_exclusive_time = OA_EMPTY_SUMMARY_METRICS
  end

  OneApm::Support::MethodTracer::Helpers.trace_execution_scoped_footer(
    state,
    start_time.to_f,
    name,
    summary_metrics_with_exclusive_time,
    outermost_frame,
    trace_options,
    end_time.to_f)

  OneApm::Agent::BusyCalculator.dispatcher_finish(end_time)

  commit!(state, end_time, name) unless @ignore_this_transaction
end
summary_metrics() click to toggle source
# File lib/one_apm/transaction/transaction_summary.rb, line 6
def summary_metrics
  if @frozen_name.start_with?(OA_WEB_TRANSACTION_PREFIX)
    [OA_WEB_SUMMARY_METRIC]
  else
    background_summary_metrics
  end
end
transaction_specific_apdex_t() click to toggle source
# File lib/one_apm/transaction/transaction_apdex.rb, line 14
def transaction_specific_apdex_t
  key = :web_transactions_apdex
  OneApm::Manager.config[key] && OneApm::Manager.config[key][best_name]
end
user_defined_rules_ignore?() click to toggle source
# File lib/one_apm/transaction.rb, line 254
def user_defined_rules_ignore?
  return unless uri
  return if (rules = OneApm::Manager.config[:"rules.ignore_url_regexes"]).empty?

  parsed = OneApm::Support::HTTPClients::URIUtil.parse_url(uri)
  filtered_uri = OneApm::Support::HTTPClients::URIUtil.filter_uri(parsed)

  rules.any? do |rule|
    filtered_uri.match(rule)
  end
rescue URI::InvalidURIError => e
  OneApm::Manager.logger.debug("Error parsing URI: #{uri}", e)
  false
end