class ElasticAPM::Agent

@api private
rubocop:disable Metrics/ClassLength

def self.instance # rubocop:disable Style/TrivialAccessors

rubocop:disable Style/TrivialAccessors
def self.instance # rubocop:disable Style/TrivialAccessors
  @instance
end

def self.running?

def self.running?
  !!@instance
end

def self.start(config)

rubocop:disable Metrics/MethodLength
def self.start(config)
  return @instance if @instance
  config = Config.new(config) if config.is_a?(Hash)
  unless config.enabled_environments.include?(config.environment)
    config.logger && config.logger.info(
      format('Not tracking anything in "%s" env', config.environment)
    )
    return
  end
  LOCK.synchronize do
    return @instance if @instance
    @instance = new(config.freeze).start
  end
end

def self.stop

def self.stop
  LOCK.synchronize do
    return unless @instance
    @instance.stop
    @instance = nil
  end
end

def add_filter(key, callback)

def add_filter(key, callback)
  @http.filters.add(key, callback)
end

def boot_worker

def boot_worker
  debug 'Booting worker'
  @worker_thread = Thread.new do
    Worker.new(@config, @queue, @http).run_forever
  end
end

def build_context(rack_env)

def build_context(rack_env)
  @context_builder.build(rack_env)
end

def current_transaction

def current_transaction
  instrumenter.current_transaction
end

def enqueue_errors(errors)

def enqueue_errors(errors)
  boot_worker unless worker_running?
  data = @serializers.errors.build_all(errors)
  @queue << Worker::Request.new('/v1/errors', data)
  errors
end

def enqueue_transactions(transactions)

def enqueue_transactions(transactions)
  boot_worker unless worker_running?
  data = @serializers.transactions.build_all(transactions)
  @queue << Worker::Request.new('/v1/transactions', data)
  transactions
end

def initialize(config)

def initialize(config)
  @config = config
  @http = Http.new(config)
  @queue = Queue.new
  @instrumenter = Instrumenter.new(config, self)
  @context_builder = ContextBuilder.new(config)
  @error_builder = ErrorBuilder.new(config)
  @serializers = Struct.new(:transactions, :errors).new(
    Serializers::Transactions.new(config),
    Serializers::Errors.new(config)
  )
end

def inspect

def inspect
  '<ElasticAPM::Agent>'
end

def kill_worker

def kill_worker
  @queue << Worker::StopMessage.new
  if @worker_thread && !@worker_thread.join(5) # 5 secs
    raise 'Failed to wait for worker, not all messages sent'
  end
  @worker_thread = nil
end

def report(exception, handled: true)

def report(exception, handled: true)
  return if config.filter_exception_types.include?(exception.class.to_s)
  error = @error_builder.build_exception(
    exception,
    handled: handled
  )
  enqueue_errors error
end

def report_message(message, backtrace: nil, **attrs)

def report_message(message, backtrace: nil, **attrs)
  error = @error_builder.build_log(
    message,
    backtrace: backtrace,
    **attrs
  )
  enqueue_errors error
end

def set_custom_context(*args)

def set_custom_context(*args)
  instrumenter.set_custom_context(*args)
end

def set_tag(*args)

def set_tag(*args)
  instrumenter.set_tag(*args)
end

def set_user(*args)

def set_user(*args)
  instrumenter.set_user(*args)
end

def span(*args, &block)

def span(*args, &block)
  instrumenter.span(*args, &block)
end

def start

def start
  debug '[%s] Starting agent, reporting to %s', VERSION, config.server_url
  @instrumenter.start
  config.enabled_injectors.each do |lib|
    require "elastic_apm/injectors/#{lib}"
  end
  self
end

def stop

def stop
  @instrumenter.stop
  kill_worker
  self
end

def transaction(*args, &block)

def transaction(*args, &block)
  instrumenter.transaction(*args, &block)
end

def worker_running?

def worker_running?
  @worker_thread && @worker_thread.alive?
end