appsignal 2.10.7-java → 2.11.0.alpha.2-java
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/.semaphore/semaphore.yml +45 -53
- data/CHANGELOG.md +18 -0
- data/build_matrix.yml +13 -6
- data/ext/agent.yml +19 -19
- data/ext/appsignal_extension.c +10 -1
- data/ext/base.rb +15 -4
- data/gemfiles/padrino.gemfile +2 -2
- data/lib/appsignal.rb +21 -1
- data/lib/appsignal/capistrano.rb +2 -0
- data/lib/appsignal/config.rb +6 -2
- data/lib/appsignal/environment.rb +126 -0
- data/lib/appsignal/extension/jruby.rb +10 -0
- data/lib/appsignal/hooks/net_http.rb +2 -0
- data/lib/appsignal/hooks/puma.rb +2 -58
- data/lib/appsignal/hooks/redis.rb +2 -0
- data/lib/appsignal/hooks/sequel.rb +2 -0
- data/lib/appsignal/hooks/sidekiq.rb +2 -99
- data/lib/appsignal/integrations/delayed_job_plugin.rb +16 -3
- data/lib/appsignal/integrations/object.rb +4 -0
- data/lib/appsignal/integrations/resque_active_job.rb +12 -4
- data/lib/appsignal/probes/puma.rb +61 -0
- data/lib/appsignal/probes/sidekiq.rb +102 -0
- data/lib/appsignal/rack/js_exception_catcher.rb +5 -2
- data/lib/appsignal/transaction.rb +22 -7
- data/lib/appsignal/version.rb +1 -1
- data/lib/puma/plugin/appsignal.rb +2 -1
- data/spec/lib/appsignal/cli/diagnose_spec.rb +2 -1
- data/spec/lib/appsignal/config_spec.rb +6 -1
- data/spec/lib/appsignal/environment_spec.rb +167 -0
- data/spec/lib/appsignal/hooks/delayed_job_spec.rb +198 -166
- data/spec/lib/appsignal/hooks/puma_spec.rb +2 -181
- data/spec/lib/appsignal/hooks/sidekiq_spec.rb +256 -462
- data/spec/lib/appsignal/integrations/padrino_spec.rb +1 -1
- data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +55 -13
- data/spec/lib/appsignal/probes/puma_spec.rb +180 -0
- data/spec/lib/appsignal/probes/sidekiq_spec.rb +201 -0
- data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +9 -4
- data/spec/lib/appsignal/transaction_spec.rb +30 -13
- data/spec/lib/appsignal_spec.rb +22 -0
- data/spec/lib/puma/appsignal_spec.rb +1 -1
- data/spec/support/helpers/dependency_helper.rb +5 -0
- data/spec/support/helpers/env_helpers.rb +1 -1
- data/spec/support/helpers/environment_metdata_helper.rb +16 -0
- data/spec/support/stubs/sidekiq/api.rb +1 -1
- metadata +19 -8
| @@ -27,9 +27,8 @@ module Appsignal | |
| 27 27 | 
             
                      method_name = "perform"
         | 
| 28 28 | 
             
                    else
         | 
| 29 29 | 
             
                      # Delayed Job
         | 
| 30 | 
            -
                      args = extract_value( | 
| 31 | 
            -
                       | 
| 32 | 
            -
                      class_name, method_name = class_and_method_name.split("#")
         | 
| 30 | 
            +
                      args = extract_value(payload, :args, {})
         | 
| 31 | 
            +
                      class_name, method_name = class_and_method_name_from_object_or_hash(payload, job.name)
         | 
| 33 32 | 
             
                    end
         | 
| 34 33 |  | 
| 35 34 | 
             
                    params = Appsignal::Utils::HashSanitizer.sanitize(
         | 
| @@ -54,6 +53,20 @@ module Appsignal | |
| 54 53 | 
             
                    end
         | 
| 55 54 | 
             
                  end
         | 
| 56 55 |  | 
| 56 | 
            +
                  def self.class_and_method_name_from_object_or_hash(payload, default_name)
         | 
| 57 | 
            +
                    # Attempt to find appsignal_name override
         | 
| 58 | 
            +
                    class_and_method_name = extract_value(payload, :appsignal_name, nil)
         | 
| 59 | 
            +
                    return class_and_method_name.split("#") if class_and_method_name.is_a?(String)
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                    pound_split = default_name.split("#")
         | 
| 62 | 
            +
                    return pound_split if pound_split.length == 2
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    dot_split = default_name.split(".")
         | 
| 65 | 
            +
                    return default_name if dot_split.length == 2
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    ["unknown"]
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
             | 
| 57 70 | 
             
                  def self.extract_value(object_or_hash, field, default_value = nil, convert_to_s = false)
         | 
| 58 71 | 
             
                    value = nil
         | 
| 59 72 |  | 
| @@ -14,12 +14,18 @@ module Appsignal | |
| 14 14 | 
             
                          Appsignal.config[:filter_parameters]
         | 
| 15 15 | 
             
                        )
         | 
| 16 16 |  | 
| 17 | 
            +
                        queue_start =
         | 
| 18 | 
            +
                          if job.respond_to?(:enqueued_at) && job.enqueued_at
         | 
| 19 | 
            +
                            Time.parse(job.enqueued_at).utc
         | 
| 20 | 
            +
                          end
         | 
| 21 | 
            +
             | 
| 17 22 | 
             
                        Appsignal.monitor_single_transaction(
         | 
| 18 23 | 
             
                          "perform_job.resque",
         | 
| 19 | 
            -
                          :class | 
| 20 | 
            -
                          :method | 
| 21 | 
            -
                          :params | 
| 22 | 
            -
                          : | 
| 24 | 
            +
                          :class       => job.class.to_s,
         | 
| 25 | 
            +
                          :method      => "perform",
         | 
| 26 | 
            +
                          :params      => params,
         | 
| 27 | 
            +
                          :queue_start => queue_start,
         | 
| 28 | 
            +
                          :metadata    => {
         | 
| 23 29 | 
             
                            :id       => job.job_id,
         | 
| 24 30 | 
             
                            :queue    => job.queue_name
         | 
| 25 31 | 
             
                          }
         | 
| @@ -28,6 +34,8 @@ module Appsignal | |
| 28 34 | 
             
                        end
         | 
| 29 35 | 
             
                      end
         | 
| 30 36 | 
             
                    end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    Appsignal::Environment.report("ruby_active_job_resque_enabled") { true }
         | 
| 31 39 | 
             
                  end
         | 
| 32 40 | 
             
                end
         | 
| 33 41 | 
             
              end
         | 
| @@ -0,0 +1,61 @@ | |
| 1 | 
            +
            module Appsignal
         | 
| 2 | 
            +
              module Probes
         | 
| 3 | 
            +
                # @api private
         | 
| 4 | 
            +
                class PumaProbe
         | 
| 5 | 
            +
                  def initialize
         | 
| 6 | 
            +
                    @hostname = Appsignal.config[:hostname] || Socket.gethostname
         | 
| 7 | 
            +
                  end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def call
         | 
| 10 | 
            +
                    puma_stats = fetch_puma_stats
         | 
| 11 | 
            +
                    return unless puma_stats
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    stats = JSON.parse puma_stats, :symbolize_names => true
         | 
| 14 | 
            +
                    counts = {}
         | 
| 15 | 
            +
                    count_keys = [:backlog, :running, :pool_capacity, :max_threads]
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    if stats[:worker_status] # Multiple workers
         | 
| 18 | 
            +
                      stats[:worker_status].each do |worker|
         | 
| 19 | 
            +
                        stat = worker[:last_status]
         | 
| 20 | 
            +
                        count_keys.each do |key|
         | 
| 21 | 
            +
                          count_if_present counts, key, stat
         | 
| 22 | 
            +
                        end
         | 
| 23 | 
            +
                      end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                      gauge(:workers, stats[:workers], :type => :count)
         | 
| 26 | 
            +
                      gauge(:workers, stats[:booted_workers], :type => :booted)
         | 
| 27 | 
            +
                      gauge(:workers, stats[:old_workers], :type => :old)
         | 
| 28 | 
            +
                    else # Single worker
         | 
| 29 | 
            +
                      count_keys.each do |key|
         | 
| 30 | 
            +
                        count_if_present counts, key, stats
         | 
| 31 | 
            +
                      end
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    gauge(:connection_backlog, counts[:backlog]) if counts[:backlog]
         | 
| 35 | 
            +
                    gauge(:pool_capacity, counts[:pool_capacity]) if counts[:pool_capacity]
         | 
| 36 | 
            +
                    gauge(:threads, counts[:running], :type => :running) if counts[:running]
         | 
| 37 | 
            +
                    gauge(:threads, counts[:max_threads], :type => :max) if counts[:max_threads]
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  private
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  attr_reader :hostname
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  def gauge(field, count, tags = {})
         | 
| 45 | 
            +
                    Appsignal.set_gauge("puma_#{field}", count, tags.merge(:hostname => hostname))
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def count_if_present(counts, key, stats)
         | 
| 49 | 
            +
                    stat_value = stats[key]
         | 
| 50 | 
            +
                    return unless stat_value
         | 
| 51 | 
            +
                    counts[key] ||= 0
         | 
| 52 | 
            +
                    counts[key] += stat_value
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  def fetch_puma_stats
         | 
| 56 | 
            +
                    ::Puma.stats
         | 
| 57 | 
            +
                  rescue NoMethodError # rubocop:disable Lint/HandleExceptions
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
            end
         | 
| @@ -0,0 +1,102 @@ | |
| 1 | 
            +
            module Appsignal
         | 
| 2 | 
            +
              module Probes
         | 
| 3 | 
            +
                # @api private
         | 
| 4 | 
            +
                class SidekiqProbe
         | 
| 5 | 
            +
                  attr_reader :config
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  def self.dependencies_present?
         | 
| 8 | 
            +
                    Gem::Version.new(::Redis::VERSION) >= Gem::Version.new("3.3.5")
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def initialize(config = {})
         | 
| 12 | 
            +
                    @config = config
         | 
| 13 | 
            +
                    @cache = {}
         | 
| 14 | 
            +
                    config_string = " with config: #{config}" unless config.empty?
         | 
| 15 | 
            +
                    Appsignal.logger.debug("Initializing Sidekiq probe#{config_string}")
         | 
| 16 | 
            +
                    require "sidekiq/api"
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def call
         | 
| 20 | 
            +
                    track_redis_info
         | 
| 21 | 
            +
                    track_stats
         | 
| 22 | 
            +
                    track_queues
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  private
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  attr_reader :cache
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  def track_redis_info
         | 
| 30 | 
            +
                    return unless ::Sidekiq.respond_to?(:redis_info)
         | 
| 31 | 
            +
                    redis_info = ::Sidekiq.redis_info
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    gauge "connection_count", redis_info.fetch("connected_clients")
         | 
| 34 | 
            +
                    gauge "memory_usage", redis_info.fetch("used_memory")
         | 
| 35 | 
            +
                    gauge "memory_usage_rss", redis_info.fetch("used_memory_rss")
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  def track_stats
         | 
| 39 | 
            +
                    stats = ::Sidekiq::Stats.new
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    gauge "worker_count", stats.workers_size
         | 
| 42 | 
            +
                    gauge "process_count", stats.processes_size
         | 
| 43 | 
            +
                    gauge_delta :jobs_processed, "job_count", stats.processed,
         | 
| 44 | 
            +
                      :status => :processed
         | 
| 45 | 
            +
                    gauge_delta :jobs_failed, "job_count", stats.failed, :status => :failed
         | 
| 46 | 
            +
                    gauge "job_count", stats.retry_size, :status => :retry_queue
         | 
| 47 | 
            +
                    gauge_delta :jobs_dead, "job_count", stats.dead_size, :status => :died
         | 
| 48 | 
            +
                    gauge "job_count", stats.scheduled_size, :status => :scheduled
         | 
| 49 | 
            +
                    gauge "job_count", stats.enqueued, :status => :enqueued
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  def track_queues
         | 
| 53 | 
            +
                    ::Sidekiq::Queue.all.each do |queue|
         | 
| 54 | 
            +
                      gauge "queue_length", queue.size, :queue => queue.name
         | 
| 55 | 
            +
                      # Convert latency from seconds to milliseconds
         | 
| 56 | 
            +
                      gauge "queue_latency", queue.latency * 1_000.0, :queue => queue.name
         | 
| 57 | 
            +
                    end
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  # Track a gauge metric with the `sidekiq_` prefix
         | 
| 61 | 
            +
                  def gauge(key, value, tags = {})
         | 
| 62 | 
            +
                    tags[:hostname] = hostname if hostname
         | 
| 63 | 
            +
                    Appsignal.set_gauge "sidekiq_#{key}", value, tags
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  # Track the delta of two values for a gauge metric
         | 
| 67 | 
            +
                  #
         | 
| 68 | 
            +
                  # First call will store the data for the metric and the second call will
         | 
| 69 | 
            +
                  # set a gauge metric with the difference. This is used for absolute
         | 
| 70 | 
            +
                  # counter values which we want to track as gauges.
         | 
| 71 | 
            +
                  #
         | 
| 72 | 
            +
                  # @example
         | 
| 73 | 
            +
                  #   gauge_delta :my_cache_key, "my_gauge", 10
         | 
| 74 | 
            +
                  #   gauge_delta :my_cache_key, "my_gauge", 15
         | 
| 75 | 
            +
                  #   # Creates a gauge with the value `5`
         | 
| 76 | 
            +
                  # @see #gauge
         | 
| 77 | 
            +
                  def gauge_delta(cache_key, key, value, tags = {})
         | 
| 78 | 
            +
                    previous_value = cache[cache_key]
         | 
| 79 | 
            +
                    cache[cache_key] = value
         | 
| 80 | 
            +
                    return unless previous_value
         | 
| 81 | 
            +
                    new_value = value - previous_value
         | 
| 82 | 
            +
                    gauge key, new_value, tags
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  def hostname
         | 
| 86 | 
            +
                    return @hostname if defined?(@hostname)
         | 
| 87 | 
            +
                    if config.key?(:hostname)
         | 
| 88 | 
            +
                      @hostname = config[:hostname]
         | 
| 89 | 
            +
                      Appsignal.logger.debug "Sidekiq probe: Using hostname config " \
         | 
| 90 | 
            +
                        "option #{@hostname.inspect} as hostname"
         | 
| 91 | 
            +
                      return @hostname
         | 
| 92 | 
            +
                    end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    host = nil
         | 
| 95 | 
            +
                    ::Sidekiq.redis { |c| host = c.connection[:host] }
         | 
| 96 | 
            +
                    Appsignal.logger.debug "Sidekiq probe: Using Redis server hostname " \
         | 
| 97 | 
            +
                      "#{host.inspect} as hostname"
         | 
| 98 | 
            +
                    @hostname = host
         | 
| 99 | 
            +
                  end
         | 
| 100 | 
            +
                end
         | 
| 101 | 
            +
              end
         | 
| 102 | 
            +
            end
         | 
| @@ -29,8 +29,11 @@ module Appsignal | |
| 29 29 | 
             
                    Appsignal.logger.debug \
         | 
| 30 30 | 
             
                      "Initializing Appsignal::Rack::JSExceptionCatcher"
         | 
| 31 31 | 
             
                    deprecation_message "The Appsignal::Rack::JSExceptionCatcher is " \
         | 
| 32 | 
            -
                      "deprecated | 
| 33 | 
            -
                      "integration  | 
| 32 | 
            +
                      "deprecated and will be removed in a future version. Please use " \
         | 
| 33 | 
            +
                      "the official AppSignal JavaScript integration by disabling " \
         | 
| 34 | 
            +
                      "`enable_frontend_error_catching` in your configuration and " \
         | 
| 35 | 
            +
                      "installing AppSignal for JavaScript instead. " \
         | 
| 36 | 
            +
                      "(https://docs.appsignal.com/front-end/)"
         | 
| 34 37 | 
             
                    @app = app
         | 
| 35 38 | 
             
                  end
         | 
| 36 39 |  | 
| @@ -228,12 +228,27 @@ module Appsignal | |
| 228 228 | 
             
                  Appsignal.logger.warn("Queue start value #{start} is too big")
         | 
| 229 229 | 
             
                end
         | 
| 230 230 |  | 
| 231 | 
            +
                # Set the queue time based on the HTTP header or `:queue_start` env key
         | 
| 232 | 
            +
                # value.
         | 
| 233 | 
            +
                #
         | 
| 234 | 
            +
                # This method will first try to read the queue time from the HTTP headers
         | 
| 235 | 
            +
                # `X-Request-Start` or `X-Queue-Start`. Which are parsed by Rack as
         | 
| 236 | 
            +
                # `HTTP_X_QUEUE_START` and `HTTP_X_REQUEST_START`.
         | 
| 237 | 
            +
                # The header value is parsed by AppSignal as either milliseconds or
         | 
| 238 | 
            +
                # microseconds.
         | 
| 239 | 
            +
                #
         | 
| 240 | 
            +
                # If no headers are found, or the value could not be parsed, it falls back
         | 
| 241 | 
            +
                # on the `:queue_start` env key on this Transaction's {request} environment
         | 
| 242 | 
            +
                # (called like `request.env[:queue_start]`). This value is parsed by
         | 
| 243 | 
            +
                # AppSignal as seconds.
         | 
| 244 | 
            +
                #
         | 
| 245 | 
            +
                # @see https://docs.appsignal.com/ruby/instrumentation/request-queue-time.html
         | 
| 246 | 
            +
                # @return [void]
         | 
| 231 247 | 
             
                def set_http_or_background_queue_start
         | 
| 232 | 
            -
                   | 
| 233 | 
            -
             | 
| 234 | 
            -
             | 
| 235 | 
            -
             | 
| 236 | 
            -
                  end
         | 
| 248 | 
            +
                  start = http_queue_start || background_queue_start
         | 
| 249 | 
            +
                  return unless start
         | 
| 250 | 
            +
             | 
| 251 | 
            +
                  set_queue_start(start)
         | 
| 237 252 | 
             
                end
         | 
| 238 253 |  | 
| 239 254 | 
             
                def set_metadata(key, value)
         | 
| @@ -346,14 +361,14 @@ module Appsignal | |
| 346 361 | 
             
                #
         | 
| 347 362 | 
             
                # @return [nil] if no {#environment} is present.
         | 
| 348 363 | 
             
                # @return [nil] if there is no `:queue_start` in the {#environment}.
         | 
| 349 | 
            -
                # @return [Integer]
         | 
| 364 | 
            +
                # @return [Integer] `:queue_start` time (in seconds) converted to milliseconds
         | 
| 350 365 | 
             
                def background_queue_start
         | 
| 351 366 | 
             
                  env = environment
         | 
| 352 367 | 
             
                  return unless env
         | 
| 353 368 | 
             
                  queue_start = env[:queue_start]
         | 
| 354 369 | 
             
                  return unless queue_start
         | 
| 355 370 |  | 
| 356 | 
            -
                  (queue_start.to_f * 1000.0).to_i
         | 
| 371 | 
            +
                  (queue_start.to_f * 1000.0).to_i # Convert seconds to milliseconds
         | 
| 357 372 | 
             
                end
         | 
| 358 373 |  | 
| 359 374 | 
             
                # Returns HTTP queue start time in milliseconds.
         | 
    
        data/lib/appsignal/version.rb
    CHANGED
    
    
| @@ -17,7 +17,8 @@ Puma::Plugin.create do | |
| 17 17 | 
             
                launcher.events.on_booted do
         | 
| 18 18 | 
             
                  require "appsignal"
         | 
| 19 19 | 
             
                  if ::Puma.respond_to?(:stats)
         | 
| 20 | 
            -
                     | 
| 20 | 
            +
                    require "appsignal/probes/puma"
         | 
| 21 | 
            +
                    Appsignal::Minutely.probes.register :puma, Appsignal::Probes::PumaProbe
         | 
| 21 22 | 
             
                  end
         | 
| 22 23 | 
             
                  Appsignal.start
         | 
| 23 24 | 
             
                  Appsignal.start_logger
         | 
| @@ -263,7 +263,8 @@ describe Appsignal::CLI::Diagnose, :api_stub => true, :send_report => :yes_cli_i | |
| 263 263 | 
             
                      },
         | 
| 264 264 | 
             
                      "download" => {
         | 
| 265 265 | 
             
                        "download_url" => kind_of(String),
         | 
| 266 | 
            -
                        "checksum" => "verified"
         | 
| 266 | 
            +
                        "checksum" => "verified",
         | 
| 267 | 
            +
                        "http_proxy" => nil
         | 
| 267 268 | 
             
                      },
         | 
| 268 269 | 
             
                      "build" => {
         | 
| 269 270 | 
             
                        "time" => kind_of(String),
         | 
| @@ -148,6 +148,7 @@ describe Appsignal::Config do | |
| 148 148 | 
             
                    :instrument_redis               => true,
         | 
| 149 149 | 
             
                    :instrument_sequel              => true,
         | 
| 150 150 | 
             
                    :skip_session_data              => false,
         | 
| 151 | 
            +
                    :send_environment_metadata      => true,
         | 
| 151 152 | 
             
                    :send_params                    => true,
         | 
| 152 153 | 
             
                    :endpoint                       => "https://push.appsignal.com",
         | 
| 153 154 | 
             
                    :push_api_key                   => "abc",
         | 
| @@ -411,7 +412,8 @@ describe Appsignal::Config do | |
| 411 412 | 
             
                    :instrument_sequel => false,
         | 
| 412 413 | 
             
                    :files_world_accessible => false,
         | 
| 413 414 | 
             
                    :request_headers => %w[accept accept-charset],
         | 
| 414 | 
            -
                    :revision => "v2.5.1"
         | 
| 415 | 
            +
                    :revision => "v2.5.1",
         | 
| 416 | 
            +
                    :send_environment_metadata => false
         | 
| 415 417 | 
             
                  }
         | 
| 416 418 | 
             
                end
         | 
| 417 419 | 
             
                before do
         | 
| @@ -428,6 +430,7 @@ describe Appsignal::Config do | |
| 428 430 | 
             
                  ENV["APPSIGNAL_INSTRUMENT_SEQUEL"]       = "false"
         | 
| 429 431 | 
             
                  ENV["APPSIGNAL_FILES_WORLD_ACCESSIBLE"]  = "false"
         | 
| 430 432 | 
             
                  ENV["APPSIGNAL_REQUEST_HEADERS"]         = "accept,accept-charset"
         | 
| 433 | 
            +
                  ENV["APPSIGNAL_SEND_ENVIRONMENT_METADATA"] = "false"
         | 
| 431 434 | 
             
                  ENV["APP_REVISION"] = "v2.5.1"
         | 
| 432 435 | 
             
                end
         | 
| 433 436 |  | 
| @@ -527,6 +530,7 @@ describe Appsignal::Config do | |
| 527 530 | 
             
                  config[:running_in_container] = false
         | 
| 528 531 | 
             
                  config[:dns_servers] = ["8.8.8.8", "8.8.4.4"]
         | 
| 529 532 | 
             
                  config[:transaction_debug_mode] = true
         | 
| 533 | 
            +
                  config[:send_environment_metadata] = false
         | 
| 530 534 | 
             
                  config[:revision] = "v2.5.1"
         | 
| 531 535 | 
             
                  config.write_to_environment
         | 
| 532 536 | 
             
                end
         | 
| @@ -555,6 +559,7 @@ describe Appsignal::Config do | |
| 555 559 | 
             
                  expect(ENV["_APPSIGNAL_DNS_SERVERS"]).to                  eq "8.8.8.8,8.8.4.4"
         | 
| 556 560 | 
             
                  expect(ENV["_APPSIGNAL_FILES_WORLD_ACCESSIBLE"]).to       eq "true"
         | 
| 557 561 | 
             
                  expect(ENV["_APPSIGNAL_TRANSACTION_DEBUG_MODE"]).to       eq "true"
         | 
| 562 | 
            +
                  expect(ENV["_APPSIGNAL_SEND_ENVIRONMENT_METADATA"]).to    eq "false"
         | 
| 558 563 | 
             
                  expect(ENV["_APP_REVISION"]).to                           eq "v2.5.1"
         | 
| 559 564 | 
             
                  expect(ENV).to_not                                        have_key("_APPSIGNAL_WORKING_DIR_PATH")
         | 
| 560 565 | 
             
                  expect(ENV).to_not                                        have_key("_APPSIGNAL_WORKING_DIRECTORY_PATH")
         | 
| @@ -0,0 +1,167 @@ | |
| 1 | 
            +
            describe Appsignal::Environment do
         | 
| 2 | 
            +
              include EnvironmentMetadataHelper
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              before(:context) { start_agent }
         | 
| 5 | 
            +
              before { capture_environment_metadata_report_calls }
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              def report(key, &value_block)
         | 
| 8 | 
            +
                described_class.report(key, &value_block)
         | 
| 9 | 
            +
              end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              describe ".report" do
         | 
| 12 | 
            +
                it "sends environment metadata to the extension" do
         | 
| 13 | 
            +
                  logs =
         | 
| 14 | 
            +
                    capture_logs do
         | 
| 15 | 
            +
                      report("_test_ruby_version") { "1.0.0" }
         | 
| 16 | 
            +
                      expect_environment_metadata("_test_ruby_version", "1.0.0")
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
                  expect(logs).to be_empty
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                context "when the key is a non String type" do
         | 
| 22 | 
            +
                  it "does not set the value" do
         | 
| 23 | 
            +
                    logs =
         | 
| 24 | 
            +
                      capture_logs do
         | 
| 25 | 
            +
                        report(:_test_symbol) { "1.0.0" }
         | 
| 26 | 
            +
                        expect_not_environment_metadata(:_test_symbol)
         | 
| 27 | 
            +
                        expect_not_environment_metadata("_test_symbol")
         | 
| 28 | 
            +
                      end
         | 
| 29 | 
            +
                    expect(logs).to contains_log(
         | 
| 30 | 
            +
                      :error,
         | 
| 31 | 
            +
                      "Unable to report on environment metadata: Unsupported value type for :_test_symbol"
         | 
| 32 | 
            +
                    )
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                context "when the key is nil" do
         | 
| 37 | 
            +
                  it "does not set the value" do
         | 
| 38 | 
            +
                    logs =
         | 
| 39 | 
            +
                      capture_logs do
         | 
| 40 | 
            +
                        report(nil) { "1" }
         | 
| 41 | 
            +
                        expect_not_environment_metadata(nil)
         | 
| 42 | 
            +
                      end
         | 
| 43 | 
            +
                    expect(logs).to contains_log(
         | 
| 44 | 
            +
                      :error,
         | 
| 45 | 
            +
                      "Unable to report on environment metadata: Unsupported value type for nil"
         | 
| 46 | 
            +
                    )
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                context "when the value is true or false" do
         | 
| 51 | 
            +
                  it "reports true or false as Strings" do
         | 
| 52 | 
            +
                    logs =
         | 
| 53 | 
            +
                      capture_logs do
         | 
| 54 | 
            +
                        report("_test_true") { true }
         | 
| 55 | 
            +
                        report("_test_false") { false }
         | 
| 56 | 
            +
                        expect_environment_metadata("_test_true", "true")
         | 
| 57 | 
            +
                        expect_environment_metadata("_test_false", "false")
         | 
| 58 | 
            +
                      end
         | 
| 59 | 
            +
                    expect(logs).to be_empty
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                context "when the value is nil" do
         | 
| 64 | 
            +
                  it "does not set the value" do
         | 
| 65 | 
            +
                    logs =
         | 
| 66 | 
            +
                      capture_logs do
         | 
| 67 | 
            +
                        report("_test_ruby_version") { nil }
         | 
| 68 | 
            +
                        expect_not_environment_metadata("_test_ruby_version")
         | 
| 69 | 
            +
                      end
         | 
| 70 | 
            +
                    expect(logs).to contains_log(
         | 
| 71 | 
            +
                      :error,
         | 
| 72 | 
            +
                      "Unable to report on environment metadata \"_test_ruby_version\": " \
         | 
| 73 | 
            +
                        "Unsupported value type for nil"
         | 
| 74 | 
            +
                    )
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                context "when the value block raises an error" do
         | 
| 79 | 
            +
                  it "does not re-raise the error and writes it to the log" do
         | 
| 80 | 
            +
                    logs =
         | 
| 81 | 
            +
                      capture_logs do
         | 
| 82 | 
            +
                        report("_test_error") { raise "uh oh" }
         | 
| 83 | 
            +
                        expect_not_environment_metadata("_test_error")
         | 
| 84 | 
            +
                      end
         | 
| 85 | 
            +
                    expect(logs).to contains_log(
         | 
| 86 | 
            +
                      :error,
         | 
| 87 | 
            +
                      "Unable to report on environment metadata \"_test_error\":\n" \
         | 
| 88 | 
            +
                        "RuntimeError: uh oh"
         | 
| 89 | 
            +
                    )
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                context "when something unforseen errors" do
         | 
| 94 | 
            +
                  it "does not re-raise the error and writes it to the log" do
         | 
| 95 | 
            +
                    klass = Class.new do
         | 
| 96 | 
            +
                      def inspect
         | 
| 97 | 
            +
                        raise "inspect error"
         | 
| 98 | 
            +
                      end
         | 
| 99 | 
            +
                    end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                    logs =
         | 
| 102 | 
            +
                      capture_logs do
         | 
| 103 | 
            +
                        report(klass.new) { raise "value error" }
         | 
| 104 | 
            +
                        expect(Appsignal::Extension).to_not have_received(:set_environment_metadata)
         | 
| 105 | 
            +
                      end
         | 
| 106 | 
            +
                    expect(logs).to contains_log(
         | 
| 107 | 
            +
                      :error,
         | 
| 108 | 
            +
                      "Unable to report on environment metadata:\n" \
         | 
| 109 | 
            +
                        "RuntimeError: inspect error"
         | 
| 110 | 
            +
                    )
         | 
| 111 | 
            +
                  end
         | 
| 112 | 
            +
                end
         | 
| 113 | 
            +
              end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
              describe ".report_supported_gems" do
         | 
| 116 | 
            +
                it "reports about all AppSignal supported gems in the bundle" do
         | 
| 117 | 
            +
                  logs = capture_logs { described_class.report_supported_gems }
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                  expect(logs).to be_empty
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                  bundle_gem_specs = ::Bundler.rubygems.all_specs
         | 
| 122 | 
            +
                  rack_spec = bundle_gem_specs.find { |s| s.name == "rack" }
         | 
| 123 | 
            +
                  rake_spec = bundle_gem_specs.find { |s| s.name == "rake" }
         | 
| 124 | 
            +
                  expect_environment_metadata("ruby_rack_version", rack_spec.version.to_s)
         | 
| 125 | 
            +
                  expect_environment_metadata("ruby_rake_version", rake_spec.version.to_s)
         | 
| 126 | 
            +
                  expect(rack_spec.version.to_s).to_not be_empty
         | 
| 127 | 
            +
                  expect(rake_spec.version.to_s).to_not be_empty
         | 
| 128 | 
            +
                end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                context "when something unforseen errors" do
         | 
| 131 | 
            +
                  it "does not re-raise the error and writes it to the log" do
         | 
| 132 | 
            +
                    expect(Bundler).to receive(:rubygems).and_raise(RuntimeError, "bundler error")
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                    logs = capture_logs { described_class.report_supported_gems }
         | 
| 135 | 
            +
                    expect(logs).to contains_log(
         | 
| 136 | 
            +
                      :error,
         | 
| 137 | 
            +
                      "Unable to report supported gems:\nRuntimeError: bundler error"
         | 
| 138 | 
            +
                    )
         | 
| 139 | 
            +
                  end
         | 
| 140 | 
            +
                end
         | 
| 141 | 
            +
              end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
              describe ".report_enabled" do
         | 
| 144 | 
            +
                it "reports a feature being enabled" do
         | 
| 145 | 
            +
                  logs = capture_logs { described_class.report_enabled("a_test") }
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                  expect(logs).to be_empty
         | 
| 148 | 
            +
                  expect_environment_metadata("ruby_a_test_enabled", "true")
         | 
| 149 | 
            +
                end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                context "when something unforseen errors" do
         | 
| 152 | 
            +
                  it "does not re-raise the error and writes it to the log" do
         | 
| 153 | 
            +
                    klass = Class.new do
         | 
| 154 | 
            +
                      def to_s
         | 
| 155 | 
            +
                        raise "to_s error"
         | 
| 156 | 
            +
                      end
         | 
| 157 | 
            +
                    end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                    logs = capture_logs { described_class.report_enabled(klass.new) }
         | 
| 160 | 
            +
                    expect(logs).to contains_log(
         | 
| 161 | 
            +
                      :error,
         | 
| 162 | 
            +
                      "Unable to report integration enabled:\nRuntimeError: to_s error"
         | 
| 163 | 
            +
                    )
         | 
| 164 | 
            +
                  end
         | 
| 165 | 
            +
                end
         | 
| 166 | 
            +
              end
         | 
| 167 | 
            +
            end
         |