elastic-apm 3.10.1 → 3.11.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/CHANGELOG.asciidoc +23 -0
- data/Gemfile +0 -2
- data/docs/configuration.asciidoc +1 -0
- data/lib/elastic_apm/config.rb +24 -6
- data/lib/elastic_apm/config/options.rb +2 -1
- data/lib/elastic_apm/config/round_float.rb +31 -0
- data/lib/elastic_apm/config/wildcard_pattern_list.rb +2 -0
- data/lib/elastic_apm/instrumenter.rb +10 -3
- data/lib/elastic_apm/metadata.rb +3 -1
- data/lib/elastic_apm/metadata/cloud_info.rb +128 -0
- data/lib/elastic_apm/span.rb +4 -1
- data/lib/elastic_apm/spies/delayed_job.rb +4 -2
- data/lib/elastic_apm/spies/elasticsearch.rb +7 -1
- data/lib/elastic_apm/trace_context.rb +1 -1
- data/lib/elastic_apm/trace_context/traceparent.rb +2 -4
- data/lib/elastic_apm/trace_context/tracestate.rb +112 -9
- data/lib/elastic_apm/transaction.rb +26 -5
- data/lib/elastic_apm/transport/connection.rb +1 -0
- data/lib/elastic_apm/transport/filters/hash_sanitizer.rb +9 -16
- data/lib/elastic_apm/transport/filters/secrets_filter.rb +8 -7
- data/lib/elastic_apm/transport/serializers.rb +8 -6
- data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +29 -3
- data/lib/elastic_apm/transport/serializers/span_serializer.rb +2 -1
- data/lib/elastic_apm/transport/serializers/transaction_serializer.rb +1 -0
- data/lib/elastic_apm/transport/user_agent.rb +3 -3
- data/lib/elastic_apm/transport/worker.rb +1 -0
- data/lib/elastic_apm/util.rb +2 -0
- data/lib/elastic_apm/util/precision_validator.rb +46 -0
- data/lib/elastic_apm/version.rb +1 -1
- metadata +6 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: bae79ddc57bfa8955a50f062b2305db04ed9618fa40011ec78341f62083487a3
         | 
| 4 | 
            +
              data.tar.gz: 176267b31455a0f491aaff4326e1274db3684aa878bf811b60b7f5195613a228
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: '089b563ba5cba6732bf75f6b29218e1b2cdf29f251c21c4c0abd577b4492fcea2c7ee24d5d616c6e8cec92f051cbbf551219e580846db48689fcf5c77c857b71'
         | 
| 7 | 
            +
              data.tar.gz: 28c4160d29e5af26bd388dfda7feacdbb0d484f522080e57b5aafc10041ed07b8c62b6c799a8a6676bb59fca3db643a9319fcb7afbe661788c37558454bd924c
         | 
    
        data/CHANGELOG.asciidoc
    CHANGED
    
    | @@ -35,6 +35,28 @@ endif::[] | |
| 35 35 | 
             
            [[release-notes-3.x]]
         | 
| 36 36 | 
             
            === Ruby Agent version 3.x
         | 
| 37 37 |  | 
| 38 | 
            +
            [[release-notes-3.11.0]]
         | 
| 39 | 
            +
            ==== 3.11.0 (2020-10-27)
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            [float]
         | 
| 42 | 
            +
            ===== Added
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            - Add and read sampling info from Tracestate headers {pull}858[#858]
         | 
| 45 | 
            +
            - Add information about cloud hosting environment if available {pull}871[#871]
         | 
| 46 | 
            +
             | 
| 47 | 
            +
            [float]
         | 
| 48 | 
            +
            ===== Changed
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            - Align the default value of `sanitize_field_names` with other agents {pull}867[#867]
         | 
| 51 | 
            +
            - Ensure max 4 digits of precision for `sample_rate` as per agent spec {pull}880[#880]
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            [float]
         | 
| 54 | 
            +
            ===== Fixed
         | 
| 55 | 
            +
             | 
| 56 | 
            +
            - Fix Delayed::Job class names when used through ActiveJob {pull}869[#869]
         | 
| 57 | 
            +
            - Fix Delayed::Job when run without the agent running {pull}874[#874]
         | 
| 58 | 
            +
            - Fix Kubernetes related metadata {pull}882[#882]
         | 
| 59 | 
            +
             | 
| 38 60 | 
             
            [[release-notes-3.10.1]]
         | 
| 39 61 | 
             
            ==== 3.10.1 (2020-08-26)
         | 
| 40 62 |  | 
| @@ -42,6 +64,7 @@ endif::[] | |
| 42 64 | 
             
            ===== Fixed
         | 
| 43 65 |  | 
| 44 66 | 
             
            - Remove secrets from cookies in errors {pull}863[#863]
         | 
| 67 | 
            +
            - Silence deprecation warning when setting `ignore_url_patterns` to default {pull}865[#865]
         | 
| 45 68 |  | 
| 46 69 | 
             
            [[release-notes-3.10.0]]
         | 
| 47 70 | 
             
            ==== 3.10.0 (2020-08-26)
         | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/docs/configuration.asciidoc
    CHANGED
    
    | @@ -853,6 +853,7 @@ To reduce overhead and storage requirements, you can set the sample rate to a va | |
| 853 853 | 
             
            between `0.0` and `1.0`.
         | 
| 854 854 | 
             
            We still record overall time and the result for unsampled transactions, but no
         | 
| 855 855 | 
             
            context information, tags, or spans.
         | 
| 856 | 
            +
            Note that the sample rate will be rounded to 4 digits of precision.
         | 
| 856 857 |  | 
| 857 858 | 
             
            [float]
         | 
| 858 859 | 
             
            [[config-use-experimental-sql-parser]]
         | 
    
        data/lib/elastic_apm/config.rb
    CHANGED
    
    | @@ -17,9 +17,10 @@ | |
| 17 17 |  | 
| 18 18 | 
             
            # frozen_string_literal: true
         | 
| 19 19 |  | 
| 20 | 
            -
            require 'elastic_apm/config/options'
         | 
| 21 | 
            -
            require 'elastic_apm/config/duration'
         | 
| 22 20 | 
             
            require 'elastic_apm/config/bytes'
         | 
| 21 | 
            +
            require 'elastic_apm/config/duration'
         | 
| 22 | 
            +
            require 'elastic_apm/config/options'
         | 
| 23 | 
            +
            require 'elastic_apm/config/round_float'
         | 
| 23 24 | 
             
            require 'elastic_apm/config/regexp_list'
         | 
| 24 25 | 
             
            require 'elastic_apm/config/wildcard_pattern_list'
         | 
| 25 26 |  | 
| @@ -30,6 +31,11 @@ module ElasticAPM | |
| 30 31 |  | 
| 31 32 | 
             
                DEPRECATED_OPTIONS = %i[].freeze
         | 
| 32 33 |  | 
| 34 | 
            +
                # DEPRECATED: To align with other agents, change on next major bump to:
         | 
| 35 | 
            +
                # "password, passwd, pwd, secret, *key, *token*, *session*, *credit*, *card*, authorization, set-cookie"
         | 
| 36 | 
            +
                SANITIZE_FIELD_NAMES_DEFAULT =
         | 
| 37 | 
            +
                  %w[*password* *passwd* *pwd* *secret* *key* *token* *session* *credit* *card* *authorization* *set-cookie*]
         | 
| 38 | 
            +
             | 
| 33 39 | 
             
                # rubocop:disable Metrics/LineLength, Layout/ExtraSpacing
         | 
| 34 40 | 
             
                option :config_file,                       type: :string, default: 'config/elastic_apm.yml'
         | 
| 35 41 | 
             
                option :server_url,                        type: :url,    default: 'http://localhost:8200'
         | 
| @@ -45,6 +51,7 @@ module ElasticAPM | |
| 45 51 | 
             
                option :capture_elasticsearch_queries,     type: :bool,   default: false
         | 
| 46 52 | 
             
                option :capture_env,                       type: :bool,   default: true
         | 
| 47 53 | 
             
                option :central_config,                    type: :bool,   default: true
         | 
| 54 | 
            +
                option :cloud_provider,                    type: :string, default: 'auto'
         | 
| 48 55 | 
             
                option :current_user_email_method,         type: :string, default: 'email'
         | 
| 49 56 | 
             
                option :current_user_id_method,            type: :string, default: 'id'
         | 
| 50 57 | 
             
                option :current_user_username_method,      type: :string, default: 'username'
         | 
| @@ -76,7 +83,8 @@ module ElasticAPM | |
| 76 83 | 
             
                option :proxy_port,                        type: :int
         | 
| 77 84 | 
             
                option :proxy_username,                    type: :string
         | 
| 78 85 | 
             
                option :recording,                         type: :bool,   default: true
         | 
| 79 | 
            -
                option :sanitize_field_names,              type: :list, | 
| 86 | 
            +
                option :sanitize_field_names,              type: :list,
         | 
| 87 | 
            +
                  default: SANITIZE_FIELD_NAMES_DEFAULT, converter: WildcardPatternList.new
         | 
| 80 88 | 
             
                option :server_ca_cert,                    type: :string
         | 
| 81 89 | 
             
                option :service_name,                      type: :string
         | 
| 82 90 | 
             
                option :service_node_name,                 type: :string
         | 
| @@ -89,7 +97,7 @@ module ElasticAPM | |
| 89 97 | 
             
                option :stack_trace_limit,                 type: :int,    default: 999_999
         | 
| 90 98 | 
             
                option :transaction_ignore_urls,           type: :list,   default: [],      converter: WildcardPatternList.new
         | 
| 91 99 | 
             
                option :transaction_max_spans,             type: :int,    default: 500
         | 
| 92 | 
            -
                option :transaction_sample_rate,           type: :float,  default: 1.0
         | 
| 100 | 
            +
                option :transaction_sample_rate,           type: :float,  default: 1.0,     converter: RoundFloat.new
         | 
| 93 101 | 
             
                option :use_elastic_traceparent_header,    type: :bool,   default: true
         | 
| 94 102 | 
             
                option :use_legacy_sql_parser,             type: :bool,   default: false
         | 
| 95 103 | 
             
                option :verify_server_cert,                type: :bool,   default: true
         | 
| @@ -189,6 +197,14 @@ module ElasticAPM | |
| 189 197 | 
             
                  metrics_interval > 0
         | 
| 190 198 | 
             
                end
         | 
| 191 199 |  | 
| 200 | 
            +
                # DEPRECATED: Remove this in next major version
         | 
| 201 | 
            +
                def sanitize_field_names=(value)
         | 
| 202 | 
            +
                  list = WildcardPatternList.new.call(value)
         | 
| 203 | 
            +
                  defaults = WildcardPatternList.new.call(SANITIZE_FIELD_NAMES_DEFAULT)
         | 
| 204 | 
            +
                  get(:sanitize_field_names).value =
         | 
| 205 | 
            +
                    defaults.concat(list).uniq(&:pattern) # use regex pattern for comparisons
         | 
| 206 | 
            +
                end
         | 
| 207 | 
            +
             | 
| 192 208 | 
             
                def span_frames_min_duration?
         | 
| 193 209 | 
             
                  span_frames_min_duration != 0
         | 
| 194 210 | 
             
                end
         | 
| @@ -236,8 +252,10 @@ module ElasticAPM | |
| 236 252 | 
             
                end
         | 
| 237 253 |  | 
| 238 254 | 
             
                def ignore_url_patterns=(value)
         | 
| 239 | 
            -
                   | 
| 240 | 
            -
                    ' | 
| 255 | 
            +
                  unless value == self.class.schema[:ignore_url_patterns][:default]
         | 
| 256 | 
            +
                    warn '[DEPRECATED] The option ignore_url_patterns is being removed. ' \
         | 
| 257 | 
            +
                      'Consider using transaction_ignore_urls instead.'
         | 
| 258 | 
            +
                  end
         | 
| 241 259 |  | 
| 242 260 | 
             
                  set(:ignore_url_patterns, value)
         | 
| 243 261 | 
             
                end
         | 
| @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            # Licensed to Elasticsearch B.V. under one or more contributor
         | 
| 2 | 
            +
            # license agreements. See the NOTICE file distributed with
         | 
| 3 | 
            +
            # this work for additional information regarding copyright
         | 
| 4 | 
            +
            # ownership. Elasticsearch B.V. licenses this file to you under
         | 
| 5 | 
            +
            # the Apache License, Version 2.0 (the "License"); you may
         | 
| 6 | 
            +
            # not use this file except in compliance with the License.
         | 
| 7 | 
            +
            # You may obtain a copy of the License at
         | 
| 8 | 
            +
            #
         | 
| 9 | 
            +
            #   http://www.apache.org/licenses/LICENSE-2.0
         | 
| 10 | 
            +
            #
         | 
| 11 | 
            +
            # Unless required by applicable law or agreed to in writing,
         | 
| 12 | 
            +
            # software distributed under the License is distributed on an
         | 
| 13 | 
            +
            # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
         | 
| 14 | 
            +
            # KIND, either express or implied.  See the License for the
         | 
| 15 | 
            +
            # specific language governing permissions and limitations
         | 
| 16 | 
            +
            # under the License.
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            # frozen_string_literal: true
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            require 'elastic_apm/util/precision_validator'
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            module ElasticAPM
         | 
| 23 | 
            +
              class Config
         | 
| 24 | 
            +
                # @api private
         | 
| 25 | 
            +
                class RoundFloat
         | 
| 26 | 
            +
                  def call(value)
         | 
| 27 | 
            +
                    Util::PrecisionValidator.validate(value, precision: 4, minimum: 0.0001)
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         | 
| @@ -119,7 +119,13 @@ module ElasticAPM | |
| 119 119 | 
             
                      "Already inside #{transaction.inspect}"
         | 
| 120 120 | 
             
                  end
         | 
| 121 121 |  | 
| 122 | 
            -
                   | 
| 122 | 
            +
                  if trace_context
         | 
| 123 | 
            +
                    sampled = trace_context.recorded?
         | 
| 124 | 
            +
                    sample_rate = trace_context.tracestate.sample_rate
         | 
| 125 | 
            +
                  else
         | 
| 126 | 
            +
                    sampled = random_sample?(config)
         | 
| 127 | 
            +
                    sample_rate = config.transaction_sample_rate
         | 
| 128 | 
            +
                  end
         | 
| 123 129 |  | 
| 124 130 | 
             
                  transaction =
         | 
| 125 131 | 
             
                    Transaction.new(
         | 
| @@ -128,6 +134,7 @@ module ElasticAPM | |
| 128 134 | 
             
                      context: context,
         | 
| 129 135 | 
             
                      trace_context: trace_context,
         | 
| 130 136 | 
             
                      sampled: sampled,
         | 
| 137 | 
            +
                      sample_rate: sample_rate,
         | 
| 131 138 | 
             
                      config: config
         | 
| 132 139 | 
             
                    )
         | 
| 133 140 |  | 
| @@ -259,7 +266,7 @@ module ElasticAPM | |
| 259 266 | 
             
                end
         | 
| 260 267 |  | 
| 261 268 | 
             
                def update_transaction_metrics(transaction)
         | 
| 262 | 
            -
                  return unless transaction.collect_metrics
         | 
| 269 | 
            +
                  return unless transaction.collect_metrics?
         | 
| 263 270 |  | 
| 264 271 | 
             
                  tags = {
         | 
| 265 272 | 
             
                    'transaction.name': transaction.name,
         | 
| @@ -298,7 +305,7 @@ module ElasticAPM | |
| 298 305 | 
             
                end
         | 
| 299 306 |  | 
| 300 307 | 
             
                def update_span_metrics(span)
         | 
| 301 | 
            -
                  return unless span.transaction. | 
| 308 | 
            +
                  return unless span.transaction.collect_metrics?
         | 
| 302 309 |  | 
| 303 310 | 
             
                  tags = {
         | 
| 304 311 | 
             
                    'span.type': span.type,
         | 
    
        data/lib/elastic_apm/metadata.rb
    CHANGED
    
    | @@ -25,12 +25,14 @@ module ElasticAPM | |
| 25 25 | 
             
                  @process = ProcessInfo.new(config)
         | 
| 26 26 | 
             
                  @system = SystemInfo.new(config)
         | 
| 27 27 | 
             
                  @labels = config.global_labels
         | 
| 28 | 
            +
                  @cloud = CloudInfo.new(config).fetch!
         | 
| 28 29 | 
             
                end
         | 
| 29 30 |  | 
| 30 | 
            -
                attr_reader :service, :process, :system, :labels
         | 
| 31 | 
            +
                attr_reader :service, :process, :system, :cloud, :labels
         | 
| 31 32 | 
             
              end
         | 
| 32 33 | 
             
            end
         | 
| 33 34 |  | 
| 34 35 | 
             
            require 'elastic_apm/metadata/service_info'
         | 
| 35 36 | 
             
            require 'elastic_apm/metadata/system_info'
         | 
| 36 37 | 
             
            require 'elastic_apm/metadata/process_info'
         | 
| 38 | 
            +
            require 'elastic_apm/metadata/cloud_info'
         | 
| @@ -0,0 +1,128 @@ | |
| 1 | 
            +
            # Licensed to Elasticsearch B.V. under one or more contributor
         | 
| 2 | 
            +
            # license agreements. See the NOTICE file distributed with
         | 
| 3 | 
            +
            # this work for additional information regarding copyright
         | 
| 4 | 
            +
            # ownership. Elasticsearch B.V. licenses this file to you under
         | 
| 5 | 
            +
            # the Apache License, Version 2.0 (the "License"); you may
         | 
| 6 | 
            +
            # not use this file except in compliance with the License.
         | 
| 7 | 
            +
            # You may obtain a copy of the License at
         | 
| 8 | 
            +
            #
         | 
| 9 | 
            +
            #   http://www.apache.org/licenses/LICENSE-2.0
         | 
| 10 | 
            +
            #
         | 
| 11 | 
            +
            # Unless required by applicable law or agreed to in writing,
         | 
| 12 | 
            +
            # software distributed under the License is distributed on an
         | 
| 13 | 
            +
            # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
         | 
| 14 | 
            +
            # KIND, either express or implied.  See the License for the
         | 
| 15 | 
            +
            # specific language governing permissions and limitations
         | 
| 16 | 
            +
            # under the License.
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            # frozen_string_literal: true
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            require "http"
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            module ElasticAPM
         | 
| 23 | 
            +
              class Metadata
         | 
| 24 | 
            +
                # @api private
         | 
| 25 | 
            +
                class CloudInfo
         | 
| 26 | 
            +
                  include Logging
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  AWS_URI = "http://169.254.169.254/latest/dynamic/instance-identity/document"
         | 
| 29 | 
            +
                  GCP_URI = "http://metadata.google.internal/computeMetadata/v1/?recursive=true"
         | 
| 30 | 
            +
                  AZURE_URI = "http://169.254.169.254/metadata/instance/compute?api-version=2019-08-15"
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def initialize(config)
         | 
| 33 | 
            +
                    @config = config
         | 
| 34 | 
            +
                    @client = HTTP.timeout(0.1)
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  attr_reader :config
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  attr_accessor(
         | 
| 40 | 
            +
                    :account_id,
         | 
| 41 | 
            +
                    :account_name,
         | 
| 42 | 
            +
                    :instance_id,
         | 
| 43 | 
            +
                    :instance_name,
         | 
| 44 | 
            +
                    :machine_type,
         | 
| 45 | 
            +
                    :project_id,
         | 
| 46 | 
            +
                    :project_name,
         | 
| 47 | 
            +
                    :availability_zone,
         | 
| 48 | 
            +
                    :provider,
         | 
| 49 | 
            +
                    :region
         | 
| 50 | 
            +
                  )
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  def fetch!
         | 
| 53 | 
            +
                    case config.cloud_provider
         | 
| 54 | 
            +
                    when "aws"
         | 
| 55 | 
            +
                      fetch_aws
         | 
| 56 | 
            +
                    when "gcp"
         | 
| 57 | 
            +
                      fetch_gcp
         | 
| 58 | 
            +
                    when "azure"
         | 
| 59 | 
            +
                      fetch_azure
         | 
| 60 | 
            +
                    when "auto"
         | 
| 61 | 
            +
                      fetch_aws || fetch_gcp || fetch_azure
         | 
| 62 | 
            +
                    when "none"
         | 
| 63 | 
            +
                      nil
         | 
| 64 | 
            +
                    else
         | 
| 65 | 
            +
                      error("Unknown setting for cloud_provider '#{config.cloud_provider}'")
         | 
| 66 | 
            +
                    end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                    self
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  private
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  def fetch_aws
         | 
| 74 | 
            +
                    resp = @client.get(AWS_URI)
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                    return unless resp.status === 200
         | 
| 77 | 
            +
                    return unless (metadata = JSON.parse(resp.body))
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                    self.provider = "aws"
         | 
| 80 | 
            +
                    self.account_id = metadata["accountId"]
         | 
| 81 | 
            +
                    self.instance_id = metadata["instanceId"]
         | 
| 82 | 
            +
                    self.availability_zone = metadata["availabilityZone"]
         | 
| 83 | 
            +
                    self.machine_type = metadata["instanceType"]
         | 
| 84 | 
            +
                    self.region = metadata["region"]
         | 
| 85 | 
            +
                  rescue HTTP::TimeoutError, HTTP::ConnectionError
         | 
| 86 | 
            +
                    nil
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  def fetch_gcp
         | 
| 90 | 
            +
                    resp = @client.headers("Metadata-Flavor" => "Google").get(GCP_URI)
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    return unless resp.status === 200
         | 
| 93 | 
            +
                    return unless (metadata = JSON.parse(resp.body))
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                    zone = metadata["instance"]["zone"]&.split("/")&.at(-1)
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                    self.provider = "gcp"
         | 
| 98 | 
            +
                    self.instance_id = metadata["instance"]["id"]
         | 
| 99 | 
            +
                    self.instance_name = metadata["instance"]["name"]
         | 
| 100 | 
            +
                    self.project_id = metadata["project"]["numericProjectId"]
         | 
| 101 | 
            +
                    self.project_name = metadata["project"]["projectId"]
         | 
| 102 | 
            +
                    self.availability_zone = zone
         | 
| 103 | 
            +
                    self.region = zone.split("-")[0..-2].join("-")
         | 
| 104 | 
            +
                    self.machine_type = metadata["instance"]["machineType"]
         | 
| 105 | 
            +
                  rescue HTTP::TimeoutError, HTTP::ConnectionError
         | 
| 106 | 
            +
                    nil
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                  def fetch_azure
         | 
| 110 | 
            +
                    resp = @client.headers("Metadata" => "true").get(AZURE_URI)
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                    return unless resp.status === 200
         | 
| 113 | 
            +
                    return unless (metadata = JSON.parse(resp.body))
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                    self.provider = 'azure'
         | 
| 116 | 
            +
                    self.account_id = metadata["subscriptionId"]
         | 
| 117 | 
            +
                    self.instance_id = metadata["vmId"]
         | 
| 118 | 
            +
                    self.instance_name = metadata["name"]
         | 
| 119 | 
            +
                    self.project_name = metadata["resourceGroupName"]
         | 
| 120 | 
            +
                    self.availability_zone = metadata["zone"]
         | 
| 121 | 
            +
                    self.machine_type = metadata["vmSize"]
         | 
| 122 | 
            +
                    self.region = metadata["location"]
         | 
| 123 | 
            +
                  rescue HTTP::TimeoutError, HTTP::ConnectionError
         | 
| 124 | 
            +
                    nil
         | 
| 125 | 
            +
                  end
         | 
| 126 | 
            +
                end
         | 
| 127 | 
            +
              end
         | 
| 128 | 
            +
            end
         | 
    
        data/lib/elastic_apm/span.rb
    CHANGED
    
    | @@ -38,7 +38,8 @@ module ElasticAPM | |
| 38 38 | 
             
                  action: nil,
         | 
| 39 39 | 
             
                  context: nil,
         | 
| 40 40 | 
             
                  stacktrace_builder: nil,
         | 
| 41 | 
            -
                  sync: nil
         | 
| 41 | 
            +
                  sync: nil,
         | 
| 42 | 
            +
                  sample_rate: nil
         | 
| 42 43 | 
             
                )
         | 
| 43 44 | 
             
                  @name = name
         | 
| 44 45 |  | 
| @@ -53,6 +54,7 @@ module ElasticAPM | |
| 53 54 | 
             
                  @transaction = transaction
         | 
| 54 55 | 
             
                  @parent = parent
         | 
| 55 56 | 
             
                  @trace_context = trace_context || parent.trace_context.child
         | 
| 57 | 
            +
                  @sample_rate = transaction.sample_rate
         | 
| 56 58 |  | 
| 57 59 | 
             
                  @context = context || Span::Context.new(sync: sync)
         | 
| 58 60 | 
             
                  @stacktrace_builder = stacktrace_builder
         | 
| @@ -73,6 +75,7 @@ module ElasticAPM | |
| 73 75 | 
             
                  :context,
         | 
| 74 76 | 
             
                  :duration,
         | 
| 75 77 | 
             
                  :parent,
         | 
| 78 | 
            +
                  :sample_rate,
         | 
| 76 79 | 
             
                  :self_time,
         | 
| 77 80 | 
             
                  :stacktrace,
         | 
| 78 81 | 
             
                  :timestamp,
         | 
| @@ -41,10 +41,10 @@ module ElasticAPM | |
| 41 41 | 
             
                    job_name = name_from_payload(job.payload_object)
         | 
| 42 42 | 
             
                    transaction = ElasticAPM.start_transaction(job_name, TYPE)
         | 
| 43 43 | 
             
                    job.invoke_job_without_apm(*args, &block)
         | 
| 44 | 
            -
                    transaction | 
| 44 | 
            +
                    transaction&.done 'success'
         | 
| 45 45 | 
             
                  rescue ::Exception => e
         | 
| 46 46 | 
             
                    ElasticAPM.report(e, handled: false)
         | 
| 47 | 
            -
                    transaction | 
| 47 | 
            +
                    transaction&.done 'error'
         | 
| 48 48 | 
             
                    raise
         | 
| 49 49 | 
             
                  ensure
         | 
| 50 50 | 
             
                    ElasticAPM.end_transaction
         | 
| @@ -53,6 +53,8 @@ module ElasticAPM | |
| 53 53 | 
             
                  def self.name_from_payload(payload_object)
         | 
| 54 54 | 
             
                    if payload_object.is_a?(::Delayed::PerformableMethod)
         | 
| 55 55 | 
             
                      performable_method_name(payload_object)
         | 
| 56 | 
            +
                    elsif payload_object.class.name == 'ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper'
         | 
| 57 | 
            +
                      payload_object.job_data['job_class']
         | 
| 56 58 | 
             
                    else
         | 
| 57 59 | 
             
                      payload_object.class.name
         | 
| 58 60 | 
             
                    end
         | 
| @@ -27,7 +27,13 @@ module ElasticAPM | |
| 27 27 | 
             
                  SUBTYPE = 'elasticsearch'
         | 
| 28 28 |  | 
| 29 29 | 
             
                  def self.sanitizer
         | 
| 30 | 
            -
                    @sanitizer ||= | 
| 30 | 
            +
                    @sanitizer ||=
         | 
| 31 | 
            +
                      begin
         | 
| 32 | 
            +
                        config = ElasticAPM.agent.config
         | 
| 33 | 
            +
                        ElasticAPM::Transport::Filters::HashSanitizer.new(
         | 
| 34 | 
            +
                          key_patterns: config.custom_key_filters + config.sanitize_field_names
         | 
| 35 | 
            +
                        )
         | 
| 36 | 
            +
                      end
         | 
| 31 37 | 
             
                  end
         | 
| 32 38 |  | 
| 33 39 | 
             
                  def install
         | 
| @@ -33,8 +33,7 @@ module ElasticAPM | |
| 33 33 | 
             
                    trace_id: nil,
         | 
| 34 34 | 
             
                    span_id: nil,
         | 
| 35 35 | 
             
                    id: nil,
         | 
| 36 | 
            -
                    recorded: true | 
| 37 | 
            -
                    tracestate: nil
         | 
| 36 | 
            +
                    recorded: true
         | 
| 38 37 | 
             
                  )
         | 
| 39 38 | 
             
                    @version = version
         | 
| 40 39 | 
             
                    @trace_id = trace_id || hex(TRACE_ID_LENGTH)
         | 
| @@ -42,11 +41,10 @@ module ElasticAPM | |
| 42 41 | 
             
                    @parent_id = span_id
         | 
| 43 42 | 
             
                    @id = id || hex(ID_LENGTH)
         | 
| 44 43 | 
             
                    @recorded = recorded
         | 
| 45 | 
            -
                    @tracestate = tracestate
         | 
| 46 44 | 
             
                  end
         | 
| 47 45 | 
             
                  # rubocop:enable Metrics/ParameterLists
         | 
| 48 46 |  | 
| 49 | 
            -
                  attr_accessor :version, :id, :trace_id, :parent_id, :recorded | 
| 47 | 
            +
                  attr_accessor :version, :id, :trace_id, :parent_id, :recorded
         | 
| 50 48 |  | 
| 51 49 | 
             
                  alias :recorded? :recorded
         | 
| 52 50 |  | 
| @@ -17,26 +17,129 @@ | |
| 17 17 |  | 
| 18 18 | 
             
            # frozen_string_literal: true
         | 
| 19 19 |  | 
| 20 | 
            +
            require 'elastic_apm/util/precision_validator'
         | 
| 21 | 
            +
             | 
| 20 22 | 
             
            module ElasticAPM
         | 
| 21 23 | 
             
              class TraceContext
         | 
| 22 24 | 
             
                # @api private
         | 
| 23 25 | 
             
                class Tracestate
         | 
| 24 | 
            -
                   | 
| 25 | 
            -
             | 
| 26 | 
            +
                  # @api private
         | 
| 27 | 
            +
                  class Entry
         | 
| 28 | 
            +
                    def initialize(key, value)
         | 
| 29 | 
            +
                      @key = key
         | 
| 30 | 
            +
                      @value = value
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    attr_reader :key, :value
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    def to_s
         | 
| 36 | 
            +
                      "#{key}=#{value}"
         | 
| 37 | 
            +
                    end
         | 
| 26 38 | 
             
                  end
         | 
| 27 39 |  | 
| 28 | 
            -
                   | 
| 40 | 
            +
                  class EsEntry
         | 
| 41 | 
            +
                    ASSIGN = ':'
         | 
| 42 | 
            +
                    SPLIT = ';'
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    SHORT_TO_LONG = { 's' => 'sample_rate' }
         | 
| 45 | 
            +
                    LONG_TO_SHORT = { 'sample_rate' => 's' }
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                    def initialize(values = nil)
         | 
| 48 | 
            +
                      parse(values)
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    attr_reader :sample_rate
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    def key
         | 
| 54 | 
            +
                      'es'
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    def value
         | 
| 58 | 
            +
                      LONG_TO_SHORT.map do |l, s|
         | 
| 59 | 
            +
                        "#{s}#{ASSIGN}#{send(l)}"
         | 
| 60 | 
            +
                      end.join(SPLIT)
         | 
| 61 | 
            +
                    end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                    def empty?
         | 
| 64 | 
            +
                      !sample_rate
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    def sample_rate=(val)
         | 
| 68 | 
            +
                      @sample_rate = Util::PrecisionValidator.validate(
         | 
| 69 | 
            +
                        val, precision: 4, minimum: 0.0001
         | 
| 70 | 
            +
                      )
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                    def to_s
         | 
| 74 | 
            +
                      return nil if empty?
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                      "es=#{value}"
         | 
| 77 | 
            +
                    end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                    private
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                    def parse(values)
         | 
| 82 | 
            +
                      return unless values
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                      values.split(SPLIT).map do |kv|
         | 
| 85 | 
            +
                        k, v = kv.split(ASSIGN)
         | 
| 86 | 
            +
                        next unless SHORT_TO_LONG.keys.include?(k)
         | 
| 87 | 
            +
                        send("#{SHORT_TO_LONG[k]}=", v)
         | 
| 88 | 
            +
                      end
         | 
| 89 | 
            +
                    end
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  extend Forwardable
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                  def initialize(entries: {}, sample_rate: nil)
         | 
| 95 | 
            +
                    @entries = entries
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                    self.sample_rate = sample_rate if sample_rate
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                  attr_accessor :entries
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                  def_delegators :es_entry, :sample_rate, :sample_rate=
         | 
| 29 103 |  | 
| 30 104 | 
             
                  def self.parse(header)
         | 
| 31 | 
            -
                     | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 105 | 
            +
                    entries =
         | 
| 106 | 
            +
                      split_by_nl_and_comma(header)
         | 
| 107 | 
            +
                      .each_with_object({}) do |entry, hsh|
         | 
| 108 | 
            +
                        k, v = entry.split('=')
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                        hsh[k] =
         | 
| 111 | 
            +
                          case k
         | 
| 112 | 
            +
                          when 'es' then EsEntry.new(v)
         | 
| 113 | 
            +
                          else Entry.new(k, v)
         | 
| 114 | 
            +
                          end
         | 
| 115 | 
            +
                      end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                    new(entries: entries)
         | 
| 36 118 | 
             
                  end
         | 
| 37 119 |  | 
| 38 120 | 
             
                  def to_header
         | 
| 39 | 
            -
                     | 
| 121 | 
            +
                    return "" unless entries.any?
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                    entries.values.map(&:to_s).join(',')
         | 
| 124 | 
            +
                  end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                  private
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                  def es_entry
         | 
| 129 | 
            +
                    # lazy generate this so we only add it if necessary
         | 
| 130 | 
            +
                    entries['es'] ||= EsEntry.new
         | 
| 131 | 
            +
                  end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                  class << self
         | 
| 134 | 
            +
                    private
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                    def split_by_nl_and_comma(str)
         | 
| 137 | 
            +
                      # HTTP allows multiple headers with the same name, eg. multiple
         | 
| 138 | 
            +
                      # Set-Cookie headers per response.
         | 
| 139 | 
            +
                      # Rack handles this by joining the headers under the same key, separated
         | 
| 140 | 
            +
                      # by newlines, see https://www.rubydoc.info/github/rack/rack/file/SPEC
         | 
| 141 | 
            +
                      String(str).split("\n").map { |s| s.split(',') }.flatten
         | 
| 142 | 
            +
                    end
         | 
| 40 143 | 
             
                  end
         | 
| 41 144 | 
             
                end
         | 
| 42 145 | 
             
              end
         | 
| @@ -34,6 +34,7 @@ module ElasticAPM | |
| 34 34 | 
             
                  name = nil,
         | 
| 35 35 | 
             
                  type = nil,
         | 
| 36 36 | 
             
                  sampled: true,
         | 
| 37 | 
            +
                  sample_rate: 1,
         | 
| 37 38 | 
             
                  context: nil,
         | 
| 38 39 | 
             
                  config:,
         | 
| 39 40 | 
             
                  trace_context: nil
         | 
| @@ -52,13 +53,19 @@ module ElasticAPM | |
| 52 53 | 
             
                  @default_labels = config.default_labels
         | 
| 53 54 |  | 
| 54 55 | 
             
                  @sampled = sampled
         | 
| 56 | 
            +
                  @sample_rate = sample_rate
         | 
| 55 57 |  | 
| 56 58 | 
             
                  @context = context || Context.new # TODO: Lazy generate this?
         | 
| 57 59 | 
             
                  if @default_labels
         | 
| 58 60 | 
             
                    Util.reverse_merge!(@context.labels, @default_labels)
         | 
| 59 61 | 
             
                  end
         | 
| 60 62 |  | 
| 61 | 
            -
                  @trace_context = trace_context | 
| 63 | 
            +
                  unless (@trace_context = trace_context)
         | 
| 64 | 
            +
                    @trace_context = TraceContext.new(
         | 
| 65 | 
            +
                      traceparent: TraceContext::Traceparent.new(recorded: sampled),
         | 
| 66 | 
            +
                      tracestate: TraceContext::Tracestate.new(sample_rate: sampled ? sample_rate : 0)
         | 
| 67 | 
            +
                    )
         | 
| 68 | 
            +
                  end
         | 
| 62 69 |  | 
| 63 70 | 
             
                  @started_spans = 0
         | 
| 64 71 | 
             
                  @dropped_spans = 0
         | 
| @@ -69,10 +76,24 @@ module ElasticAPM | |
| 69 76 |  | 
| 70 77 | 
             
                attr_accessor :name, :type, :result
         | 
| 71 78 |  | 
| 72 | 
            -
                attr_reader | 
| 73 | 
            -
                  : | 
| 74 | 
            -
                  : | 
| 75 | 
            -
                  : | 
| 79 | 
            +
                attr_reader(
         | 
| 80 | 
            +
                  :breakdown_metrics,
         | 
| 81 | 
            +
                  :collect_metrics,
         | 
| 82 | 
            +
                  :context,
         | 
| 83 | 
            +
                  :dropped_spans,
         | 
| 84 | 
            +
                  :duration,
         | 
| 85 | 
            +
                  :framework_name,
         | 
| 86 | 
            +
                  :notifications,
         | 
| 87 | 
            +
                  :self_time,
         | 
| 88 | 
            +
                  :sample_rate,
         | 
| 89 | 
            +
                  :span_frames_min_duration,
         | 
| 90 | 
            +
                  :started_spans,
         | 
| 91 | 
            +
                  :timestamp,
         | 
| 92 | 
            +
                  :trace_context,
         | 
| 93 | 
            +
                  :transaction_max_spans 
         | 
| 94 | 
            +
                )
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                alias :collect_metrics? :collect_metrics
         | 
| 76 97 |  | 
| 77 98 | 
             
                def sampled?
         | 
| 78 99 | 
             
                  @sampled
         | 
| @@ -23,29 +23,22 @@ module ElasticAPM | |
| 23 23 | 
             
                  class HashSanitizer
         | 
| 24 24 | 
             
                    FILTERED = '[FILTERED]'
         | 
| 25 25 |  | 
| 26 | 
            -
                     | 
| 27 | 
            -
             | 
| 28 | 
            -
                      /auth/i,
         | 
| 29 | 
            -
                      /^pw$/,
         | 
| 30 | 
            -
                      /secret/i,
         | 
| 31 | 
            -
                      /token/i,
         | 
| 32 | 
            -
                      /api[-._]?key/i,
         | 
| 33 | 
            -
                      /session[-._]?id/i,
         | 
| 34 | 
            -
                      /(set[-_])?cookie/i
         | 
| 35 | 
            -
                    ].freeze
         | 
| 26 | 
            +
                    # DEPRECATED: Remove these additions in next major version
         | 
| 27 | 
            +
                    LEGACY_KEY_FILTERS = [/cookie/i, /auth/i].freeze
         | 
| 36 28 |  | 
| 29 | 
            +
                    # DEPRECATED: Remove this check in next major version
         | 
| 37 30 | 
             
                    VALUE_FILTERS = [
         | 
| 38 31 | 
             
                      # (probably) credit card number
         | 
| 39 32 | 
             
                      /^\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}$/
         | 
| 40 33 | 
             
                    ].freeze
         | 
| 41 34 |  | 
| 42 | 
            -
                     | 
| 43 | 
            -
             | 
| 44 | 
            -
                    def initialize
         | 
| 45 | 
            -
                      @key_filters = KEY_FILTERS
         | 
| 35 | 
            +
                    def initialize(key_patterns:)
         | 
| 36 | 
            +
                      @key_patterns = key_patterns + LEGACY_KEY_FILTERS
         | 
| 46 37 | 
             
                    end
         | 
| 47 38 |  | 
| 48 | 
            -
                     | 
| 39 | 
            +
                    attr_accessor :key_patterns
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    def strip_from!(obj)
         | 
| 49 42 | 
             
                      return unless obj&.is_a?(Hash)
         | 
| 50 43 |  | 
| 51 44 | 
             
                      obj.each do |k, v|
         | 
| @@ -65,7 +58,7 @@ module ElasticAPM | |
| 65 58 | 
             
                    end
         | 
| 66 59 |  | 
| 67 60 | 
             
                    def filter_key?(key)
         | 
| 68 | 
            -
                      @ | 
| 61 | 
            +
                      @key_patterns.any? { |regex| regex.match(key) }
         | 
| 69 62 | 
             
                    end
         | 
| 70 63 |  | 
| 71 64 | 
             
                    def filter_value?(value)
         | 
| @@ -26,20 +26,21 @@ module ElasticAPM | |
| 26 26 | 
             
                  class SecretsFilter
         | 
| 27 27 | 
             
                    def initialize(config)
         | 
| 28 28 | 
             
                      @config = config
         | 
| 29 | 
            -
                      @sanitizer = | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 29 | 
            +
                      @sanitizer =
         | 
| 30 | 
            +
                        HashSanitizer.new(
         | 
| 31 | 
            +
                          key_patterns: config.custom_key_filters + config.sanitize_field_names
         | 
| 32 | 
            +
                        )
         | 
| 32 33 | 
             
                    end
         | 
| 33 34 |  | 
| 34 35 | 
             
                    def call(payload)
         | 
| 35 | 
            -
                      @sanitizer.strip_from! payload.dig(:transaction, :context, :request, : | 
| 36 | 
            -
                      @sanitizer.strip_from! payload.dig(:transaction, :context, :request, :env)
         | 
| 36 | 
            +
                      @sanitizer.strip_from! payload.dig(:transaction, :context, :request, :body)
         | 
| 37 37 | 
             
                      @sanitizer.strip_from! payload.dig(:transaction, :context, :request, :cookies)
         | 
| 38 | 
            +
                      @sanitizer.strip_from! payload.dig(:transaction, :context, :request, :env)
         | 
| 39 | 
            +
                      @sanitizer.strip_from! payload.dig(:transaction, :context, :request, :headers)
         | 
| 38 40 | 
             
                      @sanitizer.strip_from! payload.dig(:transaction, :context, :response, :headers)
         | 
| 39 | 
            -
                      @sanitizer.strip_from! payload.dig(:error, :context, :request, :headers)
         | 
| 40 41 | 
             
                      @sanitizer.strip_from! payload.dig(:error, :context, :request, :cookies)
         | 
| 42 | 
            +
                      @sanitizer.strip_from! payload.dig(:error, :context, :request, :headers)
         | 
| 41 43 | 
             
                      @sanitizer.strip_from! payload.dig(:error, :context, :response, :headers)
         | 
| 42 | 
            -
                      @sanitizer.strip_from! payload.dig(:transaction, :context, :request, :body)
         | 
| 43 44 |  | 
| 44 45 | 
             
                      payload
         | 
| 45 46 | 
             
                    end
         | 
| @@ -45,18 +45,20 @@ module ElasticAPM | |
| 45 45 | 
             
                    def keyword_object(hash)
         | 
| 46 46 | 
             
                      return unless hash
         | 
| 47 47 |  | 
| 48 | 
            -
                      hash. | 
| 49 | 
            -
                         | 
| 48 | 
            +
                      hash.each do |k, v|
         | 
| 49 | 
            +
                        hash[k] =
         | 
| 50 | 
            +
                          case v
         | 
| 51 | 
            +
                          when Hash then keyword_object(v)
         | 
| 52 | 
            +
                          else keyword_field(v)
         | 
| 53 | 
            +
                          end
         | 
| 50 54 | 
             
                      end
         | 
| 51 55 | 
             
                    end
         | 
| 52 56 |  | 
| 53 57 | 
             
                    def mixed_object(hash)
         | 
| 54 58 | 
             
                      return unless hash
         | 
| 55 59 |  | 
| 56 | 
            -
                      hash. | 
| 57 | 
            -
                         | 
| 58 | 
            -
                          hash[k] = v.is_a?(String) ? keyword_field(v) : v
         | 
| 59 | 
            -
                        end
         | 
| 60 | 
            +
                      hash.each do |k, v|
         | 
| 61 | 
            +
                        hash[k] = v.is_a?(String) ? keyword_field(v) : v
         | 
| 60 62 | 
             
                      end
         | 
| 61 63 | 
             
                    end
         | 
| 62 64 | 
             
                  end
         | 
| @@ -23,14 +23,19 @@ module ElasticAPM | |
| 23 23 | 
             
                  # @api private
         | 
| 24 24 | 
             
                  class MetadataSerializer < Serializer
         | 
| 25 25 | 
             
                    def build(metadata)
         | 
| 26 | 
            -
                       | 
| 27 | 
            -
                         | 
| 26 | 
            +
                      base =
         | 
| 27 | 
            +
                        {
         | 
| 28 28 | 
             
                          service: build_service(metadata.service),
         | 
| 29 29 | 
             
                          process: build_process(metadata.process),
         | 
| 30 30 | 
             
                          system: build_system(metadata.system),
         | 
| 31 31 | 
             
                          labels: build_labels(metadata.labels)
         | 
| 32 32 | 
             
                        }
         | 
| 33 | 
            -
             | 
| 33 | 
            +
             | 
| 34 | 
            +
                      if (metadata.cloud.provider)
         | 
| 35 | 
            +
                        base[:cloud] = build_cloud(metadata.cloud)
         | 
| 36 | 
            +
                      end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                      { metadata: base }
         | 
| 34 39 | 
             
                    end
         | 
| 35 40 |  | 
| 36 41 | 
             
                    private
         | 
| @@ -83,6 +88,27 @@ module ElasticAPM | |
| 83 88 | 
             
                      }
         | 
| 84 89 | 
             
                    end
         | 
| 85 90 |  | 
| 91 | 
            +
                    def build_cloud(cloud)
         | 
| 92 | 
            +
                      {
         | 
| 93 | 
            +
                        provider: cloud.provider,
         | 
| 94 | 
            +
                        account: {
         | 
| 95 | 
            +
                          id: keyword_field(cloud.account_id),
         | 
| 96 | 
            +
                          name: keyword_field(cloud.account_name),
         | 
| 97 | 
            +
                        },
         | 
| 98 | 
            +
                        availability_zone: keyword_field(cloud.availability_zone),
         | 
| 99 | 
            +
                        instance: {
         | 
| 100 | 
            +
                          id: keyword_field(cloud.instance_id),
         | 
| 101 | 
            +
                          name: keyword_field(cloud.instance_name),
         | 
| 102 | 
            +
                        },
         | 
| 103 | 
            +
                        machine: { type: keyword_field(cloud.machine_type) },
         | 
| 104 | 
            +
                        project: {
         | 
| 105 | 
            +
                          id: keyword_field(cloud.project_id),
         | 
| 106 | 
            +
                          name: keyword_field(cloud.project_name),
         | 
| 107 | 
            +
                        },
         | 
| 108 | 
            +
                        region: keyword_field(cloud.region)
         | 
| 109 | 
            +
                      }
         | 
| 110 | 
            +
                    end
         | 
| 111 | 
            +
             | 
| 86 112 | 
             
                    def build_labels(labels)
         | 
| 87 113 | 
             
                      keyword_object(labels)
         | 
| 88 114 | 
             
                    end
         | 
| @@ -38,6 +38,7 @@ module ElasticAPM | |
| 38 38 | 
             
                          duration: ms(transaction.duration),
         | 
| 39 39 | 
             
                          timestamp: transaction.timestamp,
         | 
| 40 40 | 
             
                          sampled: transaction.sampled?,
         | 
| 41 | 
            +
                          sample_rate: transaction.sample_rate,
         | 
| 41 42 | 
             
                          context: context_serializer.build(transaction.context),
         | 
| 42 43 | 
             
                          span_count: {
         | 
| 43 44 | 
             
                            started: transaction.started_spans,
         | 
| @@ -32,14 +32,14 @@ module ElasticAPM | |
| 32 32 | 
             
                  private
         | 
| 33 33 |  | 
| 34 34 | 
             
                  def build(config)
         | 
| 35 | 
            -
                     | 
| 35 | 
            +
                    service = Metadata::ServiceInfo.new(config)
         | 
| 36 36 |  | 
| 37 37 | 
             
                    [
         | 
| 38 38 | 
             
                      "elastic-apm-ruby/#{VERSION}",
         | 
| 39 39 | 
             
                      HTTP::Request::USER_AGENT,
         | 
| 40 40 | 
             
                      [
         | 
| 41 | 
            -
                         | 
| 42 | 
            -
                         | 
| 41 | 
            +
                        service.runtime.name,
         | 
| 42 | 
            +
                        service.runtime.version
         | 
| 43 43 | 
             
                      ].join('/')
         | 
| 44 44 | 
             
                    ].join(' ')
         | 
| 45 45 | 
             
                  end
         | 
    
        data/lib/elastic_apm/util.rb
    CHANGED
    
    
| @@ -0,0 +1,46 @@ | |
| 1 | 
            +
            # Licensed to Elasticsearch B.V. under one or more contributor
         | 
| 2 | 
            +
            # license agreements. See the NOTICE file distributed with
         | 
| 3 | 
            +
            # this work for additional information regarding copyright
         | 
| 4 | 
            +
            # ownership. Elasticsearch B.V. licenses this file to you under
         | 
| 5 | 
            +
            # the Apache License, Version 2.0 (the "License"); you may
         | 
| 6 | 
            +
            # not use this file except in compliance with the License.
         | 
| 7 | 
            +
            # You may obtain a copy of the License at
         | 
| 8 | 
            +
            #
         | 
| 9 | 
            +
            #   http://www.apache.org/licenses/LICENSE-2.0
         | 
| 10 | 
            +
            #
         | 
| 11 | 
            +
            # Unless required by applicable law or agreed to in writing,
         | 
| 12 | 
            +
            # software distributed under the License is distributed on an
         | 
| 13 | 
            +
            # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
         | 
| 14 | 
            +
            # KIND, either express or implied.  See the License for the
         | 
| 15 | 
            +
            # specific language governing permissions and limitations
         | 
| 16 | 
            +
            # under the License.
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            # frozen_string_literal: true
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            module ElasticAPM
         | 
| 21 | 
            +
              module Util
         | 
| 22 | 
            +
                # @api private
         | 
| 23 | 
            +
                # Rounds half away from zero.
         | 
| 24 | 
            +
                # If `minimum` is provided, and the value rounds to 0 (but was not zero to
         | 
| 25 | 
            +
                # begin with), use the minimum instead.
         | 
| 26 | 
            +
                module PrecisionValidator
         | 
| 27 | 
            +
                  extend self
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  def validate(value, precision: 0, minimum: nil)
         | 
| 30 | 
            +
                    float = Float(value)
         | 
| 31 | 
            +
                    return nil unless (0.0..1.0).cover?(float)
         | 
| 32 | 
            +
                    return float if float == 0
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    multiplier = Float(10**precision)
         | 
| 35 | 
            +
                    rounded = (float * multiplier + 0.5).floor / multiplier
         | 
| 36 | 
            +
                    if rounded == 0 && minimum
         | 
| 37 | 
            +
                      minimum
         | 
| 38 | 
            +
                    else
         | 
| 39 | 
            +
                      rounded
         | 
| 40 | 
            +
                    end
         | 
| 41 | 
            +
                  rescue ArgumentError
         | 
| 42 | 
            +
                    nil
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
            end
         | 
    
        data/lib/elastic_apm/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: elastic-apm
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 3. | 
| 4 | 
            +
              version: 3.11.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Mikkel Malmberg
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2020- | 
| 11 | 
            +
            date: 2020-10-27 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: concurrent-ruby
         | 
| @@ -128,6 +128,7 @@ files: | |
| 128 128 | 
             
            - lib/elastic_apm/config/duration.rb
         | 
| 129 129 | 
             
            - lib/elastic_apm/config/options.rb
         | 
| 130 130 | 
             
            - lib/elastic_apm/config/regexp_list.rb
         | 
| 131 | 
            +
            - lib/elastic_apm/config/round_float.rb
         | 
| 131 132 | 
             
            - lib/elastic_apm/config/wildcard_pattern_list.rb
         | 
| 132 133 | 
             
            - lib/elastic_apm/context.rb
         | 
| 133 134 | 
             
            - lib/elastic_apm/context/request.rb
         | 
| @@ -148,6 +149,7 @@ files: | |
| 148 149 | 
             
            - lib/elastic_apm/internal_error.rb
         | 
| 149 150 | 
             
            - lib/elastic_apm/logging.rb
         | 
| 150 151 | 
             
            - lib/elastic_apm/metadata.rb
         | 
| 152 | 
            +
            - lib/elastic_apm/metadata/cloud_info.rb
         | 
| 151 153 | 
             
            - lib/elastic_apm/metadata/process_info.rb
         | 
| 152 154 | 
             
            - lib/elastic_apm/metadata/service_info.rb
         | 
| 153 155 | 
             
            - lib/elastic_apm/metadata/system_info.rb
         | 
| @@ -235,6 +237,7 @@ files: | |
| 235 237 | 
             
            - lib/elastic_apm/util.rb
         | 
| 236 238 | 
             
            - lib/elastic_apm/util/inflector.rb
         | 
| 237 239 | 
             
            - lib/elastic_apm/util/lru_cache.rb
         | 
| 240 | 
            +
            - lib/elastic_apm/util/precision_validator.rb
         | 
| 238 241 | 
             
            - lib/elastic_apm/util/throttle.rb
         | 
| 239 242 | 
             
            - lib/elastic_apm/version.rb
         | 
| 240 243 | 
             
            homepage: https://github.com/elastic/apm-agent-ruby
         | 
| @@ -257,7 +260,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 257 260 | 
             
                - !ruby/object:Gem::Version
         | 
| 258 261 | 
             
                  version: '0'
         | 
| 259 262 | 
             
            requirements: []
         | 
| 260 | 
            -
            rubygems_version: 3.1. | 
| 263 | 
            +
            rubygems_version: 3.1.4
         | 
| 261 264 | 
             
            signing_key:
         | 
| 262 265 | 
             
            specification_version: 4
         | 
| 263 266 | 
             
            summary: The official Elastic APM agent for Ruby
         |