elastic-apm 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ci/.jenkins_exclude.yml +47 -0
- data/.ci/.jenkins_framework.yml +4 -0
- data/.ci/.jenkins_master_framework.yml +1 -0
- data/.ci/.jenkins_ruby.yml +1 -0
- data/.ci/downstreamTests.groovy +1 -1
- data/.gitignore +2 -1
- data/.rspec +1 -0
- data/CHANGELOG.asciidoc +24 -0
- data/Dockerfile +43 -0
- data/Gemfile +34 -15
- data/README.md +30 -1
- data/bin/dev +54 -0
- data/bin/run-tests +27 -0
- data/docker-compose.yml +32 -0
- data/docs/api.asciidoc +13 -2
- data/docs/configuration.asciidoc +30 -0
- data/docs/getting-started-rack.asciidoc +24 -0
- data/docs/release-notes.asciidoc +1 -1
- data/lib/elastic_apm.rb +12 -1
- data/lib/elastic_apm/agent.rb +15 -3
- data/lib/elastic_apm/central_config.rb +39 -19
- data/lib/elastic_apm/child_durations.rb +42 -0
- data/lib/elastic_apm/config.rb +27 -11
- data/lib/elastic_apm/context/request/socket.rb +1 -1
- data/lib/elastic_apm/context_builder.rb +1 -1
- data/lib/elastic_apm/error.rb +10 -0
- data/lib/elastic_apm/error/exception.rb +7 -0
- data/lib/elastic_apm/grape.rb +48 -0
- data/lib/elastic_apm/instrumenter.rb +77 -4
- data/lib/elastic_apm/logging.rb +0 -2
- data/lib/elastic_apm/metrics.rb +39 -26
- data/lib/elastic_apm/metrics/breakdown_set.rb +14 -0
- data/lib/elastic_apm/metrics/{cpu_mem.rb → cpu_mem_set.rb} +62 -54
- data/lib/elastic_apm/metrics/metric.rb +117 -0
- data/lib/elastic_apm/metrics/set.rb +106 -0
- data/lib/elastic_apm/metrics/span_scoped_set.rb +39 -0
- data/lib/elastic_apm/metrics/transaction_set.rb +11 -0
- data/lib/elastic_apm/metrics/vm_set.rb +44 -0
- data/lib/elastic_apm/metricset.rb +31 -4
- data/lib/elastic_apm/normalizers.rb +6 -0
- data/lib/elastic_apm/normalizers/grape.rb +5 -0
- data/lib/elastic_apm/normalizers/grape/endpoint_run.rb +47 -0
- data/lib/elastic_apm/normalizers/rails/active_record.rb +16 -5
- data/lib/elastic_apm/opentracing.rb +4 -4
- data/lib/elastic_apm/rails.rb +12 -2
- data/lib/elastic_apm/railtie.rb +1 -5
- data/lib/elastic_apm/sinatra.rb +1 -1
- data/lib/elastic_apm/span.rb +15 -10
- data/lib/elastic_apm/spies.rb +0 -1
- data/lib/elastic_apm/sql_summarizer.rb +8 -6
- data/lib/elastic_apm/subscriber.rb +4 -1
- data/lib/elastic_apm/transaction.rb +6 -6
- data/lib/elastic_apm/transport/base.rb +7 -0
- data/lib/elastic_apm/transport/connection.rb +11 -69
- data/lib/elastic_apm/transport/connection/http.rb +43 -35
- data/lib/elastic_apm/transport/connection/proxy_pipe.rb +0 -3
- data/lib/elastic_apm/transport/headers.rb +62 -0
- data/lib/elastic_apm/transport/serializers.rb +0 -2
- data/lib/elastic_apm/transport/serializers/metricset_serializer.rb +19 -6
- data/lib/elastic_apm/transport/serializers/span_serializer.rb +3 -3
- data/lib/elastic_apm/transport/user_agent.rb +31 -0
- data/lib/elastic_apm/transport/worker.rb +1 -2
- data/lib/elastic_apm/version.rb +1 -1
- metadata +20 -6
- data/lib/elastic_apm/metrics/vm.rb +0 -60
- data/lib/elastic_apm/util/prefixed_logger.rb +0 -18
| @@ -67,7 +67,7 @@ module ElasticAPM | |
| 67 67 |  | 
| 68 68 | 
             
                  # rubocop:disable Metrics/MethodLength
         | 
| 69 69 | 
             
                  def finish(clock_end: Util.monotonic_micros, end_time: nil)
         | 
| 70 | 
            -
                    return unless ( | 
| 70 | 
            +
                    return unless (agent = ElasticAPM.agent)
         | 
| 71 71 |  | 
| 72 72 | 
             
                    if end_time
         | 
| 73 73 | 
             
                      warn '[ElasticAPM] DEPRECATED: Setting a custom end time as a ' \
         | 
| @@ -79,12 +79,12 @@ module ElasticAPM | |
| 79 79 |  | 
| 80 80 | 
             
                    case elastic_span
         | 
| 81 81 | 
             
                    when ElasticAPM::Transaction
         | 
| 82 | 
            -
                      instrumenter.current_transaction = nil
         | 
| 82 | 
            +
                      agent.instrumenter.current_transaction = nil
         | 
| 83 83 | 
             
                    when ElasticAPM::Span
         | 
| 84 | 
            -
                      instrumenter.current_spans.delete(elastic_span)
         | 
| 84 | 
            +
                      agent.instrumenter.current_spans.delete(elastic_span)
         | 
| 85 85 | 
             
                    end
         | 
| 86 86 |  | 
| 87 | 
            -
                     | 
| 87 | 
            +
                    agent.enqueue elastic_span
         | 
| 88 88 | 
             
                  end
         | 
| 89 89 | 
             
                  # rubocop:enable Metrics/MethodLength
         | 
| 90 90 |  | 
    
        data/lib/elastic_apm/rails.rb
    CHANGED
    
    | @@ -1,5 +1,6 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require 'elastic_apm/railtie'
         | 
| 3 4 | 
             
            require 'elastic_apm/subscriber'
         | 
| 4 5 | 
             
            require 'elastic_apm/normalizers/rails'
         | 
| 5 6 |  | 
| @@ -8,6 +9,7 @@ module ElasticAPM | |
| 8 9 | 
             
              # It is recommended to use the Railtie instead.
         | 
| 9 10 | 
             
              module Rails
         | 
| 10 11 | 
             
                extend self
         | 
| 12 | 
            +
             | 
| 11 13 | 
             
                # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
         | 
| 12 14 | 
             
                # rubocop:disable Metrics/CyclomaticComplexity
         | 
| 13 15 | 
             
                # Start the ElasticAPM agent and hook into Rails.
         | 
| @@ -17,11 +19,13 @@ module ElasticAPM | |
| 17 19 | 
             
                # @return [true, nil] true if the agent was started, nil otherwise.
         | 
| 18 20 | 
             
                def start(config)
         | 
| 19 21 | 
             
                  config = Config.new(config) unless config.is_a?(Config)
         | 
| 22 | 
            +
             | 
| 20 23 | 
             
                  if (reason = should_skip?(config))
         | 
| 21 24 | 
             
                    unless config.disable_start_message?
         | 
| 22 25 | 
             
                      config.logger.info "Skipping because: #{reason}. " \
         | 
| 23 26 | 
             
                        "Start manually with `ElasticAPM.start'"
         | 
| 24 27 | 
             
                    end
         | 
| 28 | 
            +
             | 
| 25 29 | 
             
                    return
         | 
| 26 30 | 
             
                  end
         | 
| 27 31 |  | 
| @@ -35,10 +39,16 @@ module ElasticAPM | |
| 35 39 | 
             
                     )
         | 
| 36 40 | 
             
                    require 'elastic_apm/spies/action_dispatch'
         | 
| 37 41 | 
             
                  end
         | 
| 42 | 
            +
             | 
| 38 43 | 
             
                  ElasticAPM.running?
         | 
| 39 44 | 
             
                rescue StandardError => e
         | 
| 40 | 
            -
                  config. | 
| 41 | 
            -
             | 
| 45 | 
            +
                  if config.disable_start_message?
         | 
| 46 | 
            +
                    config.logger.error format('Failed to start: %s', e.message)
         | 
| 47 | 
            +
                    config.logger.debug "Backtrace:\n" + e.backtrace.join("\n")
         | 
| 48 | 
            +
                  else
         | 
| 49 | 
            +
                    puts format('Failed to start: %s', e.message)
         | 
| 50 | 
            +
                    puts "Backtrace:\n" + e.backtrace.join("\n")
         | 
| 51 | 
            +
                  end
         | 
| 42 52 | 
             
                end
         | 
| 43 53 | 
             
                # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
         | 
| 44 54 | 
             
                # rubocop:enable Metrics/CyclomaticComplexity
         | 
    
        data/lib/elastic_apm/railtie.rb
    CHANGED
    
    | @@ -1,7 +1,5 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require 'elastic_apm/rails'
         | 
| 4 | 
            -
             | 
| 5 3 | 
             
            module ElasticAPM
         | 
| 6 4 | 
             
              # @api private
         | 
| 7 5 | 
             
              class Railtie < ::Rails::Railtie
         | 
| @@ -13,9 +11,7 @@ module ElasticAPM | |
| 13 11 | 
             
                end
         | 
| 14 12 |  | 
| 15 13 | 
             
                initializer 'elastic_apm.initialize' do |app|
         | 
| 16 | 
            -
                  config = Config.new(app.config.elastic_apm).tap do |c|
         | 
| 17 | 
            -
                    c.app = app
         | 
| 18 | 
            -
             | 
| 14 | 
            +
                  config = Config.new(app.config.elastic_apm.merge(app: app)).tap do |c|
         | 
| 19 15 | 
             
                    # Prepend Rails.root to log_path if present
         | 
| 20 16 | 
             
                    if c.log_path && !c.log_path.start_with?('/')
         | 
| 21 17 | 
             
                      c.log_path = ::Rails.root.join(c.log_path)
         | 
    
        data/lib/elastic_apm/sinatra.rb
    CHANGED
    
    | @@ -9,7 +9,7 @@ module ElasticAPM | |
| 9 9 | 
             
                # @param app [Sinatra::Base] A Sinatra app.
         | 
| 10 10 | 
             
                # @param config [Config, Hash] An instance of Config or a Hash config.
         | 
| 11 11 | 
             
                # @return [true, nil] true if the agent was started, nil otherwise.
         | 
| 12 | 
            -
                def start(app, config)
         | 
| 12 | 
            +
                def start(app, config = {})
         | 
| 13 13 | 
             
                  config = Config.new(config) unless config.is_a?(Config)
         | 
| 14 14 | 
             
                  configure_app(app, config)
         | 
| 15 15 |  | 
    
        data/lib/elastic_apm/span.rb
    CHANGED
    
    | @@ -1,24 +1,21 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require 'securerandom'
         | 
| 4 | 
            -
            require 'forwardable'
         | 
| 5 | 
            -
             | 
| 6 3 | 
             
            require 'elastic_apm/span/context'
         | 
| 7 4 |  | 
| 8 5 | 
             
            module ElasticAPM
         | 
| 9 6 | 
             
              # @api private
         | 
| 10 7 | 
             
              class Span
         | 
| 11 8 | 
             
                extend Forwardable
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                def_delegators :@trace_context, :trace_id, :parent_id, :id
         | 
| 9 | 
            +
                include ChildDurations::Methods
         | 
| 14 10 |  | 
| 15 11 | 
             
                DEFAULT_TYPE = 'custom'
         | 
| 16 12 |  | 
| 17 13 | 
             
                # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
         | 
| 18 14 | 
             
                def initialize(
         | 
| 19 15 | 
             
                  name:,
         | 
| 20 | 
            -
                   | 
| 16 | 
            +
                  transaction:,
         | 
| 21 17 | 
             
                  trace_context:,
         | 
| 18 | 
            +
                  parent:,
         | 
| 22 19 | 
             
                  type: nil,
         | 
| 23 20 | 
             
                  subtype: nil,
         | 
| 24 21 | 
             
                  action: nil,
         | 
| @@ -35,14 +32,17 @@ module ElasticAPM | |
| 35 32 | 
             
                    @action = action
         | 
| 36 33 | 
             
                  end
         | 
| 37 34 |  | 
| 38 | 
            -
                  @ | 
| 39 | 
            -
                  @ | 
| 35 | 
            +
                  @transaction = transaction
         | 
| 36 | 
            +
                  @parent = parent
         | 
| 37 | 
            +
                  @trace_context = trace_context || parent.trace_context.child
         | 
| 40 38 |  | 
| 41 39 | 
             
                  @context = context || Span::Context.new
         | 
| 42 40 | 
             
                  @stacktrace_builder = stacktrace_builder
         | 
| 43 41 | 
             
                end
         | 
| 44 42 | 
             
                # rubocop:enable Metrics/ParameterLists, Metrics/MethodLength
         | 
| 45 43 |  | 
| 44 | 
            +
                def_delegators :@trace_context, :trace_id, :parent_id, :id
         | 
| 45 | 
            +
             | 
| 46 46 | 
             
                attr_accessor(
         | 
| 47 47 | 
             
                  :action,
         | 
| 48 48 | 
             
                  :name,
         | 
| @@ -54,9 +54,11 @@ module ElasticAPM | |
| 54 54 | 
             
                attr_reader(
         | 
| 55 55 | 
             
                  :context,
         | 
| 56 56 | 
             
                  :duration,
         | 
| 57 | 
            +
                  :parent,
         | 
| 58 | 
            +
                  :self_time,
         | 
| 57 59 | 
             
                  :stacktrace,
         | 
| 58 60 | 
             
                  :timestamp,
         | 
| 59 | 
            -
                  : | 
| 61 | 
            +
                  :transaction
         | 
| 60 62 | 
             
                )
         | 
| 61 63 |  | 
| 62 64 | 
             
                # life cycle
         | 
| @@ -64,11 +66,14 @@ module ElasticAPM | |
| 64 66 | 
             
                def start(clock_start = Util.monotonic_micros)
         | 
| 65 67 | 
             
                  @timestamp = Util.micros
         | 
| 66 68 | 
             
                  @clock_start = clock_start
         | 
| 69 | 
            +
                  @parent.child_started
         | 
| 67 70 | 
             
                  self
         | 
| 68 71 | 
             
                end
         | 
| 69 72 |  | 
| 70 73 | 
             
                def stop(clock_end = Util.monotonic_micros)
         | 
| 71 74 | 
             
                  @duration ||= (clock_end - @clock_start)
         | 
| 75 | 
            +
                  @parent.child_stopped
         | 
| 76 | 
            +
                  @self_time = @duration - child_durations.duration
         | 
| 72 77 | 
             
                  self
         | 
| 73 78 | 
             
                end
         | 
| 74 79 |  | 
| @@ -96,7 +101,7 @@ module ElasticAPM | |
| 96 101 | 
             
                # relations
         | 
| 97 102 |  | 
| 98 103 | 
             
                def inspect
         | 
| 99 | 
            -
                  "<ElasticAPM::Span id:#{id}" \
         | 
| 104 | 
            +
                  "<ElasticAPM::Span id:#{trace_context&.id}" \
         | 
| 100 105 | 
             
                    " name:#{name.inspect}" \
         | 
| 101 106 | 
             
                    " type:#{type.inspect}" \
         | 
| 102 107 | 
             
                    '>'
         | 
    
        data/lib/elastic_apm/spies.rb
    CHANGED
    
    
| @@ -9,21 +9,23 @@ module ElasticAPM | |
| 9 9 | 
             
                TABLE_REGEX = %{["'`]?([A-Za-z0-9_]+)["'`]?}
         | 
| 10 10 |  | 
| 11 11 | 
             
                REGEXES = {
         | 
| 12 | 
            -
                  /^BEGIN/ | 
| 13 | 
            -
                  /^COMMIT/ | 
| 14 | 
            -
                  /^SELECT .* FROM #{TABLE_REGEX}/ | 
| 15 | 
            -
                  /^INSERT INTO #{TABLE_REGEX}/ | 
| 16 | 
            -
                  /^UPDATE #{TABLE_REGEX}/ | 
| 17 | 
            -
                  /^DELETE FROM #{TABLE_REGEX}/ | 
| 12 | 
            +
                  /^BEGIN/iu => 'BEGIN',
         | 
| 13 | 
            +
                  /^COMMIT/iu => 'COMMIT',
         | 
| 14 | 
            +
                  /^SELECT .* FROM #{TABLE_REGEX}/iu => 'SELECT FROM ',
         | 
| 15 | 
            +
                  /^INSERT INTO #{TABLE_REGEX}/iu => 'INSERT INTO ',
         | 
| 16 | 
            +
                  /^UPDATE #{TABLE_REGEX}/iu => 'UPDATE ',
         | 
| 17 | 
            +
                  /^DELETE FROM #{TABLE_REGEX}/iu => 'DELETE FROM '
         | 
| 18 18 | 
             
                }.freeze
         | 
| 19 19 |  | 
| 20 20 | 
             
                FORMAT = '%s%s'
         | 
| 21 | 
            +
                UTF8 = 'UTF-8'
         | 
| 21 22 |  | 
| 22 23 | 
             
                def self.cache
         | 
| 23 24 | 
             
                  @cache ||= Util::LruCache.new
         | 
| 24 25 | 
             
                end
         | 
| 25 26 |  | 
| 26 27 | 
             
                def summarize(sql)
         | 
| 28 | 
            +
                  sql = sql.encode(UTF8, invalid: :replace, undef: :replace)
         | 
| 27 29 | 
             
                  self.class.cache[sql] ||=
         | 
| 28 30 | 
             
                    REGEXES.find do |regex, sig|
         | 
| 29 31 | 
             
                      if (match = sql[0...1000].match(regex))
         | 
| @@ -54,7 +54,7 @@ module ElasticAPM | |
| 54 54 | 
             
                end
         | 
| 55 55 | 
             
                # rubocop:enable Metrics/MethodLength
         | 
| 56 56 |  | 
| 57 | 
            -
                def finish( | 
| 57 | 
            +
                def finish(name, id, payload)
         | 
| 58 58 | 
             
                  # debug "AS::Notification#finish:#{name}:#{id}"
         | 
| 59 59 | 
             
                  return unless (transaction = @agent.current_transaction)
         | 
| 60 60 |  | 
| @@ -62,6 +62,9 @@ module ElasticAPM | |
| 62 62 | 
             
                    next unless notification.id == id
         | 
| 63 63 |  | 
| 64 64 | 
             
                    if (span = notification.span)
         | 
| 65 | 
            +
                      if @agent.config.span_frames_min_duration?
         | 
| 66 | 
            +
                        span.original_backtrace ||= @normalizers.backtrace(name, payload)
         | 
| 67 | 
            +
                      end
         | 
| 65 68 | 
             
                      @agent.end_span if span == @agent.current_span
         | 
| 66 69 | 
             
                    end
         | 
| 67 70 | 
             
                    return
         | 
| @@ -1,19 +1,17 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require 'securerandom'
         | 
| 4 | 
            -
            require 'forwardable'
         | 
| 5 | 
            -
             | 
| 6 3 | 
             
            module ElasticAPM
         | 
| 7 4 | 
             
              # @api private
         | 
| 8 5 | 
             
              class Transaction
         | 
| 9 6 | 
             
                extend Forwardable
         | 
| 7 | 
            +
                include ChildDurations::Methods
         | 
| 10 8 |  | 
| 11 9 | 
             
                def_delegators :@trace_context,
         | 
| 12 10 | 
             
                  :trace_id, :parent_id, :id, :ensure_parent_id
         | 
| 13 11 |  | 
| 14 12 | 
             
                DEFAULT_TYPE = 'custom'
         | 
| 15 13 |  | 
| 16 | 
            -
                # rubocop:disable Metrics/ | 
| 14 | 
            +
                # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
         | 
| 17 15 | 
             
                def initialize(
         | 
| 18 16 | 
             
                  name = nil,
         | 
| 19 17 | 
             
                  type = nil,
         | 
| @@ -40,12 +38,12 @@ module ElasticAPM | |
| 40 38 |  | 
| 41 39 | 
             
                  @notifications = [] # for AS::Notifications
         | 
| 42 40 | 
             
                end
         | 
| 43 | 
            -
                # rubocop:enable Metrics/ | 
| 41 | 
            +
                # rubocop:enable Metrics/MethodLength, Metrics/ParameterLists
         | 
| 44 42 |  | 
| 45 43 | 
             
                attr_accessor :name, :type, :result
         | 
| 46 44 |  | 
| 47 45 | 
             
                attr_reader :context, :duration, :started_spans, :dropped_spans,
         | 
| 48 | 
            -
                  :timestamp, :trace_context, :notifications, :config
         | 
| 46 | 
            +
                  :timestamp, :trace_context, :notifications, :self_time, :config
         | 
| 49 47 |  | 
| 50 48 | 
             
                def sampled?
         | 
| 51 49 | 
             
                  @sampled
         | 
| @@ -66,6 +64,8 @@ module ElasticAPM | |
| 66 64 | 
             
                def stop(clock_end = Util.monotonic_micros)
         | 
| 67 65 | 
             
                  raise 'Transaction not yet start' unless timestamp
         | 
| 68 66 | 
             
                  @duration = clock_end - @clock_start
         | 
| 67 | 
            +
                  @self_time = @duration - child_durations.duration
         | 
| 68 | 
            +
             | 
| 69 69 | 
             
                  self
         | 
| 70 70 | 
             
                end
         | 
| 71 71 |  | 
| @@ -1,10 +1,14 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            require 'elastic_apm/metadata'
         | 
| 4 | 
            +
            require 'elastic_apm/transport/user_agent'
         | 
| 5 | 
            +
            require 'elastic_apm/transport/headers'
         | 
| 4 6 | 
             
            require 'elastic_apm/transport/connection'
         | 
| 5 7 | 
             
            require 'elastic_apm/transport/worker'
         | 
| 6 8 | 
             
            require 'elastic_apm/transport/serializers'
         | 
| 7 9 | 
             
            require 'elastic_apm/transport/filters'
         | 
| 10 | 
            +
            require 'elastic_apm/transport/connection/http'
         | 
| 11 | 
            +
             | 
| 8 12 | 
             
            require 'elastic_apm/util/throttle'
         | 
| 9 13 |  | 
| 10 14 | 
             
            module ElasticAPM
         | 
| @@ -39,6 +43,8 @@ module ElasticAPM | |
| 39 43 |  | 
| 40 44 | 
             
                    ensure_watcher_running
         | 
| 41 45 | 
             
                    ensure_worker_count
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                    @stopped.make_false unless @stopped.false?
         | 
| 42 48 | 
             
                  end
         | 
| 43 49 |  | 
| 44 50 | 
             
                  def stop
         | 
| @@ -54,6 +60,7 @@ module ElasticAPM | |
| 54 60 | 
             
                  def submit(resource)
         | 
| 55 61 | 
             
                    if @stopped.true?
         | 
| 56 62 | 
             
                      warn '%s: Transport stopping, no new events accepted', pid_str
         | 
| 63 | 
            +
                      debug 'Dropping: %s', resource.inspect
         | 
| 57 64 | 
             
                      return false
         | 
| 58 65 | 
             
                    end
         | 
| 59 66 |  | 
| @@ -1,13 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require 'concurrent'
         | 
| 4 | 
            -
            require 'zlib'
         | 
| 5 | 
            -
             | 
| 6 | 
            -
            require 'elastic_apm/transport/connection/http'
         | 
| 7 | 
            -
             | 
| 8 3 | 
             
            module ElasticAPM
         | 
| 9 4 | 
             
              module Transport
         | 
| 10 | 
            -
                # rubocop:disable Metrics/ClassLength
         | 
| 11 5 | 
             
                # @api private
         | 
| 12 6 | 
             
                class Connection
         | 
| 13 7 | 
             
                  include Logging
         | 
| @@ -24,20 +18,14 @@ module ElasticAPM | |
| 24 18 | 
             
                  # with ongoing write requests to `http`, write and close
         | 
| 25 19 | 
             
                  # requests have to be synchronized.
         | 
| 26 20 |  | 
| 27 | 
            -
                   | 
| 28 | 
            -
                    'Content-Type' => 'application/x-ndjson',
         | 
| 29 | 
            -
                    'Transfer-Encoding' => 'chunked'
         | 
| 30 | 
            -
                  }.freeze
         | 
| 31 | 
            -
                  GZIP_HEADERS = HEADERS.merge(
         | 
| 32 | 
            -
                    'Content-Encoding' => 'gzip'
         | 
| 33 | 
            -
                  ).freeze
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                  def initialize(config, metadata)
         | 
| 21 | 
            +
                  def initialize(config)
         | 
| 36 22 | 
             
                    @config = config
         | 
| 37 | 
            -
                    @ | 
| 38 | 
            -
             | 
| 23 | 
            +
                    @metadata = JSON.fast_generate(
         | 
| 24 | 
            +
                      Serializers::MetadataSerializer.new(config).build(
         | 
| 25 | 
            +
                        Metadata.new(config)
         | 
| 26 | 
            +
                      )
         | 
| 27 | 
            +
                    )
         | 
| 39 28 | 
             
                    @url = config.server_url + '/intake/v2/events'
         | 
| 40 | 
            -
                    @ssl_context = build_ssl_context
         | 
| 41 29 | 
             
                    @mutex = Mutex.new
         | 
| 42 30 | 
             
                  end
         | 
| 43 31 |  | 
| @@ -81,9 +69,8 @@ module ElasticAPM | |
| 81 69 |  | 
| 82 70 | 
             
                  def inspect
         | 
| 83 71 | 
             
                    format(
         | 
| 84 | 
            -
                      ' | 
| 85 | 
            -
                      super.split.first,
         | 
| 86 | 
            -
                      http.closed?
         | 
| 72 | 
            +
                      '<%s url:%s closed:%s >',
         | 
| 73 | 
            +
                      super.split.first, url, http&.closed?
         | 
| 87 74 | 
             
                    )
         | 
| 88 75 | 
             
                  end
         | 
| 89 76 |  | 
| @@ -93,11 +80,9 @@ module ElasticAPM | |
| 93 80 | 
             
                    schedule_closing if @config.api_request_time
         | 
| 94 81 |  | 
| 95 82 | 
             
                    @http =
         | 
| 96 | 
            -
                      Http.open(
         | 
| 97 | 
            -
                        @ | 
| 98 | 
            -
             | 
| 99 | 
            -
                        ssl_context: @ssl_context
         | 
| 100 | 
            -
                      ).tap { |http| http.write(@metadata) }
         | 
| 83 | 
            +
                      Http.open(@config, @url).tap do |http|
         | 
| 84 | 
            +
                        http.write(@metadata)
         | 
| 85 | 
            +
                      end
         | 
| 101 86 | 
             
                  end
         | 
| 102 87 | 
             
                  # rubocop:enable
         | 
| 103 88 |  | 
| @@ -108,49 +93,6 @@ module ElasticAPM | |
| 108 93 | 
             
                        flush(:timeout)
         | 
| 109 94 | 
             
                      end
         | 
| 110 95 | 
             
                  end
         | 
| 111 | 
            -
             | 
| 112 | 
            -
                  def build_headers(metadata)
         | 
| 113 | 
            -
                    (
         | 
| 114 | 
            -
                      @config.http_compression? ? GZIP_HEADERS : HEADERS
         | 
| 115 | 
            -
                    ).dup.tap do |headers|
         | 
| 116 | 
            -
                      headers['User-Agent'] = build_user_agent(metadata)
         | 
| 117 | 
            -
             | 
| 118 | 
            -
                      if (token = @config.secret_token)
         | 
| 119 | 
            -
                        headers['Authorization'] = "Bearer #{token}"
         | 
| 120 | 
            -
                      end
         | 
| 121 | 
            -
                    end
         | 
| 122 | 
            -
                  end
         | 
| 123 | 
            -
             | 
| 124 | 
            -
                  def build_user_agent(metadata)
         | 
| 125 | 
            -
                    runtime = metadata.dig(:metadata, :service, :runtime)
         | 
| 126 | 
            -
             | 
| 127 | 
            -
                    [
         | 
| 128 | 
            -
                      "elastic-apm-ruby/#{VERSION}",
         | 
| 129 | 
            -
                      HTTP::Request::USER_AGENT,
         | 
| 130 | 
            -
                      [runtime[:name], runtime[:version]].join('/')
         | 
| 131 | 
            -
                    ].join(' ')
         | 
| 132 | 
            -
                  end
         | 
| 133 | 
            -
             | 
| 134 | 
            -
                  def build_ssl_context # rubocop:disable Metrics/MethodLength
         | 
| 135 | 
            -
                    return unless @config.use_ssl?
         | 
| 136 | 
            -
             | 
| 137 | 
            -
                    OpenSSL::SSL::SSLContext.new.tap do |context|
         | 
| 138 | 
            -
                      if @config.server_ca_cert
         | 
| 139 | 
            -
                        context.ca_file = @config.server_ca_cert
         | 
| 140 | 
            -
                      else
         | 
| 141 | 
            -
                        context.cert_store =
         | 
| 142 | 
            -
                          OpenSSL::X509::Store.new.tap(&:set_default_paths)
         | 
| 143 | 
            -
                      end
         | 
| 144 | 
            -
             | 
| 145 | 
            -
                      context.verify_mode =
         | 
| 146 | 
            -
                        if @config.verify_server_cert
         | 
| 147 | 
            -
                          OpenSSL::SSL::VERIFY_PEER
         | 
| 148 | 
            -
                        else
         | 
| 149 | 
            -
                          OpenSSL::SSL::VERIFY_NONE
         | 
| 150 | 
            -
                        end
         | 
| 151 | 
            -
                    end
         | 
| 152 | 
            -
                  end
         | 
| 153 96 | 
             
                end
         | 
| 154 | 
            -
                # rubocop:enable Metrics/ClassLength
         | 
| 155 97 | 
             
              end
         | 
| 156 98 | 
             
            end
         | 
| @@ -1,9 +1,5 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require 'http'
         | 
| 4 | 
            -
            require 'concurrent'
         | 
| 5 | 
            -
            require 'zlib'
         | 
| 6 | 
            -
             | 
| 7 3 | 
             
            require 'elastic_apm/transport/connection/proxy_pipe'
         | 
| 8 4 |  | 
| 9 5 | 
             
            module ElasticAPM
         | 
| @@ -13,23 +9,43 @@ module ElasticAPM | |
| 13 9 | 
             
                  class Http
         | 
| 14 10 | 
             
                    include Logging
         | 
| 15 11 |  | 
| 16 | 
            -
                    def initialize(config)
         | 
| 12 | 
            +
                    def initialize(config, headers: nil)
         | 
| 17 13 | 
             
                      @config = config
         | 
| 18 | 
            -
                      @ | 
| 19 | 
            -
             | 
| 20 | 
            -
                      @ | 
| 14 | 
            +
                      @headers = headers || Headers.new(config)
         | 
| 15 | 
            +
                      @client = build_client
         | 
| 16 | 
            +
                      @closed = Concurrent::AtomicBoolean.new(true)
         | 
| 21 17 | 
             
                    end
         | 
| 22 18 |  | 
| 23 | 
            -
                    def open(url | 
| 24 | 
            -
                      @ | 
| 19 | 
            +
                    def open(url)
         | 
| 20 | 
            +
                      @closed.make_false
         | 
| 21 | 
            +
                      @rd, @wr = ProxyPipe.pipe(compress: @config.http_compression?)
         | 
| 22 | 
            +
                      @request = open_request_in_thread(url)
         | 
| 25 23 | 
             
                    end
         | 
| 26 24 |  | 
| 27 | 
            -
                    def self.open(config, url | 
| 25 | 
            +
                    def self.open(config, url)
         | 
| 28 26 | 
             
                      new(config).tap do |http|
         | 
| 29 | 
            -
                        http.open(url | 
| 27 | 
            +
                        http.open(url)
         | 
| 30 28 | 
             
                      end
         | 
| 31 29 | 
             
                    end
         | 
| 32 30 |  | 
| 31 | 
            +
                    def post(url, body: nil, headers: nil)
         | 
| 32 | 
            +
                      request(:post, url, body: body, headers: headers)
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    def get(url, headers: nil)
         | 
| 36 | 
            +
                      request(:get, url, headers: headers)
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    def request(method, url, body: nil, headers: nil)
         | 
| 40 | 
            +
                      @client.send(
         | 
| 41 | 
            +
                        method,
         | 
| 42 | 
            +
                        url,
         | 
| 43 | 
            +
                        body: body,
         | 
| 44 | 
            +
                        headers: (headers ? @headers.merge(headers) : @headers).to_h,
         | 
| 45 | 
            +
                        ssl_context: @config.ssl_context
         | 
| 46 | 
            +
                      ).flush
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
             | 
| 33 49 | 
             
                    def write(str)
         | 
| 34 50 | 
             
                      @wr.write(str)
         | 
| 35 51 | 
             
                      @wr.bytes_sent
         | 
| @@ -69,23 +85,29 @@ module ElasticAPM | |
| 69 85 | 
             
                      format('[THREAD:%s]', Thread.current.object_id)
         | 
| 70 86 | 
             
                    end
         | 
| 71 87 |  | 
| 72 | 
            -
                    # rubocop:disable Metrics/ | 
| 73 | 
            -
                    def open_request_in_thread(url | 
| 74 | 
            -
                      client = build_client(headers)
         | 
| 75 | 
            -
             | 
| 88 | 
            +
                    # rubocop:disable Metrics/MethodLength
         | 
| 89 | 
            +
                    def open_request_in_thread(url)
         | 
| 76 90 | 
             
                      debug '%s: Opening new request', thread_str
         | 
| 77 91 | 
             
                      Thread.new do
         | 
| 78 92 | 
             
                        begin
         | 
| 79 | 
            -
                          post( | 
| 93 | 
            +
                          resp = post(url, body: @rd, headers: @headers.chunked.to_h)
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                          if resp&.status == 202
         | 
| 96 | 
            +
                            debug 'APM Server responded with status 202'
         | 
| 97 | 
            +
                          elsif resp
         | 
| 98 | 
            +
                            error "APM Server responded with an error:\n%p", resp.body.to_s
         | 
| 99 | 
            +
                          end
         | 
| 80 100 | 
             
                        rescue Exception => e
         | 
| 81 | 
            -
                          error | 
| 101 | 
            +
                          error(
         | 
| 102 | 
            +
                            "Couldn't establish connection to APM Server:\n%p", e.inspect
         | 
| 103 | 
            +
                          )
         | 
| 82 104 | 
             
                        end
         | 
| 83 105 | 
             
                      end
         | 
| 84 106 | 
             
                    end
         | 
| 85 | 
            -
                    # rubocop:enable Metrics/ | 
| 107 | 
            +
                    # rubocop:enable Metrics/MethodLength
         | 
| 86 108 |  | 
| 87 | 
            -
                    def build_client | 
| 88 | 
            -
                      client = HTTP.headers(headers)
         | 
| 109 | 
            +
                    def build_client
         | 
| 110 | 
            +
                      client = HTTP.headers(@headers)
         | 
| 89 111 | 
             
                      return client unless @config.proxy_address && @config.proxy_port
         | 
| 90 112 |  | 
| 91 113 | 
             
                      client.via(
         | 
| @@ -96,20 +118,6 @@ module ElasticAPM | |
| 96 118 | 
             
                        @config.proxy_headers
         | 
| 97 119 | 
             
                      )
         | 
| 98 120 | 
             
                    end
         | 
| 99 | 
            -
             | 
| 100 | 
            -
                    def post(client, url, ssl_context)
         | 
| 101 | 
            -
                      resp = client.post(
         | 
| 102 | 
            -
                        url,
         | 
| 103 | 
            -
                        body: @rd,
         | 
| 104 | 
            -
                        ssl_context: ssl_context
         | 
| 105 | 
            -
                      ).flush
         | 
| 106 | 
            -
             | 
| 107 | 
            -
                      if resp&.status == 202
         | 
| 108 | 
            -
                        debug 'APM Server responded with status 202'
         | 
| 109 | 
            -
                      elsif resp
         | 
| 110 | 
            -
                        error "APM Server responded with an error:\n%p", resp.body.to_s
         | 
| 111 | 
            -
                      end
         | 
| 112 | 
            -
                    end
         | 
| 113 121 | 
             
                  end
         | 
| 114 122 | 
             
                end
         | 
| 115 123 | 
             
              end
         |