class ElasticAPM::Instrumenter

@api private
rubocop:disable Metrics/ClassLength

def current_span

def current_span
  current_spans.last
end

def current_spans

def current_spans
  @current.spans
end

def current_transaction

def current_transaction
  @current.transaction
end

def current_transaction=(transaction)

def current_transaction=(transaction)
  @current.transaction = transaction
end

def end_span

def end_span
  return unless (span = current_spans.pop)
  span.done
  enqueue.call span
  update_span_metrics(span)
  span
end

def end_transaction(result = nil)

def end_transaction(result = nil)
  return nil unless (transaction = current_transaction)
  self.current_transaction = nil
  transaction.done result
  enqueue.call transaction
  update_transaction_metrics(transaction)
  transaction
end

def initialize(config, metrics:, stacktrace_builder:, &enqueue)

def initialize(config, metrics:, stacktrace_builder:, &enqueue)
  @config = config
  @stacktrace_builder = stacktrace_builder
  @enqueue = enqueue
  @metrics = metrics
  @current = Current.new
end

def inspect

def inspect
  '<ElasticAPM::Instrumenter ' \
    "current_transaction=#{current_transaction.inspect}" \
    '>'
end

def random_sample?(config)

def random_sample?(config)
  rand <= config.transaction_sample_rate
end

def set_custom_context(context)

def set_custom_context(context)
  return unless current_transaction
  current_transaction.context.custom.merge!(context)
end

def set_label(key, value)

def set_label(key, value)
  return unless current_transaction
  key = key.to_s.gsub(/[\."\*]/, '_').to_sym
  current_transaction.context.labels[key] = value
end

def set_user(user)

def set_user(user)
  return unless current_transaction
  current_transaction.set_user(user)
end

def start

def start
  debug 'Starting instrumenter'
end

def start_span(

rubocop:disable Metrics/ParameterLists
rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
def start_span(
  name,
  type = nil,
  subtype: nil,
  action: nil,
  backtrace: nil,
  context: nil,
  trace_context: nil
)
  return unless (transaction = current_transaction)
  return unless transaction.sampled?
  transaction.inc_started_spans!
  if transaction.max_spans_reached?
    transaction.inc_dropped_spans!
    return
  end
  parent = current_span || transaction
  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
  )
  if backtrace && transaction.config.span_frames_min_duration?
    span.original_backtrace = backtrace
  end
  current_spans.push span
  span.start
end

def start_transaction(

rubocop:disable Metrics/MethodLength
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
  sampled = trace_context ? trace_context.recorded? : random_sample?(config)
  transaction =
    Transaction.new(
      name,
      type,
      context: context,
      trace_context: trace_context,
      sampled: sampled,
      config: config
    )
  transaction.start
  self.current_transaction = transaction
end

def stop

def stop
  debug 'Stopping instrumenter'
  self.current_transaction = nil
  current_spans.pop until current_spans.empty?
  @subscriber.unregister! if @subscriber
end

def subscriber=(subscriber)

def subscriber=(subscriber)
  debug 'Registering subscriber'
  @subscriber = subscriber
  @subscriber.register!
end

def update_span_metrics(span)

rubocop:disable Metrics/MethodLength, Metrics/AbcSize
def update_span_metrics(span)
  return unless span.transaction.config.breakdown_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

def update_transaction_metrics(transaction)

rubocop:disable Metrics/MethodLength, Metrics/AbcSize
def update_transaction_metrics(transaction)
  return unless transaction.config.collect_metrics?
  tags = {
    'transaction.name': transaction.name,
    'transaction.type': transaction.type
  }
  @metrics.get(:transaction).timer(
    :'transaction.duration.sum.us',
    tags: tags, reset_on_collect: true
  ).update(transaction.duration)
  @metrics.get(:transaction).counter(
    :'transaction.duration.count',
    tags: tags, reset_on_collect: true
  ).inc!
  return unless transaction.sampled?
  return unless transaction.config.breakdown_metrics?
  @metrics.get(:breakdown).counter(
    :'transaction.breakdown.count',
    tags: tags, reset_on_collect: true
  ).inc!
  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