class ElasticAPM::Instrumenter

@api private

Constants

SPAN_KEY
TRANSACTION_KEY

Attributes

enqueue[R]
stacktrace_builder[R]

Public Class Methods

new(config, metrics:, stacktrace_builder:, &enqueue) click to toggle source
# File lib/elastic_apm/instrumenter.rb, line 59
def initialize(config, metrics:, stacktrace_builder:, &enqueue)
  @config = config
  @stacktrace_builder = stacktrace_builder
  @enqueue = enqueue
  @metrics = metrics

  @current = Current.new
end

Public Instance Methods

current_span() click to toggle source
# File lib/elastic_apm/instrumenter.rb, line 168
def current_span
  current_spans.last
end
current_spans() click to toggle source

spans

# File lib/elastic_apm/instrumenter.rb, line 164
def current_spans
  @current.spans
end
current_transaction() click to toggle source

transactions

# File lib/elastic_apm/instrumenter.rb, line 99
def current_transaction
  @current.transaction
end
current_transaction=(transaction) click to toggle source
# File lib/elastic_apm/instrumenter.rb, line 103
def current_transaction=(transaction)
  @current.transaction = transaction
end
end_span(span = nil) click to toggle source

rubocop:enable Metrics/ParameterLists rubocop:enable Metrics/CyclomaticComplexity rubocop:enable Metrics/PerceivedComplexity

# File lib/elastic_apm/instrumenter.rb, line 238
def end_span(span = nil)
  if span
    current_spans.delete(span)
  else
    span = current_spans.pop
  end

  return unless span

  span.done

  enqueue.call span

  update_span_metrics(span)

  span
end
end_transaction(result = nil) click to toggle source
# File lib/elastic_apm/instrumenter.rb, line 146
def end_transaction(result = nil)
  return nil unless (transaction = current_transaction)

  self.current_transaction = nil

  transaction.done result

  if transaction.sampled? || @config.version < Config::ServerInfo::VERSION_8_0
    enqueue.call transaction
  end

  update_transaction_metrics(transaction)

  transaction
end
handle_forking!() click to toggle source
# File lib/elastic_apm/instrumenter.rb, line 86
def handle_forking!
  stop
  start
end
inspect() click to toggle source
# File lib/elastic_apm/instrumenter.rb, line 275
def inspect
  '<ElasticAPM::Instrumenter ' \
    "current_transaction=#{current_transaction.inspect}" \
    '>'
end
set_custom_context(context) click to toggle source
# File lib/elastic_apm/instrumenter.rb, line 265
def set_custom_context(context)
  return unless current_transaction
  current_transaction.context.custom.merge!(context)
end
set_label(key, value) click to toggle source

metadata

# File lib/elastic_apm/instrumenter.rb, line 258
def set_label(key, value)
  return unless current_transaction

  key = key.to_s.gsub(/[."*]/, '_').to_sym
  current_transaction.context.labels[key] = value
end
set_user(user) click to toggle source
# File lib/elastic_apm/instrumenter.rb, line 270
def set_user(user)
  return unless current_transaction
  current_transaction.set_user(user)
end
start() click to toggle source
# File lib/elastic_apm/instrumenter.rb, line 70
def start
  debug 'Starting instrumenter'
  # We call register! on @subscriber in case the
  # instrumenter was stopped and started again
  @subscriber&.register!
end
start_span( name, type = nil, subtype: nil, action: nil, backtrace: nil, context: nil, trace_context: nil, parent: nil, sync: nil, exit_span: nil ) click to toggle source

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

# File lib/elastic_apm/instrumenter.rb, line 175
def start_span(
  name,
  type = nil,
  subtype: nil,
  action: nil,
  backtrace: nil,
  context: nil,
  trace_context: nil,
  parent: nil,
  sync: nil,
  exit_span: nil
)

  transaction =
    case parent
    when Span
      parent.transaction
    when Transaction
      parent
    else
      current_transaction
    end
  return unless transaction
  return unless transaction.sampled?
  return unless transaction.inc_started_spans!

  parent ||= (current_span || current_transaction)

  # To not mess with breakdown metric stats, exit spans MUST not add
  # sub-spans unless they share the same type and subtype.
  if parent && parent.is_a?(Span) && parent.exit_span?
    if parent.type != type || parent.subtype != subtype
      debug "Skipping new span '#{name}' as its parent is an exit_span"
      return
    end
  end

  span = Span.new(
    name: name,
    subtype: subtype,
    action: action,
    transaction: transaction,
    parent: parent,
    trace_context: trace_context,
    type: type,
    context: context,
    stacktrace_builder: stacktrace_builder,
    sync: sync,
    exit_span: exit_span
  )

  if backtrace && transaction.span_frames_min_duration
    span.original_backtrace = backtrace
  end

  current_spans.push span

  span.start
end
start_transaction( name = nil, type = nil, config:, context: nil, trace_context: nil ) click to toggle source
# File lib/elastic_apm/instrumenter.rb, line 107
def start_transaction(
  name = nil,
  type = nil,
  config:,
  context: nil,
  trace_context: nil
)
  return nil unless config.instrument?

  if (transaction = current_transaction)
    raise ExistingTransactionError,
      "Transactions may not be nested.\n" \
      "Already inside #{transaction.inspect}"
  end

  if trace_context
    sampled = trace_context.recorded?
    sample_rate = trace_context.tracestate.sample_rate
  else
    sampled = random_sample?(config)
    sample_rate = sampled ? config.transaction_sample_rate : 0
  end

  transaction =
    Transaction.new(
      name,
      type,
      context: context,
      trace_context: trace_context,
      sampled: sampled,
      sample_rate: sample_rate,
      config: config
    )

  transaction.start

  self.current_transaction = transaction
end
stop() click to toggle source
# File lib/elastic_apm/instrumenter.rb, line 77
def stop
  debug 'Stopping instrumenter'

  self.current_transaction = nil
  current_spans.pop until current_spans.empty?

  @subscriber&.unregister!
end
subscriber=(subscriber) click to toggle source
# File lib/elastic_apm/instrumenter.rb, line 91
def subscriber=(subscriber)
  debug 'Registering ActiveSupport::Notifications subscriber'
  @subscriber = subscriber
  @subscriber.register!
end

Private Instance Methods

random_sample?(config) click to toggle source
# File lib/elastic_apm/instrumenter.rb, line 283
def random_sample?(config)
  rand <= config.transaction_sample_rate
end
update_span_metrics(span) click to toggle source
# File lib/elastic_apm/instrumenter.rb, line 311
def update_span_metrics(span)
  return unless span.transaction.collect_metrics?

  tags = {
    'span.type': span.type,
    'transaction.name': span.transaction.name,
    'transaction.type': span.transaction.type
  }

  tags[:'span.subtype'] = span.subtype if span.subtype

  @metrics.get(:breakdown).timer(
    :'span.self_time.sum.us',
    tags: tags, reset_on_collect: true
  ).update(span.self_time)

  @metrics.get(:breakdown).counter(
    :'span.self_time.count',
    tags: tags, reset_on_collect: true
  ).inc!
end
update_transaction_metrics(transaction) click to toggle source
# File lib/elastic_apm/instrumenter.rb, line 287
def update_transaction_metrics(transaction)
  return unless transaction.collect_metrics?

  tags = {
    'transaction.name': transaction.name,
    'transaction.type': transaction.type
  }

  return unless transaction.sampled?
  return unless transaction.breakdown_metrics

  span_tags = tags.merge('span.type': 'app')

  @metrics.get(:breakdown).timer(
    :'span.self_time.sum.us',
    tags: span_tags, reset_on_collect: true
  ).update(transaction.self_time)

  @metrics.get(:breakdown).counter(
    :'span.self_time.count',
    tags: span_tags, reset_on_collect: true
  ).inc!
end