class Mmtrix::Agent::Transaction

This class represents a single transaction (usually mapping to one web request or background job invocation) instrumented by the Ruby agent.

@api public

Constants

APDEX_ALL_METRIC
APDEX_F
APDEX_METRIC
APDEX_OTHER_METRIC
APDEX_OTHER_TXN_METRIC_PREFIX
APDEX_S
APDEX_T
APDEX_TXN_METRIC_PREFIX
CONTROLLER_MIDDLEWARE_PREFIX
CONTROLLER_PREFIX
EMPTY_SUMMARY_METRICS
FAILED_TO_STOP_MESSAGE
GRAPE_PREFIX
GUID_LENGTH
HEX_DIGITS
JRUBY_CPU_TIME_ERROR
MIDDLEWARE_PREFIX
MIDDLEWARE_SUMMARY_METRICS
NESTED_TRACE_STOP_OPTIONS
OTHER_SUMMARY_METRIC
OTHER_TRANSACTION_PREFIX
QUEUE_TIME_METRIC
RACK_PREFIX
SINATRA_PREFIX
SUBTRANSACTION_PREFIX

for nested transactions

TASK_PREFIX
TRACE_OPTIONS_SCOPED
TRACE_OPTIONS_UNSCOPED
TRANSACTION_NAMING_SOURCES
WEB_SUMMARY_METRIC
WEB_TRANSACTION_CATEGORIES

Attributes

apdex_start[RW]

A Time instance used for calculating the apdex score, which might end up being @start, or it might be further upstream if we can find a request header for the queue entry time

attributes[R]
cat_path_hashes[R]
category[R]
exceptions[RW]
filtered_params[RW]
frame_stack[R]
gc_start_snapshot[R]
guid[R]
http_response_code[RW]
jruby_cpu_start[RW]
metrics[R]
process_cpu_start[RW]
raw_synthetics_header[RW]

Fields for tracking synthetics requests

referer[R]
request_path[R]
start_time[RW]

A Time instance for the start time, never nil

synthetics_payload[RW]

Fields for tracking synthetics requests

transaction_trace[R]

Populated with the trace sample once this transaction is completed.

Public Class Methods

abort_transaction!() click to toggle source

Indicate that you don’t want to keep the currently saved transaction information

# File lib/mmtrix/agent/transaction.rb, line 182
def self.abort_transaction! #THREAD_LOCAL_ACCESS
  state = Mmtrix::Agent::TransactionState.tl_get
  txn = state.current_transaction
  txn.abort_transaction!(state) if txn
end
add_agent_attribute(key, value, default_destinations) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 222
def self.add_agent_attribute(key, value, default_destinations)
  if txn = tl_current
    txn.add_agent_attribute(key, value, default_destinations)
  else
    Mmtrix::Agent.logger.debug "Attempted to add agent attribute: #{key} without transaction"
  end
end
apdex_bucket(duration, failed, apdex_t) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 209
def self.apdex_bucket(duration, failed, apdex_t)
  case
  when failed
    :apdex_f
  when duration <= apdex_t
    :apdex_s
  when duration <= 4 * apdex_t
    :apdex_t
  else
    :apdex_f
  end
end
merge_untrusted_agent_attributes(attributes, prefix, default_destinations) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 234
def self.merge_untrusted_agent_attributes(attributes, prefix, default_destinations)
  if txn = tl_current
    txn.merge_untrusted_agent_attributes(attributes, prefix, default_destinations)
  else
    Mmtrix::Agent.logger.debug "Attempted to merge untrusted attributes without transaction"
  end
end
nested_transaction_name(name) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 172
def self.nested_transaction_name(name)
  if name.start_with?(CONTROLLER_PREFIX) || name.start_with?(OTHER_TRANSACTION_PREFIX)
    "#{SUBTRANSACTION_PREFIX}#{name}"
  else
    name
  end
end
new(category, options) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 258
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 = Mmtrix::Agent::StatsEngine::GCProfiler.take_snapshot
  @filtered_params = options[:filtered_params] || {}

  @exceptions = {}
  @metrics = TransactionMetrics.new
  @guid = generate_guid
  @cat_path_hashes = nil

  @ignore_this_transaction = false
  @ignore_apdex = false
  @ignore_enduser = false

  @attributes = Attributes.new(Mmtrix::Agent.instance.attribute_filter)

  merge_request_parameters(@filtered_params)

  if request = options[:request]
    @request_path = path_from_request(request)
    @referer = referer_from_request(request)
  end
end
notice_error(e, options={}) click to toggle source

See Mmtrix::Agent.notice_error for options and commentary

# File lib/mmtrix/agent/transaction.rb, line 189
def self.notice_error(e, options={}) #THREAD_LOCAL_ACCESS
  state = Mmtrix::Agent::TransactionState.tl_get
  txn = state.current_transaction
  if txn
    txn.notice_error(e, options)
  elsif Mmtrix::Agent.instance
    Mmtrix::Agent.instance.error_collector.notice_error(e, options)
  end
end
recording_web_transaction?() click to toggle source

Returns truthy if the current in-progress transaction is considered a a web transaction (as opposed to, e.g., a background transaction).

@api public

# File lib/mmtrix/agent/transaction.rb, line 204
def self.recording_web_transaction? #THREAD_LOCAL_ACCESS
  txn = tl_current
  txn && txn.recording_web_transaction?
end
set_default_transaction_name(name, category = nil, node_name = nil) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 76
def self.set_default_transaction_name(name, category = nil, node_name = nil) #THREAD_LOCAL_ACCESS
  txn  = tl_current
  name = txn.make_transaction_name(name, category)
  txn.name_last_frame(node_name || name)
  txn.set_default_transaction_name(name, category)
end
set_overriding_transaction_name(name, category = nil) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 83
def self.set_overriding_transaction_name(name, category = nil) #THREAD_LOCAL_ACCESS
  txn = tl_current
  return unless txn

  name = txn.make_transaction_name(name, category)

  txn.name_last_frame(name)
  txn.set_overriding_transaction_name(name, category)
end
start(state, category, options) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 108
def self.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
  Mmtrix::Agent.logger.error("Exception during Transaction.start", e)
  nil
end
start_new_transaction(state, category, options) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 124
def self.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/mmtrix/agent/transaction.rb, line 133
def self.stop(state, end_time=Time.now)
  txn = state.current_transaction

  if txn.nil?
    Mmtrix::Agent.logger.error(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?(MIDDLEWARE_PREFIX)
      summary_metrics = MIDDLEWARE_SUMMARY_METRICS
    else
      summary_metrics = EMPTY_SUMMARY_METRICS
    end

    Mmtrix::Agent::MethodTracerHelpers.trace_execution_scoped_footer(
      state,
      nested_frame.start_time.to_f,
      nested_name,
      summary_metrics,
      nested_frame,
      NESTED_TRACE_STOP_OPTIONS,
      end_time.to_f)
  end

  :transaction_stopped
rescue => e
  state.reset
  Mmtrix::Agent.logger.error("Exception during Transaction.stop", e)
  nil
end
tl_current() click to toggle source

Return the currently active transaction, or nil.

# File lib/mmtrix/agent/transaction.rb, line 72
def self.tl_current
  TransactionState.tl_get.current_transaction
end
wrap(state, name, category, options = {}) { || ... } click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 93
def self.wrap(state, name, category, options = {})
  Transaction.start(state, category, options.merge(:transaction_name => name))

  begin
    # We shouldn't raise from Transaction.start, but only wrap the yield
    # to be absolutely sure we don't report agent problems as app errors
    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 trace is not saved To fully ignore all metrics and errors, use ignore! instead.

# File lib/mmtrix/agent/transaction.rb, line 424
def abort_transaction!(state)
  @ignore_trace = true
end
add_agent_attribute(key, value, default_destinations) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 230
def add_agent_attribute(key, value, default_destinations)
  @attributes.add_agent_attribute(key, value, default_destinations)
end
add_custom_attributes(p) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 810
def add_custom_attributes(p)
  attributes.merge_custom_attributes(p)
end
add_custom_parameters(p)
apdex_bucket(duration, current_apdex_t) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 752
def apdex_bucket(duration, current_apdex_t)
  self.class.apdex_bucket(duration, had_error?, current_apdex_t)
end
apdex_t() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 785
def apdex_t
  transaction_specific_apdex_t || Agent.config[:apdex_t]
end
append_apdex_perf_zone(duration, payload) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 642
def append_apdex_perf_zone(duration, payload)
  if recording_web_transaction?
    bucket = apdex_bucket(duration, apdex_t)
  elsif background_apdex_t = transaction_specific_apdex_t
    bucket = apdex_bucket(duration, background_apdex_t)
  end

  return unless bucket

  bucket_str = case bucket
  when :apdex_s then APDEX_S
  when :apdex_t then APDEX_T
  when :apdex_f then APDEX_F
  else nil
  end
  payload[:apdex_perf_zone] = bucket_str if bucket_str
end
append_cat_info(state, duration, payload) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 660
def append_cat_info(state, duration, payload)
  return unless include_guid?(state, duration)
  payload[:guid] = guid

  return unless state.is_cross_app?
  trip_id             = cat_trip_id(state)
  path_hash           = cat_path_hash(state)
  referring_path_hash = cat_referring_path_hash(state)

  payload[:cat_trip_id]             = trip_id             if trip_id
  payload[:cat_referring_path_hash] = referring_path_hash if referring_path_hash

  if path_hash
    payload[:cat_path_hash] = path_hash

    alternate_path_hashes = cat_path_hashes - [path_hash]
    unless alternate_path_hashes.empty?
      payload[:cat_alternate_path_hashes] = alternate_path_hashes
    end
  end
end
append_referring_transaction_guid_to(state, payload) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 690
def append_referring_transaction_guid_to(state, payload)
  referring_guid = Mmtrix::Agent.instance.cross_app_monitor.client_referring_transaction_guid(state)
  if referring_guid
    payload[:referring_transaction_guid] = referring_guid
  end
end
append_synthetics_to(state, payload) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 682
def append_synthetics_to(state, payload)
  return unless is_synthetics_request?

  payload[:synthetics_resource_id] = synthetics_resource_id
  payload[:synthetics_job_id]      = synthetics_job_id
  payload[:synthetics_monitor_id]  = synthetics_monitor_id
end
assign_agent_attributes() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 514
def assign_agent_attributes
  if referer
    add_agent_attribute(:'request.headers.referer', referer,
                        Mmtrix::Agent::AttributeFilter::DST_ERROR_COLLECTOR)
  end

  if http_response_code
    add_agent_attribute(:httpResponseCode, http_response_code.to_s,
                        Mmtrix::Agent::AttributeFilter::DST_TRANSACTION_TRACER|
                        Mmtrix::Agent::AttributeFilter::DST_TRANSACTION_EVENTS|
                        Mmtrix::Agent::AttributeFilter::DST_ERROR_COLLECTOR)
  end
end
assign_intrinsics(state) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 528
def assign_intrinsics(state)
  if gc_time = calculate_gc_time
    attributes.add_intrinsic_attribute(:gc_time, gc_time)
  end

  if burn = cpu_burn
    attributes.add_intrinsic_attribute(:cpu_time, burn)
  end

  if is_synthetics_request?
    attributes.add_intrinsic_attribute(:synthetics_resource_id, synthetics_resource_id)
    attributes.add_intrinsic_attribute(:synthetics_job_id, synthetics_job_id)
    attributes.add_intrinsic_attribute(:synthetics_monitor_id, synthetics_monitor_id)
  end

  if state.is_cross_app?
    attributes.add_intrinsic_attribute(:trip_id, cat_trip_id(state))
    attributes.add_intrinsic_attribute(:path_hash, cat_path_hash(state))
  end
end
background_summary_metrics() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 439
def background_summary_metrics
  segments = @frozen_name.split('/')
  if segments.size > 2
    ["OtherTransaction/#{segments[1]}/all", OTHER_SUMMARY_METRIC]
  else
    []
  end
end
best_name() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 370
def best_name
  @frozen_name  || @overridden_name ||
    @default_name || Mmtrix::Agent::UNKNOWN_METRIC
end
calculate_gc_time() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 549
def calculate_gc_time
  gc_stop_snapshot = Mmtrix::Agent::StatsEngine::GCProfiler.take_snapshot
  Mmtrix::Agent::StatsEngine::GCProfiler.record_delta(gc_start_snapshot, gc_stop_snapshot)
end
cat_path_hash(state) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 590
def cat_path_hash(state)
  referring_path_hash = cat_referring_path_hash(state) || '0'
  seed = referring_path_hash.to_i(16)
  result = Mmtrix::Agent.instance.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/mmtrix/agent/transaction.rb, line 605
def cat_referring_path_hash(state)
  Mmtrix::Agent.instance.cross_app_monitor.client_referring_transaction_path_hash(state)
end
cat_trip_id(state) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 586
def cat_trip_id(state)
  Mmtrix::Agent.instance.cross_app_monitor.client_referring_transaction_trip_id(state) || guid
end
commit!(state, end_time, outermost_node_name) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 497
def commit!(state, end_time, outermost_node_name)
  assign_agent_attributes
  assign_intrinsics(state)

  @transaction_trace = transaction_sampler.on_finishing_transaction(state, self, end_time)
  sql_sampler.on_finishing_transaction(state, @frozen_name)

  record_summary_metrics(outermost_node_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/mmtrix/agent/transaction.rb, line 829
def cpu_burn
  normal_cpu_burn || jruby_cpu_burn
end
create_nested_frame(state, category, options) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 319
def create_nested_frame(state, category, options)
  @has_children = true
  if options[:filtered_params] && !options[:filtered_params].empty?
    @filtered_params = options[:filtered_params]
    merge_request_parameters(options[:filtered_params])
  end

  frame_stack.push Mmtrix::Agent::MethodTracerHelpers.trace_execution_scoped_header(state, Time.now.to_f)
  name_last_frame(options[:transaction_name])

  set_default_transaction_name(options[:transaction_name], category)
end
default_name=(name) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 315
def default_name=(name)
  @default_name = Helper.correctly_encoded(name)
end
freeze_name_and_execute_if_not_ignored() { || ... } click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 387
def freeze_name_and_execute_if_not_ignored
  if !name_frozen?
    name = promoted_transaction_name(best_name)
    name = Mmtrix::Agent.instance.transaction_rules.rename(name)
    @name_frozen = true

    if name.nil?
      ignore!
      @frozen_name = best_name
    else
      @frozen_name = name
    end
  end

  if block_given? && !@ignore_this_transaction
    yield
  end
end
had_error?() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 745
def had_error?
  @exceptions.each do |exception, _|
    return true unless Mmtrix::Agent.instance.error_collector.error_is_ignored?(exception)
  end
  false
end
ignore!() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 843
def ignore!
  @ignore_this_transaction = true
end
ignore?() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 847
def ignore?
  @ignore_this_transaction
end
ignore_apdex!() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 851
def ignore_apdex!
  @ignore_apdex = true
end
ignore_apdex?() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 855
def ignore_apdex?
  @ignore_apdex
end
ignore_enduser!() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 859
def ignore_enduser!
  @ignore_enduser = true
end
ignore_enduser?() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 863
def ignore_enduser?
  @ignore_enduser
end
ignore_trace?() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 867
def ignore_trace?
  @ignore_trace
end
include_guid?(state, duration) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 582
def include_guid?(state, duration)
  state.is_cross_app? || is_synthetics_request?
end
influences_transaction_name?(category) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 366
def influences_transaction_name?(category)
  !category || frame_stack.size == 1 || similar_category?(category)
end
instrumentation_state() click to toggle source

This transaction-local hash may be used as temprory storage by instrumentation that needs to pass data from one instrumentation point to another.

For example, if both A and B are instrumented, and A calls B but some piece of state needed by the instrumentation at B is only available at A, the instrumentation at A may write into the hash, call through, and then remove the key afterwards, allowing the instrumentation at B to read the value in between.

Keys should be symbols, and care should be taken to not generate key names dynamically, and to ensure that keys are removed upon return from the method that creates them.

# File lib/mmtrix/agent/transaction.rb, line 307
def instrumentation_state
  @instrumentation_state ||= {}
end
is_synthetics_request?() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 609
def is_synthetics_request?
  synthetics_payload != nil && raw_synthetics_header != nil
end
jruby_cpu_burn() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 838
def jruby_cpu_burn
  return unless @jruby_cpu_start
  jruby_cpu_time - @jruby_cpu_start
end
log_frozen_name(name) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 361
def log_frozen_name(name)
  Mmtrix::Agent.logger.warn("Attempted to rename transaction to '#{name}' after transaction name was already frozen as '#{@frozen_name}'.")
  nil
end
make_transaction_name(name, category=nil) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 336
def make_transaction_name(name, category=nil)
  namer = Instrumentation::ControllerInstrumentation::TransactionNamer
  "#{namer.prefix_for_category(self, category)}#{name}"
end
merge_metrics() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 697
def merge_metrics
  Mmtrix::Agent.instance.stats_engine.merge_transaction_metrics!(@metrics, best_name)
end
merge_request_parameters(params) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 332
def merge_request_parameters(params)
  merge_untrusted_agent_attributes(params, :'request.parameters', AttributeFilter::DST_NONE)
end
merge_untrusted_agent_attributes(attributes, prefix, default_destinations) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 242
def merge_untrusted_agent_attributes(attributes, prefix, default_destinations)
  @attributes.merge_untrusted_agent_attributes(attributes, prefix, default_destinations)
end
name_frozen?() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 406
def name_frozen?
  @frozen_name ? true : false
end
name_last_frame(name) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 357
def name_last_frame(name)
  frame_stack.last.name = name
end
name_set?() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 375
def name_set?
  (@overridden_name || @default_name) ? true : false
end
needs_middleware_summary_metrics?(name) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 448
def needs_middleware_summary_metrics?(name)
  name.start_with?(MIDDLEWARE_PREFIX)
end
normal_cpu_burn() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 833
def normal_cpu_burn
  return unless @process_cpu_start
  process_cpu - @process_cpu_start
end
overridden_name=(name) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 311
def overridden_name=(name)
  @overridden_name = Helper.correctly_encoded(name)
end
promoted_transaction_name(name) click to toggle source
queue_time() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 722
def queue_time
  @apdex_start ? @start_time - @apdex_start : 0
end
record_apdex(state, end_time=Time.now) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 756
def record_apdex(state, end_time=Time.now)
  return unless state.is_execution_traced?

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

    if recording_web_transaction?
      record_apdex_metrics(APDEX_METRIC, APDEX_TXN_METRIC_PREFIX,
                           total_duration, action_duration, apdex_t)
    else
      record_apdex_metrics(APDEX_OTHER_METRIC, APDEX_OTHER_TXN_METRIC_PREFIX,
                           total_duration, action_duration, transaction_specific_apdex_t)
    end
  end
end
record_apdex_metrics(rollup_metric, transaction_prefix, total_duration, action_duration, current_apdex_t) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 773
def record_apdex_metrics(rollup_metric, transaction_prefix, total_duration, action_duration, current_apdex_t)
  return unless current_apdex_t

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

  @metrics.record_unscoped(rollup_metric, apdex_bucket_global, current_apdex_t)
  @metrics.record_unscoped(APDEX_ALL_METRIC, apdex_bucket_global, current_apdex_t)
  txn_apdex_metric = @frozen_name.sub(/^[^\/]+\//, transaction_prefix)
  @metrics.record_unscoped(txn_apdex_metric, apdex_bucket_txn, current_apdex_t)
end
record_cat_path_hash(hash) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 598
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/mmtrix/agent/transaction.rb, line 701
def record_exceptions
  @exceptions.each do |exception, options|
    options[:uri]      ||= request_path if request_path
    options[:metric]     = best_name
    options[:attributes] = @attributes

    agent.error_collector.notice_error(exception, options)
  end
end
record_queue_time() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 726
def record_queue_time
  value = queue_time
  if value > 0.0
    if value < MethodTracerHelpers::MAX_ALLOWED_METRIC_DURATION
      @metrics.record_unscoped(QUEUE_TIME_METRIC, value)
    else
      ::Mmtrix::Agent.logger.log_once(:warn, :too_high_queue_time, "Not recording unreasonably large queue time of #{value} s")
    end
  end
end
record_summary_metrics(outermost_node_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/mmtrix/agent/transaction.rb, line 556
def record_summary_metrics(outermost_node_name, end_time)
  metrics = summary_metrics
  metrics << @frozen_name unless @frozen_name == outermost_node_name
  @metrics.record_unscoped(metrics, end_time.to_f - start_time.to_f, 0)
end
recording_web_transaction?() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 817
def recording_web_transaction?
  web_category?(@category)
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/mmtrix/agent/transaction.rb, line 564
def send_transaction_finished_event(state, start_time, end_time)
  duration = end_time.to_f - start_time.to_f
  payload = {
    :name                 => @frozen_name,
    :bucket               => recording_web_transaction? ? :request : :background,
    :start_timestamp      => start_time.to_f,
    :duration             => duration,
    :metrics              => @metrics,
    :attributes           => @attributes,
  }
  append_cat_info(state, duration, payload)
  append_apdex_perf_zone(duration, payload)
  append_synthetics_to(state, payload)
  append_referring_transaction_guid_to(state, payload)

  agent.events.notify(:transaction_finished, payload)
end
set_default_transaction_name(name, category) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 341
def set_default_transaction_name(name, category)
  return log_frozen_name(name) if name_frozen?
  if influences_transaction_name?(category)
    self.default_name = name
    @category = category if category
  end
end
set_overriding_transaction_name(name, category) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 349
def set_overriding_transaction_name(name, category)
  return log_frozen_name(name) if name_frozen?
  if influences_transaction_name?(category)
    self.overridden_name = name
    @category = category if category
  end
end
set_user_attributes(p)
similar_category?(category) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 825
def similar_category?(category)
  web_category?(@category) == web_category?(category)
end
start(state) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 410
def start(state)
  return if !state.is_execution_traced?

  transaction_sampler.on_start_transaction(state, start_time)
  sql_sampler.on_start_transaction(state, start_time, request_path)
  Mmtrix::Agent.instance.events.notify(:start_transaction)
  Mmtrix::Agent::BusyCalculator.dispatcher_start(start_time)

  frame_stack.push Mmtrix::Agent::MethodTracerHelpers.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/mmtrix/agent/transaction.rb, line 452
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 = TRACE_OPTIONS_SCOPED
  else
    name = @frozen_name
    trace_options = TRACE_OPTIONS_UNSCOPED
  end

  # 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 = MIDDLEWARE_SUMMARY_METRICS
  else
    summary_metrics_with_exclusive_time = EMPTY_SUMMARY_METRICS
  end

  Mmtrix::Agent::MethodTracerHelpers.trace_execution_scoped_footer(
    state,
    start_time.to_f,
    name,
    summary_metrics_with_exclusive_time,
    outermost_frame,
    trace_options,
    end_time.to_f)

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

  commit!(state, end_time, name) unless @ignore_this_transaction
end
summary_metrics() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 431
def summary_metrics
  if @frozen_name.start_with?(CONTROLLER_PREFIX)
    [WEB_SUMMARY_METRIC]
  else
    background_summary_metrics
  end
end
synthetics_account_id() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 618
def synthetics_account_id
  info = synthetics_payload or return nil
  info[1]
end
synthetics_job_id() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 628
def synthetics_job_id
  info = synthetics_payload or return nil
  info[3]
end
synthetics_monitor_id() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 633
def synthetics_monitor_id
  info = synthetics_payload or return nil
  info[4]
end
synthetics_resource_id() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 623
def synthetics_resource_id
  info = synthetics_payload or return nil
  info[2]
end
synthetics_version() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 613
def synthetics_version
  info = synthetics_payload or return nil
  info[0]
end
transaction_specific_apdex_t() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 789
def transaction_specific_apdex_t
  key = :web_transactions_apdex
  Agent.config[key] && Agent.config[key][best_name]
end
user_defined_rules_ignore?() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 488
def user_defined_rules_ignore?
  return unless request_path
  return if (rules = Mmtrix::Agent.config[:"rules.ignore_url_regexes"]).empty?

  rules.any? do |rule|
    request_path.match(rule)
  end
end
web_category?(category) click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 821
def web_category?(category)
  WEB_TRANSACTION_CATEGORIES.include?(category)
end
with_database_metric_name(model, method, product=nil) { || ... } click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 794
def with_database_metric_name(model, method, product=nil)
  previous = self.instrumentation_state[:datastore_override]
  model_name = case model
               when Class
                 model.name
               when String
                 model
               else
                 model.to_s
               end
  self.instrumentation_state[:datastore_override] = [method, model_name, product]
  yield
ensure
  self.instrumentation_state[:datastore_override] = previous
end

Private Instance Methods

agent() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 894
def agent
  Mmtrix::Agent.instance
end
generate_guid() click to toggle source

generate a random 64 bit uuid

# File lib/mmtrix/agent/transaction.rb, line 910
def generate_guid
  guid = ''
  GUID_LENGTH.times do |a|
    guid << HEX_DIGITS[rand(16)]
  end
  guid
end
jruby_cpu_time() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 880
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
  ::Mmtrix::Agent.logger.log_once(:warn, :jruby_cpu_time, JRUBY_CPU_TIME_ERROR, e)
  ::Mmtrix::Agent.logger.debug(JRUBY_CPU_TIME_ERROR, e)
  nil
end
path_from_request(req) click to toggle source

In practice we expect req to be a Rack::Request or ActionController::AbstractRequest (for older Rails versions). But anything that responds to path can be passed to perform_action_with_mmtrix_trace.

We don’t expect the path to include a query string, however older test helpers for rails construct the PATH_INFO enviroment variable improperly and we’re generally being defensive.

# File lib/mmtrix/agent/transaction.rb, line 933
def path_from_request(req)
  path = req.path
  path = HTTPClients::URIUtil.strip_query_string(path)
  path.empty? ? "/" : path
end
process_cpu() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 873
def process_cpu
  return nil if defined? JRuby
  p = Process.times
  p.stime + p.utime
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/mmtrix/agent/transaction.rb, line 920
def referer_from_request(req)
  if req && req.respond_to?(:referer) && req.referer
    HTTPClients::URIUtil.strip_query_string(req.referer.to_s)
  end
end
sql_sampler() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 902
def sql_sampler
  agent.sql_sampler
end
transaction_sampler() click to toggle source
# File lib/mmtrix/agent/transaction.rb, line 898
def transaction_sampler
  agent.transaction_sampler
end