appsignal 3.8.0-java → 3.9.0-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/CHANGELOG.md +29 -0
- data/lib/appsignal/config.rb +10 -2
- data/lib/appsignal/hooks/sidekiq.rb +18 -1
- data/lib/appsignal/integrations/sidekiq.rb +37 -18
- data/lib/appsignal/integrations/sinatra.rb +9 -7
- data/lib/appsignal/rack/abstract_middleware.rb +127 -0
- data/lib/appsignal/rack/event_handler.rb +29 -1
- data/lib/appsignal/rack/generic_instrumentation.rb +6 -33
- data/lib/appsignal/rack/sinatra_instrumentation.rb +16 -42
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +1 -0
- data/spec/lib/appsignal/config_spec.rb +28 -0
- data/spec/lib/appsignal/integrations/sidekiq_spec.rb +175 -17
- data/spec/lib/appsignal/integrations/sinatra_spec.rb +57 -33
- data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +250 -0
- data/spec/lib/appsignal/rack/event_handler_spec.rb +56 -3
- data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +26 -79
- data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +80 -164
- data/support/install_deps +6 -0
- metadata +4 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 87ae30cedc24d783bcd6f28bb01bb19384e0f7a280fe2c8b89585a5161aaaf77
         | 
| 4 | 
            +
              data.tar.gz: 36c1760ff247804c579bb0b7bf3db2aee1d4ed53ede1a1920d5a8a1fa762f032
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: ed40403d826734ba4725b087b4d52d75be79d9bfd7172d12ceba70da3393de9bf55ca02964a81104082e9058eb44d1c3795623a21b244b13905a0310f2961d16
         | 
| 7 | 
            +
              data.tar.gz: 66832484c634de15a474774f49e8c38838faf4b71889c68c4ad7ef897d1ad64c119b926152a228117409103b657f8523599597394b989bbf87553d1c072bf5a1
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,5 +1,34 @@ | |
| 1 1 | 
             
            # AppSignal for Ruby gem Changelog
         | 
| 2 2 |  | 
| 3 | 
            +
            ## 3.9.0
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            _Published on 2024-06-21._
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ### Added
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            - [500b2b4b](https://github.com/appsignal/appsignal-ruby/commit/500b2b4bb57a29663a197ff063c672e6b0c44769) minor - Report Sidekiq errors when a job is dead/discarded. Configure the new `sidekiq_report_errors` config option to "discard" to only report errors when the job is not retried further.
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            ### Changed
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            - [c76952ff](https://github.com/appsignal/appsignal-ruby/commit/c76952ff5c8bd6e9d1d841a3aeb600b27494bb43) patch - Improve instrumentation for mounted Sinatra apps in Rails apps. The sample reported for the Sinatra request will now include the time spent in Rails and its middleware.
         | 
| 14 | 
            +
            - [661b8e08](https://github.com/appsignal/appsignal-ruby/commit/661b8e08de962e8f95326f0bbc9c0061b8cc0a62) patch - Support apps that have multiple Appsignal::Rack::EventHandler-s in the middleware stack.
         | 
| 15 | 
            +
            - [7382afa3](https://github.com/appsignal/appsignal-ruby/commit/7382afa3e9c89ce0c9f3430fb71825736e484e82) patch - Improve support for instrumentation of nested pure Rack and Sinatra apps. It will now report more of the request's duration and events. This also improves support for apps that have multiple Rack GenericInstrumentation or SinatraInstrumentation middlewares.
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            ### Fixed
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            - [2478eb19](https://github.com/appsignal/appsignal-ruby/commit/2478eb19f51c18433785347d02af18f405eeeabd) patch - Fix issue with AppSignal getting stuck in a boot loop when loading the Sinatra integration with: `require "appsignal/integrations/sinatra"`
         | 
| 20 | 
            +
              This could happen in nested applications, like a Sinatra app in a Rails app. It will now use the first config AppSignal starts with.
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            ## 3.8.1
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            _Published on 2024-06-17._
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            ### Added
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            - [5459a021](https://github.com/appsignal/appsignal-ruby/commit/5459a021d7d4bbbd09a0dcbdf5f3af7bf861b6f5) patch - Report the response status for Rails requests as the `response_status` tag on samples, e.g. 200, 301, 500. This tag is visible on the sample detail page.
         | 
| 29 | 
            +
              
         | 
| 30 | 
            +
              The response status is also reported as the `response_status` metric.
         | 
| 31 | 
            +
             | 
| 3 32 | 
             
            ## 3.8.0
         | 
| 4 33 |  | 
| 5 34 | 
             
            _Published on 2024-06-17._
         | 
    
        data/lib/appsignal/config.rb
    CHANGED
    
    | @@ -47,6 +47,7 @@ module Appsignal | |
| 47 47 | 
             
                  ],
         | 
| 48 48 | 
             
                  :send_environment_metadata => true,
         | 
| 49 49 | 
             
                  :send_params => true,
         | 
| 50 | 
            +
                  :sidekiq_report_errors => "all",
         | 
| 50 51 | 
             
                  :transaction_debug_mode => false
         | 
| 51 52 | 
             
                }.freeze
         | 
| 52 53 |  | 
| @@ -67,7 +68,7 @@ module Appsignal | |
| 67 68 |  | 
| 68 69 | 
             
                ENV_TO_KEY_MAPPING = {
         | 
| 69 70 | 
             
                  "APPSIGNAL_ACTIVE" => :active,
         | 
| 70 | 
            -
                  " | 
| 71 | 
            +
                  "APPSIGNAL_ACTIVEJOB_REPORT_ERRORS" => :activejob_report_errors,
         | 
| 71 72 | 
             
                  "APPSIGNAL_APP_NAME" => :name,
         | 
| 72 73 | 
             
                  "APPSIGNAL_BIND_ADDRESS" => :bind_address,
         | 
| 73 74 | 
             
                  "APPSIGNAL_CA_FILE_PATH" => :ca_file_path,
         | 
| @@ -108,6 +109,7 @@ module Appsignal | |
| 108 109 | 
             
                  "APPSIGNAL_SEND_ENVIRONMENT_METADATA" => :send_environment_metadata,
         | 
| 109 110 | 
             
                  "APPSIGNAL_SEND_PARAMS" => :send_params,
         | 
| 110 111 | 
             
                  "APPSIGNAL_SEND_SESSION_DATA" => :send_session_data,
         | 
| 112 | 
            +
                  "APPSIGNAL_SIDEKIQ_REPORT_ERRORS" => :sidekiq_report_errors,
         | 
| 111 113 | 
             
                  "APPSIGNAL_SKIP_SESSION_DATA" => :skip_session_data,
         | 
| 112 114 | 
             
                  "APPSIGNAL_STATSD_PORT" => :statsd_port,
         | 
| 113 115 | 
             
                  "APPSIGNAL_TRANSACTION_DEBUG_MODE" => :transaction_debug_mode,
         | 
| @@ -117,7 +119,7 @@ module Appsignal | |
| 117 119 | 
             
                }.freeze
         | 
| 118 120 | 
             
                # @api private
         | 
| 119 121 | 
             
                ENV_STRING_KEYS = %w[
         | 
| 120 | 
            -
                   | 
| 122 | 
            +
                  APPSIGNAL_ACTIVEJOB_REPORT_ERRORS
         | 
| 121 123 | 
             
                  APPSIGNAL_APP_NAME
         | 
| 122 124 | 
             
                  APPSIGNAL_BIND_ADDRESS
         | 
| 123 125 | 
             
                  APPSIGNAL_CA_FILE_PATH
         | 
| @@ -130,6 +132,7 @@ module Appsignal | |
| 130 132 | 
             
                  APPSIGNAL_LOGGING_ENDPOINT
         | 
| 131 133 | 
             
                  APPSIGNAL_PUSH_API_ENDPOINT
         | 
| 132 134 | 
             
                  APPSIGNAL_PUSH_API_KEY
         | 
| 135 | 
            +
                  APPSIGNAL_SIDEKIQ_REPORT_ERRORS
         | 
| 133 136 | 
             
                  APPSIGNAL_STATSD_PORT
         | 
| 134 137 | 
             
                  APPSIGNAL_WORKING_DIRECTORY_PATH
         | 
| 135 138 | 
             
                  APPSIGNAL_WORKING_DIR_PATH
         | 
| @@ -563,6 +566,11 @@ module Appsignal | |
| 563 566 | 
             
                    config[:activejob_report_errors] = "all"
         | 
| 564 567 | 
             
                  end
         | 
| 565 568 |  | 
| 569 | 
            +
                  if config_hash[:sidekiq_report_errors] == "discard" &&
         | 
| 570 | 
            +
                      !Appsignal::Hooks::SidekiqHook.version_5_1_or_higher?
         | 
| 571 | 
            +
                    config[:sidekiq_report_errors] = "all"
         | 
| 572 | 
            +
                  end
         | 
| 573 | 
            +
             | 
| 566 574 | 
             
                  config
         | 
| 567 575 | 
             
                end
         | 
| 568 576 |  | 
| @@ -5,10 +5,23 @@ module Appsignal | |
| 5 5 | 
             
                class SidekiqHook < Appsignal::Hooks::Hook
         | 
| 6 6 | 
             
                  register :sidekiq
         | 
| 7 7 |  | 
| 8 | 
            -
                  def  | 
| 8 | 
            +
                  def self.version_5_1_or_higher?
         | 
| 9 | 
            +
                    @version_5_1_or_higher ||=
         | 
| 10 | 
            +
                      if dependencies_present?
         | 
| 11 | 
            +
                        Gem::Version.new(::Sidekiq::VERSION) >= Gem::Version.new("5.1.0")
         | 
| 12 | 
            +
                      else
         | 
| 13 | 
            +
                        false
         | 
| 14 | 
            +
                      end
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  def self.dependencies_present?
         | 
| 9 18 | 
             
                    defined?(::Sidekiq)
         | 
| 10 19 | 
             
                  end
         | 
| 11 20 |  | 
| 21 | 
            +
                  def dependencies_present?
         | 
| 22 | 
            +
                    self.class.dependencies_present?
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 12 25 | 
             
                  def install
         | 
| 13 26 | 
             
                    require "appsignal/integrations/sidekiq"
         | 
| 14 27 | 
             
                    Appsignal::Probes.register :sidekiq, Appsignal::Probes::SidekiqProbe
         | 
| @@ -16,6 +29,10 @@ module Appsignal | |
| 16 29 | 
             
                    ::Sidekiq.configure_server do |config|
         | 
| 17 30 | 
             
                      config.error_handlers <<
         | 
| 18 31 | 
             
                        Appsignal::Integrations::SidekiqErrorHandler.new
         | 
| 32 | 
            +
                      if config.respond_to? :death_handlers
         | 
| 33 | 
            +
                        config.death_handlers <<
         | 
| 34 | 
            +
                          Appsignal::Integrations::SidekiqDeathHandler.new
         | 
| 35 | 
            +
                      end
         | 
| 19 36 |  | 
| 20 37 | 
             
                      config.server_middleware do |chain|
         | 
| 21 38 | 
             
                        if chain.respond_to? :prepend
         | 
| @@ -4,32 +4,51 @@ require "yaml" | |
| 4 4 |  | 
| 5 5 | 
             
            module Appsignal
         | 
| 6 6 | 
             
              module Integrations
         | 
| 7 | 
            +
                # Handler for job death events. We get notified when a job has exhausted
         | 
| 8 | 
            +
                # its retries.
         | 
| 9 | 
            +
                #
         | 
| 10 | 
            +
                # This is called before the SidekiqErrorHandler so it doesn't need to worry
         | 
| 11 | 
            +
                # about completing the transaction.
         | 
| 12 | 
            +
                #
         | 
| 13 | 
            +
                # Introduced in Sidekiq 5.1.
         | 
| 14 | 
            +
                class SidekiqDeathHandler
         | 
| 15 | 
            +
                  def call(_job_context, exception)
         | 
| 16 | 
            +
                    return unless Appsignal.config[:sidekiq_report_errors] == "discard"
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    transaction = Appsignal::Transaction.current
         | 
| 19 | 
            +
                    transaction.set_error(exception)
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 7 23 | 
             
                # Error handler for Sidekiq to report errors from jobs and internal Sidekiq
         | 
| 8 24 | 
             
                # errors.
         | 
| 9 25 | 
             
                #
         | 
| 10 26 | 
             
                # @api private
         | 
| 11 27 | 
             
                class SidekiqErrorHandler
         | 
| 28 | 
            +
                  # Sidekiq 7.1.5 introduced the third sidekiq_config argument. It is not
         | 
| 29 | 
            +
                  # given on older Sidekiq versions.
         | 
| 12 30 | 
             
                  def call(exception, sidekiq_context, _sidekiq_config = nil)
         | 
| 13 | 
            -
                     | 
| 14 | 
            -
                      if Appsignal | 
| 15 | 
            -
                        Appsignal::Transaction.current
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                        # Sidekiq error outside of the middleware scope.
         | 
| 18 | 
            -
                        # Can be a job JSON parse error or some other error happening in
         | 
| 19 | 
            -
                        # Sidekiq.
         | 
| 20 | 
            -
                        transaction =
         | 
| 21 | 
            -
                          Appsignal::Transaction.create(
         | 
| 22 | 
            -
                            SecureRandom.uuid, # Newly generated job id
         | 
| 23 | 
            -
                            Appsignal::Transaction::BACKGROUND_JOB,
         | 
| 24 | 
            -
                            Appsignal::Transaction::GenericRequest.new({})
         | 
| 25 | 
            -
                          )
         | 
| 26 | 
            -
                        transaction.set_action_if_nil("SidekiqInternal")
         | 
| 27 | 
            -
                        transaction.set_metadata("sidekiq_error", sidekiq_context[:context])
         | 
| 28 | 
            -
                        transaction.params = { :jobstr => sidekiq_context[:jobstr] }
         | 
| 29 | 
            -
                        transaction
         | 
| 31 | 
            +
                    if Appsignal::Transaction.current?
         | 
| 32 | 
            +
                      if Appsignal.config[:sidekiq_report_errors] == "all"
         | 
| 33 | 
            +
                        transaction = Appsignal::Transaction.current
         | 
| 34 | 
            +
                        transaction.set_error(exception)
         | 
| 30 35 | 
             
                      end
         | 
| 36 | 
            +
                    else
         | 
| 37 | 
            +
                      # Sidekiq error outside of the middleware scope.
         | 
| 38 | 
            +
                      # Can be a job JSON parse error or some other error happening in
         | 
| 39 | 
            +
                      # Sidekiq.
         | 
| 40 | 
            +
                      transaction =
         | 
| 41 | 
            +
                        Appsignal::Transaction.create(
         | 
| 42 | 
            +
                          SecureRandom.uuid, # Newly generated job id
         | 
| 43 | 
            +
                          Appsignal::Transaction::BACKGROUND_JOB,
         | 
| 44 | 
            +
                          Appsignal::Transaction::GenericRequest.new({})
         | 
| 45 | 
            +
                        )
         | 
| 46 | 
            +
                      transaction.set_action_if_nil("SidekiqInternal")
         | 
| 47 | 
            +
                      transaction.set_metadata("sidekiq_error", sidekiq_context[:context])
         | 
| 48 | 
            +
                      transaction.params = { :jobstr => sidekiq_context[:jobstr] }
         | 
| 49 | 
            +
                      transaction.set_error(exception)
         | 
| 50 | 
            +
                    end
         | 
| 31 51 |  | 
| 32 | 
            -
                    transaction.set_error(exception)
         | 
| 33 52 | 
             
                    Appsignal::Transaction.complete_current!
         | 
| 34 53 | 
             
                  end
         | 
| 35 54 | 
             
                end
         | 
| @@ -5,13 +5,15 @@ require "appsignal/rack/sinatra_instrumentation" | |
| 5 5 |  | 
| 6 6 | 
             
            Appsignal.internal_logger.debug("Loading Sinatra (#{Sinatra::VERSION}) integration")
         | 
| 7 7 |  | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
               | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 8 | 
            +
            unless Appsignal.active?
         | 
| 9 | 
            +
              app_settings = ::Sinatra::Application.settings
         | 
| 10 | 
            +
              Appsignal.config = Appsignal::Config.new(
         | 
| 11 | 
            +
                app_settings.root || Dir.pwd,
         | 
| 12 | 
            +
                app_settings.environment
         | 
| 13 | 
            +
              )
         | 
| 13 14 |  | 
| 14 | 
            -
            Appsignal.start_logger
         | 
| 15 | 
            -
            Appsignal.start
         | 
| 15 | 
            +
              Appsignal.start_logger
         | 
| 16 | 
            +
              Appsignal.start
         | 
| 17 | 
            +
            end
         | 
| 16 18 |  | 
| 17 19 | 
             
            ::Sinatra::Base.use(Appsignal::Rack::SinatraBaseInstrumentation) if Appsignal.active?
         | 
| @@ -0,0 +1,127 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "rack"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Appsignal
         | 
| 6 | 
            +
              # @api private
         | 
| 7 | 
            +
              module Rack
         | 
| 8 | 
            +
                class AbstractMiddleware
         | 
| 9 | 
            +
                  def initialize(app, options = {})
         | 
| 10 | 
            +
                    Appsignal.internal_logger.debug "Initializing #{self.class}"
         | 
| 11 | 
            +
                    @app = app
         | 
| 12 | 
            +
                    @options = options
         | 
| 13 | 
            +
                    @request_class = options.fetch(:request_class, ::Rack::Request)
         | 
| 14 | 
            +
                    @params_method = options.fetch(:params_method, :params)
         | 
| 15 | 
            +
                    @instrument_span_name = options.fetch(:instrument_span_name, "process.abstract")
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def call(env)
         | 
| 19 | 
            +
                    if Appsignal.active?
         | 
| 20 | 
            +
                      request = request_for(env)
         | 
| 21 | 
            +
                      # Supported nested instrumentation middlewares by checking if there's
         | 
| 22 | 
            +
                      # already a transaction active for this request.
         | 
| 23 | 
            +
                      wrapped_instrumentation = env.key?(Appsignal::Rack::APPSIGNAL_TRANSACTION)
         | 
| 24 | 
            +
                      transaction =
         | 
| 25 | 
            +
                        if wrapped_instrumentation
         | 
| 26 | 
            +
                          env[Appsignal::Rack::APPSIGNAL_TRANSACTION]
         | 
| 27 | 
            +
                        else
         | 
| 28 | 
            +
                          Appsignal::Transaction.create(
         | 
| 29 | 
            +
                            SecureRandom.uuid,
         | 
| 30 | 
            +
                            Appsignal::Transaction::HTTP_REQUEST,
         | 
| 31 | 
            +
                            request
         | 
| 32 | 
            +
                          )
         | 
| 33 | 
            +
                        end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                      add_transaction_metadata_before(transaction, request)
         | 
| 36 | 
            +
                      if wrapped_instrumentation
         | 
| 37 | 
            +
                        instrument_wrapped_request(request, transaction)
         | 
| 38 | 
            +
                      else
         | 
| 39 | 
            +
                        # Set transaction on the request environment so other nested
         | 
| 40 | 
            +
                        # middleware can detect if there is parent instrumentation
         | 
| 41 | 
            +
                        # middleware active.
         | 
| 42 | 
            +
                        env[Appsignal::Rack::APPSIGNAL_TRANSACTION] = transaction
         | 
| 43 | 
            +
                        instrument_request(request, transaction)
         | 
| 44 | 
            +
                      end
         | 
| 45 | 
            +
                    else
         | 
| 46 | 
            +
                      @app.call(env)
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  private
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  # Another instrumentation middleware is active earlier in the stack, so
         | 
| 53 | 
            +
                  # don't report any exceptions here, the top instrumentation middleware
         | 
| 54 | 
            +
                  # will be the one reporting the exception.
         | 
| 55 | 
            +
                  #
         | 
| 56 | 
            +
                  # Either another {GenericInstrumentation} or {EventHandler} is higher in
         | 
| 57 | 
            +
                  # the stack and will report the exception and complete the transaction.
         | 
| 58 | 
            +
                  #
         | 
| 59 | 
            +
                  # @see {#instrument_request}
         | 
| 60 | 
            +
                  def instrument_wrapped_request(request, transaction)
         | 
| 61 | 
            +
                    instrument_app_call(request.env)
         | 
| 62 | 
            +
                  ensure
         | 
| 63 | 
            +
                    add_transaction_metadata_after(transaction, request)
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  # Instrument the request fully. This is used by the top instrumentation
         | 
| 67 | 
            +
                  # middleware in the middleware stack. Unlike
         | 
| 68 | 
            +
                  # {#instrument_wrapped_request} this will report any exceptions being
         | 
| 69 | 
            +
                  # raised.
         | 
| 70 | 
            +
                  #
         | 
| 71 | 
            +
                  # @see {#instrument_wrapped_request}
         | 
| 72 | 
            +
                  def instrument_request(request, transaction)
         | 
| 73 | 
            +
                    instrument_app_call(request.env)
         | 
| 74 | 
            +
                  rescue Exception => error # rubocop:disable Lint/RescueException
         | 
| 75 | 
            +
                    transaction.set_error(error)
         | 
| 76 | 
            +
                    raise error
         | 
| 77 | 
            +
                  ensure
         | 
| 78 | 
            +
                    add_transaction_metadata_after(transaction, request)
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                    # Complete transaction because this is the top instrumentation middleware.
         | 
| 81 | 
            +
                    Appsignal::Transaction.complete_current!
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  def instrument_app_call(env)
         | 
| 85 | 
            +
                    Appsignal.instrument(@instrument_span_name) do
         | 
| 86 | 
            +
                      @app.call(env)
         | 
| 87 | 
            +
                    end
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  # Add metadata to the transaction based on the request environment.
         | 
| 91 | 
            +
                  # Override this method to set metadata before the app is called.
         | 
| 92 | 
            +
                  # Call `super` to also include the default set metadata.
         | 
| 93 | 
            +
                  def add_transaction_metadata_before(transaction, request)
         | 
| 94 | 
            +
                    params = params_for(request)
         | 
| 95 | 
            +
                    transaction.params = params if params
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  # Add metadata to the transaction based on the request environment.
         | 
| 99 | 
            +
                  # Override this method to set metadata after the app is called.
         | 
| 100 | 
            +
                  # Call `super` to also include the default set metadata.
         | 
| 101 | 
            +
                  def add_transaction_metadata_after(transaction, request)
         | 
| 102 | 
            +
                    default_action =
         | 
| 103 | 
            +
                      request.env["appsignal.route"] || request.env["appsignal.action"]
         | 
| 104 | 
            +
                    transaction.set_action_if_nil(default_action)
         | 
| 105 | 
            +
                    transaction.set_metadata("path", request.path)
         | 
| 106 | 
            +
                    transaction.set_metadata("method", request.request_method)
         | 
| 107 | 
            +
                    transaction.set_http_or_background_queue_start
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                  def params_for(request)
         | 
| 111 | 
            +
                    return unless request.respond_to?(@params_method)
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                    request.send(@params_method)
         | 
| 114 | 
            +
                  rescue => error
         | 
| 115 | 
            +
                    # Getting params from the request has been know to fail.
         | 
| 116 | 
            +
                    Appsignal.internal_logger.debug(
         | 
| 117 | 
            +
                      "Exception while getting params in #{self.class} from '#{@params_method}': #{error}"
         | 
| 118 | 
            +
                    )
         | 
| 119 | 
            +
                    nil
         | 
| 120 | 
            +
                  end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                  def request_for(env)
         | 
| 123 | 
            +
                    @request_class.new(env)
         | 
| 124 | 
            +
                  end
         | 
| 125 | 
            +
                end
         | 
| 126 | 
            +
              end
         | 
| 127 | 
            +
            end
         | 
| @@ -3,6 +3,7 @@ | |
| 3 3 | 
             
            module Appsignal
         | 
| 4 4 | 
             
              module Rack
         | 
| 5 5 | 
             
                APPSIGNAL_TRANSACTION = "appsignal.transaction"
         | 
| 6 | 
            +
                APPSIGNAL_EVENT_HANDLER_ID = "appsignal.event_handler_id"
         | 
| 6 7 | 
             
                RACK_AFTER_REPLY = "rack.after_reply"
         | 
| 7 8 |  | 
| 8 9 | 
             
                class EventHandler
         | 
| @@ -16,8 +17,22 @@ module Appsignal | |
| 16 17 | 
             
                    )
         | 
| 17 18 | 
             
                  end
         | 
| 18 19 |  | 
| 20 | 
            +
                  attr_reader :id
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def initialize
         | 
| 23 | 
            +
                    @id = SecureRandom.uuid
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def request_handler?(given_id)
         | 
| 27 | 
            +
                    id == given_id
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 19 30 | 
             
                  def on_start(request, _response)
         | 
| 31 | 
            +
                    event_handler = self
         | 
| 20 32 | 
             
                    self.class.safe_execution("Appsignal::Rack::EventHandler#on_start") do
         | 
| 33 | 
            +
                      request.env[APPSIGNAL_EVENT_HANDLER_ID] ||= id
         | 
| 34 | 
            +
                      return unless request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
         | 
| 35 | 
            +
             | 
| 21 36 | 
             
                      transaction = Appsignal::Transaction.create(
         | 
| 22 37 | 
             
                        SecureRandom.uuid,
         | 
| 23 38 | 
             
                        Appsignal::Transaction::HTTP_REQUEST,
         | 
| @@ -29,6 +44,8 @@ module Appsignal | |
| 29 44 | 
             
                      request.env[RACK_AFTER_REPLY] << proc do
         | 
| 30 45 | 
             
                        Appsignal::Rack::EventHandler
         | 
| 31 46 | 
             
                          .safe_execution("Appsignal::Rack::EventHandler's after_reply") do
         | 
| 47 | 
            +
                          next unless event_handler.request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
         | 
| 48 | 
            +
             | 
| 32 49 | 
             
                          transaction.finish_event("process_request.rack", "", "")
         | 
| 33 50 | 
             
                          transaction.set_http_or_background_queue_start
         | 
| 34 51 |  | 
| @@ -48,6 +65,8 @@ module Appsignal | |
| 48 65 |  | 
| 49 66 | 
             
                  def on_error(request, _response, error)
         | 
| 50 67 | 
             
                    self.class.safe_execution("Appsignal::Rack::EventHandler#on_error") do
         | 
| 68 | 
            +
                      return unless request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
         | 
| 69 | 
            +
             | 
| 51 70 | 
             
                      transaction = request.env[APPSIGNAL_TRANSACTION]
         | 
| 52 71 | 
             
                      return unless transaction
         | 
| 53 72 |  | 
| @@ -55,13 +74,22 @@ module Appsignal | |
| 55 74 | 
             
                    end
         | 
| 56 75 | 
             
                  end
         | 
| 57 76 |  | 
| 58 | 
            -
                  def on_finish(request,  | 
| 77 | 
            +
                  def on_finish(request, response)
         | 
| 59 78 | 
             
                    self.class.safe_execution("Appsignal::Rack::EventHandler#on_finish") do
         | 
| 79 | 
            +
                      return unless request_handler?(request.env[APPSIGNAL_EVENT_HANDLER_ID])
         | 
| 80 | 
            +
             | 
| 60 81 | 
             
                      transaction = request.env[APPSIGNAL_TRANSACTION]
         | 
| 61 82 | 
             
                      return unless transaction
         | 
| 62 83 |  | 
| 63 84 | 
             
                      transaction.finish_event("process_request.rack", "", "")
         | 
| 85 | 
            +
                      transaction.set_tags(:response_status => response.status)
         | 
| 64 86 | 
             
                      transaction.set_http_or_background_queue_start
         | 
| 87 | 
            +
                      Appsignal.increment_counter(
         | 
| 88 | 
            +
                        :response_status,
         | 
| 89 | 
            +
                        1,
         | 
| 90 | 
            +
                        :status => response.status,
         | 
| 91 | 
            +
                        :namespace => format_namespace(transaction.namespace)
         | 
| 92 | 
            +
                      )
         | 
| 65 93 |  | 
| 66 94 | 
             
                      # Make sure the current transaction is always closed when the request
         | 
| 67 95 | 
             
                      # is finished
         | 
| @@ -5,42 +5,15 @@ require "rack" | |
| 5 5 | 
             
            module Appsignal
         | 
| 6 6 | 
             
              # @api private
         | 
| 7 7 | 
             
              module Rack
         | 
| 8 | 
            -
                class GenericInstrumentation
         | 
| 8 | 
            +
                class GenericInstrumentation < AbstractMiddleware
         | 
| 9 9 | 
             
                  def initialize(app, options = {})
         | 
| 10 | 
            -
                     | 
| 11 | 
            -
                     | 
| 12 | 
            -
                    @options = options
         | 
| 10 | 
            +
                    options[:instrument_span_name] ||= "process_action.generic"
         | 
| 11 | 
            +
                    super
         | 
| 13 12 | 
             
                  end
         | 
| 14 13 |  | 
| 15 | 
            -
                  def  | 
| 16 | 
            -
                     | 
| 17 | 
            -
             | 
| 18 | 
            -
                    else
         | 
| 19 | 
            -
                      @app.call(env)
         | 
| 20 | 
            -
                    end
         | 
| 21 | 
            -
                  end
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                  def call_with_appsignal_monitoring(env)
         | 
| 24 | 
            -
                    request = ::Rack::Request.new(env)
         | 
| 25 | 
            -
                    transaction = Appsignal::Transaction.create(
         | 
| 26 | 
            -
                      SecureRandom.uuid,
         | 
| 27 | 
            -
                      Appsignal::Transaction::HTTP_REQUEST,
         | 
| 28 | 
            -
                      request
         | 
| 29 | 
            -
                    )
         | 
| 30 | 
            -
                    begin
         | 
| 31 | 
            -
                      Appsignal.instrument("process_action.generic") do
         | 
| 32 | 
            -
                        @app.call(env)
         | 
| 33 | 
            -
                      end
         | 
| 34 | 
            -
                    rescue Exception => error # rubocop:disable Lint/RescueException
         | 
| 35 | 
            -
                      transaction.set_error(error)
         | 
| 36 | 
            -
                      raise error
         | 
| 37 | 
            -
                    ensure
         | 
| 38 | 
            -
                      transaction.set_action_if_nil(env["appsignal.route"] || "unknown")
         | 
| 39 | 
            -
                      transaction.set_metadata("path", request.path)
         | 
| 40 | 
            -
                      transaction.set_metadata("method", request.request_method)
         | 
| 41 | 
            -
                      transaction.set_http_or_background_queue_start
         | 
| 42 | 
            -
                      Appsignal::Transaction.complete_current!
         | 
| 43 | 
            -
                    end
         | 
| 14 | 
            +
                  def add_transaction_metadata_after(transaction, request)
         | 
| 15 | 
            +
                    super
         | 
| 16 | 
            +
                    transaction.set_action_if_nil("unknown")
         | 
| 44 17 | 
             
                  end
         | 
| 45 18 | 
             
                end
         | 
| 46 19 | 
             
              end
         | 
| @@ -28,53 +28,29 @@ module Appsignal | |
| 28 28 | 
             
                  end
         | 
| 29 29 | 
             
                end
         | 
| 30 30 |  | 
| 31 | 
            -
                class SinatraBaseInstrumentation
         | 
| 31 | 
            +
                class SinatraBaseInstrumentation < AbstractMiddleware
         | 
| 32 32 | 
             
                  attr_reader :raise_errors_on
         | 
| 33 33 |  | 
| 34 34 | 
             
                  def initialize(app, options = {})
         | 
| 35 | 
            -
                     | 
| 36 | 
            -
                     | 
| 37 | 
            -
                     | 
| 38 | 
            -
                     | 
| 35 | 
            +
                    options[:request_class] ||= Sinatra::Request
         | 
| 36 | 
            +
                    options[:params_method] ||= :params
         | 
| 37 | 
            +
                    options[:instrument_span_name] ||= "process_action.sinatra"
         | 
| 38 | 
            +
                    super
         | 
| 39 | 
            +
                    @raise_errors_on = raise_errors?(app)
         | 
| 39 40 | 
             
                  end
         | 
| 40 41 |  | 
| 41 | 
            -
                   | 
| 42 | 
            -
                    if Appsignal.active?
         | 
| 43 | 
            -
                      call_with_appsignal_monitoring(env)
         | 
| 44 | 
            -
                    else
         | 
| 45 | 
            -
                      @app.call(env)
         | 
| 46 | 
            -
                    end
         | 
| 47 | 
            -
                  end
         | 
| 42 | 
            +
                  private
         | 
| 48 43 |  | 
| 49 | 
            -
                  def  | 
| 50 | 
            -
                     | 
| 51 | 
            -
                     | 
| 52 | 
            -
                     | 
| 53 | 
            -
                     | 
| 54 | 
            -
             | 
| 55 | 
            -
                       | 
| 56 | 
            -
                      request,
         | 
| 57 | 
            -
                      options
         | 
| 58 | 
            -
                    )
         | 
| 59 | 
            -
                    begin
         | 
| 60 | 
            -
                      Appsignal.instrument("process_action.sinatra") do
         | 
| 61 | 
            -
                        @app.call(env)
         | 
| 62 | 
            -
                      end
         | 
| 63 | 
            -
                    rescue Exception => error # rubocop:disable Lint/RescueException
         | 
| 64 | 
            -
                      transaction.set_error(error)
         | 
| 65 | 
            -
                      raise error
         | 
| 66 | 
            -
                    ensure
         | 
| 67 | 
            -
                      # If raise_error is off versions of Sinatra don't raise errors, but store
         | 
| 68 | 
            -
                      # them in the sinatra.error env var.
         | 
| 69 | 
            -
                      if !raise_errors_on && env["sinatra.error"] && !env["sinatra.skip_appsignal_error"]
         | 
| 70 | 
            -
                        transaction.set_error(env["sinatra.error"])
         | 
| 71 | 
            -
                      end
         | 
| 72 | 
            -
                      transaction.set_action_if_nil(action_name(env))
         | 
| 73 | 
            -
                      transaction.set_metadata("path", request.path)
         | 
| 74 | 
            -
                      transaction.set_metadata("method", request.request_method)
         | 
| 75 | 
            -
                      transaction.set_http_or_background_queue_start
         | 
| 76 | 
            -
                      Appsignal::Transaction.complete_current!
         | 
| 44 | 
            +
                  def add_transaction_metadata_after(transaction, request)
         | 
| 45 | 
            +
                    env = request.env
         | 
| 46 | 
            +
                    transaction.set_action_if_nil(action_name(env))
         | 
| 47 | 
            +
                    # If raise_error is off versions of Sinatra don't raise errors, but store
         | 
| 48 | 
            +
                    # them in the sinatra.error env var.
         | 
| 49 | 
            +
                    if !raise_errors_on && env["sinatra.error"] && !env["sinatra.skip_appsignal_error"]
         | 
| 50 | 
            +
                      transaction.set_error(env["sinatra.error"])
         | 
| 77 51 | 
             
                    end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    super
         | 
| 78 54 | 
             
                  end
         | 
| 79 55 |  | 
| 80 56 | 
             
                  def action_name(env)
         | 
| @@ -88,8 +64,6 @@ module Appsignal | |
| 88 64 | 
             
                    end
         | 
| 89 65 | 
             
                  end
         | 
| 90 66 |  | 
| 91 | 
            -
                  private
         | 
| 92 | 
            -
             | 
| 93 67 | 
             
                  def raise_errors?(app)
         | 
| 94 68 | 
             
                    app.respond_to?(:settings) &&
         | 
| 95 69 | 
             
                      app.settings.respond_to?(:raise_errors) &&
         | 
    
        data/lib/appsignal/version.rb
    CHANGED
    
    
    
        data/lib/appsignal.rb
    CHANGED
    
    | @@ -326,6 +326,7 @@ require "appsignal/garbage_collection" | |
| 326 326 | 
             
            require "appsignal/integrations/railtie" if defined?(::Rails)
         | 
| 327 327 | 
             
            require "appsignal/transaction"
         | 
| 328 328 | 
             
            require "appsignal/version"
         | 
| 329 | 
            +
            require "appsignal/rack/abstract_middleware"
         | 
| 329 330 | 
             
            require "appsignal/rack/generic_instrumentation"
         | 
| 330 331 | 
             
            require "appsignal/rack/event_handler"
         | 
| 331 332 | 
             
            require "appsignal/transmitter"
         | 
| @@ -187,6 +187,7 @@ describe Appsignal::Config do | |
| 187 187 | 
             
                    :send_environment_metadata      => true,
         | 
| 188 188 | 
             
                    :send_params                    => true,
         | 
| 189 189 | 
             
                    :send_session_data              => true,
         | 
| 190 | 
            +
                    :sidekiq_report_errors          => "all",
         | 
| 190 191 | 
             
                    :transaction_debug_mode         => false
         | 
| 191 192 | 
             
                  )
         | 
| 192 193 | 
             
                end
         | 
| @@ -583,6 +584,33 @@ describe Appsignal::Config do | |
| 583 584 | 
             
                    end
         | 
| 584 585 | 
             
                  end
         | 
| 585 586 | 
             
                end
         | 
| 587 | 
            +
             | 
| 588 | 
            +
                context "sidekiq_report_errors" do
         | 
| 589 | 
            +
                  let(:config_options) { { :sidekiq_report_errors => "discard" } }
         | 
| 590 | 
            +
                  before do
         | 
| 591 | 
            +
                    if Appsignal::Hooks::SidekiqHook.instance_variable_defined?(:@version_5_1_or_higher)
         | 
| 592 | 
            +
                      Appsignal::Hooks::SidekiqHook.remove_instance_variable(:@version_5_1_or_higher)
         | 
| 593 | 
            +
                    end
         | 
| 594 | 
            +
                  end
         | 
| 595 | 
            +
             | 
| 596 | 
            +
                  context "when Sidekiq >= 5.1 and 'discard'" do
         | 
| 597 | 
            +
                    before { stub_const("Sidekiq::VERSION", "5.1.0") }
         | 
| 598 | 
            +
             | 
| 599 | 
            +
                    it "does not override the sidekiq_report_errors value" do
         | 
| 600 | 
            +
                      expect(config[:sidekiq_report_errors]).to eq("discard")
         | 
| 601 | 
            +
                      expect(config.override_config[:sidekiq_report_errors]).to be_nil
         | 
| 602 | 
            +
                    end
         | 
| 603 | 
            +
                  end
         | 
| 604 | 
            +
             | 
| 605 | 
            +
                  context "when Sidekiq < 5.1 and 'discard'" do
         | 
| 606 | 
            +
                    before { stub_const("Sidekiq::VERSION", "5.0.0") }
         | 
| 607 | 
            +
             | 
| 608 | 
            +
                    it "sets sidekiq_report_errors to 'all'" do
         | 
| 609 | 
            +
                      expect(config[:sidekiq_report_errors]).to eq("all")
         | 
| 610 | 
            +
                      expect(config.override_config[:sidekiq_report_errors]).to eq("all")
         | 
| 611 | 
            +
                    end
         | 
| 612 | 
            +
                  end
         | 
| 613 | 
            +
                end
         | 
| 586 614 | 
             
              end
         | 
| 587 615 |  | 
| 588 616 | 
             
              describe "config keys" do
         |