scout_apm 2.6.6 → 4.0.3
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/.github/workflows/test.yml +49 -0
 - data/.rubocop.yml +2 -5
 - data/.travis.yml +3 -7
 - data/CHANGELOG.markdown +45 -0
 - data/Gemfile +1 -8
 - data/gems/rails6.gemfile +1 -1
 - data/lib/scout_apm.rb +22 -1
 - data/lib/scout_apm/agent.rb +22 -0
 - data/lib/scout_apm/agent_context.rb +14 -2
 - data/lib/scout_apm/background_job_integrations/sidekiq.rb +2 -2
 - data/lib/scout_apm/config.rb +17 -2
 - data/lib/scout_apm/detailed_trace.rb +2 -1
 - data/lib/scout_apm/environment.rb +16 -1
 - data/lib/scout_apm/error.rb +27 -0
 - data/lib/scout_apm/error_service.rb +32 -0
 - data/lib/scout_apm/error_service/error_buffer.rb +39 -0
 - data/lib/scout_apm/error_service/error_record.rb +211 -0
 - data/lib/scout_apm/error_service/ignored_exceptions.rb +66 -0
 - data/lib/scout_apm/error_service/middleware.rb +32 -0
 - data/lib/scout_apm/error_service/notifier.rb +33 -0
 - data/lib/scout_apm/error_service/payload.rb +47 -0
 - data/lib/scout_apm/error_service/periodic_work.rb +17 -0
 - data/lib/scout_apm/error_service/railtie.rb +11 -0
 - data/lib/scout_apm/error_service/sidekiq.rb +80 -0
 - data/lib/scout_apm/extensions/transaction_callback_payload.rb +1 -1
 - data/lib/scout_apm/instrument_manager.rb +1 -0
 - data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +47 -26
 - data/lib/scout_apm/instruments/action_view.rb +21 -8
 - data/lib/scout_apm/instruments/active_record.rb +17 -28
 - data/lib/scout_apm/instruments/typhoeus.rb +88 -0
 - data/lib/scout_apm/layer.rb +1 -1
 - data/lib/scout_apm/middleware.rb +1 -1
 - data/lib/scout_apm/remote/server.rb +13 -1
 - data/lib/scout_apm/reporter.rb +8 -3
 - data/lib/scout_apm/serializers/payload_serializer_to_json.rb +28 -10
 - data/lib/scout_apm/slow_policy/age_policy.rb +33 -0
 - data/lib/scout_apm/slow_policy/percent_policy.rb +22 -0
 - data/lib/scout_apm/slow_policy/percentile_policy.rb +24 -0
 - data/lib/scout_apm/slow_policy/policy.rb +21 -0
 - data/lib/scout_apm/slow_policy/speed_policy.rb +16 -0
 - data/lib/scout_apm/slow_request_policy.rb +18 -77
 - data/lib/scout_apm/utils/sql_sanitizer.rb +1 -0
 - data/lib/scout_apm/utils/sql_sanitizer_regex.rb +3 -3
 - data/lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb +1 -0
 - data/lib/scout_apm/version.rb +1 -1
 - data/scout_apm.gemspec +6 -6
 - data/test/unit/agent_context_test.rb +29 -0
 - data/test/unit/environment_test.rb +2 -2
 - data/test/unit/error_service/error_buffer_test.rb +25 -0
 - data/test/unit/error_service/ignored_exceptions_test.rb +49 -0
 - data/test/unit/serializers/payload_serializer_test.rb +36 -0
 - data/test/unit/slow_request_policy_test.rb +41 -13
 - data/test/unit/sql_sanitizer_test.rb +38 -0
 - metadata +26 -61
 - data/lib/scout_apm/slow_job_policy.rb +0 -111
 - data/test/unit/slow_job_policy_test.rb +0 -6
 
| 
         @@ -0,0 +1,11 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module ScoutApm
         
     | 
| 
      
 2 
     | 
    
         
            +
              module ErrorService
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Railtie < Rails::Railtie
         
     | 
| 
      
 4 
     | 
    
         
            +
                  initializer "scoutapm_error_service.middleware" do |app|
         
     | 
| 
      
 5 
     | 
    
         
            +
                    next if ScoutApm::Agent.instance.config.value("error_service")
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                    app.config.middleware.insert_after ActionDispatch::DebugExceptions, ScoutApm::ErrorService::Rack
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,80 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module ScoutApm
         
     | 
| 
      
 2 
     | 
    
         
            +
              module ErrorService
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Sidekiq
         
     | 
| 
      
 4 
     | 
    
         
            +
                  def initialize
         
     | 
| 
      
 5 
     | 
    
         
            +
                    @context = ScoutApm::Agent.instance.context
         
     | 
| 
      
 6 
     | 
    
         
            +
                  end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  def install
         
     | 
| 
      
 9 
     | 
    
         
            +
                    return false unless defined?(::Sidekiq)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                    if ::Sidekiq::VERSION < "3"
         
     | 
| 
      
 12 
     | 
    
         
            +
                      install_sidekiq_with_middleware
         
     | 
| 
      
 13 
     | 
    
         
            +
                    else
         
     | 
| 
      
 14 
     | 
    
         
            +
                      install_sidekiq_with_error_handler
         
     | 
| 
      
 15 
     | 
    
         
            +
                    end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                    true
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  def install_sidekiq_with_middleware
         
     | 
| 
      
 21 
     | 
    
         
            +
                    # old behavior
         
     | 
| 
      
 22 
     | 
    
         
            +
                    ::Sidekiq.configure_server do |config|
         
     | 
| 
      
 23 
     | 
    
         
            +
                      config.server_middleware do |chain|
         
     | 
| 
      
 24 
     | 
    
         
            +
                        chain.add ScoutApm::ErrorService::Sidekiq::SidekiqExceptionMiddleware
         
     | 
| 
      
 25 
     | 
    
         
            +
                      end
         
     | 
| 
      
 26 
     | 
    
         
            +
                    end
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def install_sidekiq_with_error_handler
         
     | 
| 
      
 30 
     | 
    
         
            +
                    ::Sidekiq.configure_server do |config|
         
     | 
| 
      
 31 
     | 
    
         
            +
                      config.error_handlers << proc { |exception, job_info|
         
     | 
| 
      
 32 
     | 
    
         
            +
                        context = ScoutApm::Agent.instance.context
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                        # Bail out early, and reraise if the error is not interesting.
         
     | 
| 
      
 35 
     | 
    
         
            +
                        if context.ignored_exceptions.ignored?(exception)
         
     | 
| 
      
 36 
     | 
    
         
            +
                          raise
         
     | 
| 
      
 37 
     | 
    
         
            +
                        end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                        job_class =
         
     | 
| 
      
 40 
     | 
    
         
            +
                          begin
         
     | 
| 
      
 41 
     | 
    
         
            +
                            job_class = job_info[:job]["class"]
         
     | 
| 
      
 42 
     | 
    
         
            +
                            job_class = job_info[:job]["args"][0]["job_class"] if job_class == "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
         
     | 
| 
      
 43 
     | 
    
         
            +
                            job_class
         
     | 
| 
      
 44 
     | 
    
         
            +
                          rescue
         
     | 
| 
      
 45 
     | 
    
         
            +
                            "UnknownJob"
         
     | 
| 
      
 46 
     | 
    
         
            +
                          end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                        # Capture the error for further processing and shipping
         
     | 
| 
      
 49 
     | 
    
         
            +
                        context.error_buffer.capture(exception, job_info.merge(:custom_controller => job_class))
         
     | 
| 
      
 50 
     | 
    
         
            +
                      }
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  class SidekiqExceptionMiddleware
         
     | 
| 
      
 55 
     | 
    
         
            +
                    def call(worker, msg, queue)
         
     | 
| 
      
 56 
     | 
    
         
            +
                      yield
         
     | 
| 
      
 57 
     | 
    
         
            +
                    rescue => exception
         
     | 
| 
      
 58 
     | 
    
         
            +
                      context = ScoutApm::Agent.instance.context
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                      # Bail out early, and reraise if the error is not interesting.
         
     | 
| 
      
 61 
     | 
    
         
            +
                      if context.ignored_exceptions.ignored?(exception)
         
     | 
| 
      
 62 
     | 
    
         
            +
                        raise
         
     | 
| 
      
 63 
     | 
    
         
            +
                      end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                      # Capture the error for further processing and shipping
         
     | 
| 
      
 66 
     | 
    
         
            +
                      context.error_buffer.capture(
         
     | 
| 
      
 67 
     | 
    
         
            +
                        exception,
         
     | 
| 
      
 68 
     | 
    
         
            +
                        {
         
     | 
| 
      
 69 
     | 
    
         
            +
                          :custom_params => msg,
         
     | 
| 
      
 70 
     | 
    
         
            +
                          :custom_controller => msg["class"]
         
     | 
| 
      
 71 
     | 
    
         
            +
                        }
         
     | 
| 
      
 72 
     | 
    
         
            +
                      )
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                      # Finally, reraise
         
     | 
| 
      
 75 
     | 
    
         
            +
                      raise exception
         
     | 
| 
      
 76 
     | 
    
         
            +
                    end
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
              end
         
     | 
| 
      
 80 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -26,7 +26,7 @@ module ScoutApm 
     | 
|
| 
       26 
26 
     | 
    
         
             
                  # The time in queue of the transaction in ms. If not present, +nil+ is returned as this is unknown.
         
     | 
| 
       27 
27 
     | 
    
         
             
                  def queue_time_ms
         
     | 
| 
       28 
28 
     | 
    
         
             
                    # Controller logic
         
     | 
| 
       29 
     | 
    
         
            -
                    if converter_results[:queue_time] && converter_results[: 
     | 
| 
      
 29 
     | 
    
         
            +
                    if converter_results[:queue_time] && converter_results[:queue_time].any?
         
     | 
| 
       30 
30 
     | 
    
         
             
                      converter_results[:queue_time].values.first.total_call_time*1000 # ms
         
     | 
| 
       31 
31 
     | 
    
         
             
                    # Job logic
         
     | 
| 
       32 
32 
     | 
    
         
             
                    elsif converter_results[:job]
         
     | 
| 
         @@ -30,6 +30,7 @@ module ScoutApm 
     | 
|
| 
       30 
30 
     | 
    
         
             
                  install_instrument(ScoutApm::Instruments::Moped)
         
     | 
| 
       31 
31 
     | 
    
         
             
                  install_instrument(ScoutApm::Instruments::Mongoid)
         
     | 
| 
       32 
32 
     | 
    
         
             
                  install_instrument(ScoutApm::Instruments::NetHttp)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  install_instrument(ScoutApm::Instruments::Typhoeus)
         
     | 
| 
       33 
34 
     | 
    
         
             
                  install_instrument(ScoutApm::Instruments::HttpClient)
         
     | 
| 
       34 
35 
     | 
    
         
             
                  install_instrument(ScoutApm::Instruments::Memcached)
         
     | 
| 
       35 
36 
     | 
    
         
             
                  install_instrument(ScoutApm::Instruments::Redis)
         
     | 
| 
         @@ -17,46 +17,73 @@ module ScoutApm 
     | 
|
| 
       17 
17 
     | 
    
         
             
                    @installed
         
     | 
| 
       18 
18 
     | 
    
         
             
                  end
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
      
 20 
     | 
    
         
            +
                  def installed!
         
     | 
| 
      
 21 
     | 
    
         
            +
                    @installed = true
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
       20 
24 
     | 
    
         
             
                  def install
         
     | 
| 
       21 
     | 
    
         
            -
                     
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
                     
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
                     
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
      
 25 
     | 
    
         
            +
                    if !defined?(::ActiveSupport)
         
     | 
| 
      
 26 
     | 
    
         
            +
                      return
         
     | 
| 
      
 27 
     | 
    
         
            +
                    end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                    # The block below runs with `self` equal to the ActionController::Base or ::API module, not this class we're in now. By saving an instance of ourselves into the `this` variable, we can continue accessing what we need.
         
     | 
| 
      
 30 
     | 
    
         
            +
                    this = self
         
     | 
| 
       27 
31 
     | 
    
         | 
| 
      
 32 
     | 
    
         
            +
                    ActiveSupport.on_load(:action_controller) do
         
     | 
| 
      
 33 
     | 
    
         
            +
                      if this.installed?
         
     | 
| 
      
 34 
     | 
    
         
            +
                        this.logger.info("Skipping ActionController - Already Ran")
         
     | 
| 
      
 35 
     | 
    
         
            +
                        next
         
     | 
| 
      
 36 
     | 
    
         
            +
                      else
         
     | 
| 
      
 37 
     | 
    
         
            +
                        this.logger.info("Instrumenting ActionController (on_load)")
         
     | 
| 
      
 38 
     | 
    
         
            +
                        this.installed!
         
     | 
| 
      
 39 
     | 
    
         
            +
                      end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                      # We previously instrumented ActionController::Metal, which missed
         
     | 
| 
      
 42 
     | 
    
         
            +
                      # before and after filter timing. Instrumenting Base includes those
         
     | 
| 
      
 43 
     | 
    
         
            +
                      # filters, at the expense of missing out on controllers that don't use
         
     | 
| 
      
 44 
     | 
    
         
            +
                      # the full Rails stack.
         
     | 
| 
       28 
45 
     | 
    
         
             
                      if defined?(::ActionController::Base)
         
     | 
| 
       29 
     | 
    
         
            -
                        logger.info "Instrumenting ActionController::Base"
         
     | 
| 
      
 46 
     | 
    
         
            +
                        this.logger.info "Instrumenting ActionController::Base"
         
     | 
| 
       30 
47 
     | 
    
         
             
                        ::ActionController::Base.class_eval do
         
     | 
| 
       31 
     | 
    
         
            -
                          # include ScoutApm::Tracer
         
     | 
| 
       32 
48 
     | 
    
         
             
                          include ScoutApm::Instruments::ActionControllerBaseInstruments
         
     | 
| 
       33 
49 
     | 
    
         
             
                        end
         
     | 
| 
       34 
50 
     | 
    
         
             
                      end
         
     | 
| 
       35 
51 
     | 
    
         | 
| 
       36 
52 
     | 
    
         
             
                      if defined?(::ActionController::Metal)
         
     | 
| 
       37 
     | 
    
         
            -
                        logger.info "Instrumenting ActionController::Metal"
         
     | 
| 
      
 53 
     | 
    
         
            +
                        this.logger.info "Instrumenting ActionController::Metal"
         
     | 
| 
       38 
54 
     | 
    
         
             
                        ::ActionController::Metal.class_eval do
         
     | 
| 
       39 
55 
     | 
    
         
             
                          include ScoutApm::Instruments::ActionControllerMetalInstruments
         
     | 
| 
       40 
56 
     | 
    
         
             
                        end
         
     | 
| 
       41 
57 
     | 
    
         
             
                      end
         
     | 
| 
       42 
58 
     | 
    
         | 
| 
       43 
59 
     | 
    
         
             
                      if defined?(::ActionController::API)
         
     | 
| 
       44 
     | 
    
         
            -
                        logger.info "Instrumenting ActionController::Api"
         
     | 
| 
      
 60 
     | 
    
         
            +
                        this.logger.info "Instrumenting ActionController::Api"
         
     | 
| 
       45 
61 
     | 
    
         
             
                        ::ActionController::API.class_eval do
         
     | 
| 
       46 
62 
     | 
    
         
             
                          include ScoutApm::Instruments::ActionControllerAPIInstruments
         
     | 
| 
       47 
63 
     | 
    
         
             
                        end
         
     | 
| 
       48 
64 
     | 
    
         
             
                      end
         
     | 
| 
       49 
65 
     | 
    
         
             
                    end
         
     | 
| 
       50 
66 
     | 
    
         | 
| 
       51 
     | 
    
         
            -
                     
     | 
| 
       52 
     | 
    
         
            -
                    # we can insert this multiple times into the ancestors
         
     | 
| 
       53 
     | 
    
         
            -
                    # stack. Otherwise it only exists the first time you include it
         
     | 
| 
       54 
     | 
    
         
            -
                    # (under Metal, instead of under API) and we miss instrumenting
         
     | 
| 
       55 
     | 
    
         
            -
                    # before_action callbacks
         
     | 
| 
      
 67 
     | 
    
         
            +
                    ScoutApm::Agent.instance.context.logger.info("Instrumenting ActionController (hook installed)")
         
     | 
| 
       56 
68 
     | 
    
         
             
                  end
         
     | 
| 
       57 
69 
     | 
    
         | 
| 
      
 70 
     | 
    
         
            +
                  # Returns a new anonymous module each time it is called. So
         
     | 
| 
      
 71 
     | 
    
         
            +
                  # we can insert this multiple times into the ancestors
         
     | 
| 
      
 72 
     | 
    
         
            +
                  # stack. Otherwise it only exists the first time you include it
         
     | 
| 
      
 73 
     | 
    
         
            +
                  # (under Metal, instead of under API) and we miss instrumenting
         
     | 
| 
      
 74 
     | 
    
         
            +
                  # before_action callbacks
         
     | 
| 
       58 
75 
     | 
    
         
             
                  def self.build_instrument_module
         
     | 
| 
       59 
76 
     | 
    
         
             
                    Module.new do
         
     | 
| 
      
 77 
     | 
    
         
            +
                      # Determine the URI of this request to capture. Overridable by users in their controller.
         
     | 
| 
      
 78 
     | 
    
         
            +
                      def scout_transaction_uri(config=ScoutApm::Agent.instance.context.config)
         
     | 
| 
      
 79 
     | 
    
         
            +
                        case config.value("uri_reporting")
         
     | 
| 
      
 80 
     | 
    
         
            +
                        when 'path'
         
     | 
| 
      
 81 
     | 
    
         
            +
                          request.path # strips off the query string for more security
         
     | 
| 
      
 82 
     | 
    
         
            +
                        else # default handles filtered params
         
     | 
| 
      
 83 
     | 
    
         
            +
                          request.filtered_path
         
     | 
| 
      
 84 
     | 
    
         
            +
                        end
         
     | 
| 
      
 85 
     | 
    
         
            +
                      end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
       60 
87 
     | 
    
         
             
                      def process_action(*args)
         
     | 
| 
       61 
88 
     | 
    
         
             
                        req = ScoutApm::RequestManager.lookup
         
     | 
| 
       62 
89 
     | 
    
         
             
                        current_layer = req.current_layer
         
     | 
| 
         @@ -72,7 +99,11 @@ module ScoutApm 
     | 
|
| 
       72 
99 
     | 
    
         
             
                          # Don't start a new layer if ActionController::API or ActionController::Base handled it already.
         
     | 
| 
       73 
100 
     | 
    
         
             
                          super
         
     | 
| 
       74 
101 
     | 
    
         
             
                        else
         
     | 
| 
       75 
     | 
    
         
            -
                           
     | 
| 
      
 102 
     | 
    
         
            +
                          begin
         
     | 
| 
      
 103 
     | 
    
         
            +
                            uri = scout_transaction_uri
         
     | 
| 
      
 104 
     | 
    
         
            +
                            req.annotate_request(:uri => uri)
         
     | 
| 
      
 105 
     | 
    
         
            +
                          rescue
         
     | 
| 
      
 106 
     | 
    
         
            +
                          end
         
     | 
| 
       76 
107 
     | 
    
         | 
| 
       77 
108 
     | 
    
         
             
                          # IP Spoofing Protection can throw an exception, just move on w/o remote ip
         
     | 
| 
       78 
109 
     | 
    
         
             
                          if agent_context.config.value('collect_remote_ip')
         
     | 
| 
         @@ -95,16 +126,6 @@ module ScoutApm 
     | 
|
| 
       95 
126 
     | 
    
         
             
                    end
         
     | 
| 
       96 
127 
     | 
    
         
             
                  end
         
     | 
| 
       97 
128 
     | 
    
         | 
| 
       98 
     | 
    
         
            -
                  # Given an +ActionDispatch::Request+, formats the uri based on config settings.
         
     | 
| 
       99 
     | 
    
         
            -
                  # XXX: Don't lookup context like this - find a way to pass it through
         
     | 
| 
       100 
     | 
    
         
            -
                  def self.scout_transaction_uri(request, config=ScoutApm::Agent.instance.context.config)
         
     | 
| 
       101 
     | 
    
         
            -
                    case config.value("uri_reporting")
         
     | 
| 
       102 
     | 
    
         
            -
                    when 'path'
         
     | 
| 
       103 
     | 
    
         
            -
                      request.path # strips off the query string for more security
         
     | 
| 
       104 
     | 
    
         
            -
                    else # default handles filtered params
         
     | 
| 
       105 
     | 
    
         
            -
                      request.filtered_path
         
     | 
| 
       106 
     | 
    
         
            -
                    end
         
     | 
| 
       107 
     | 
    
         
            -
                  end
         
     | 
| 
       108 
129 
     | 
    
         
             
                end
         
     | 
| 
       109 
130 
     | 
    
         | 
| 
       110 
131 
     | 
    
         
             
                module ActionControllerMetalInstruments
         
     | 
| 
         @@ -75,25 +75,34 @@ module ScoutApm 
     | 
|
| 
       75 
75 
     | 
    
         
             
                  end
         
     | 
| 
       76 
76 
     | 
    
         | 
| 
       77 
77 
     | 
    
         
             
                  module ActionViewPartialRendererInstruments
         
     | 
| 
       78 
     | 
    
         
            -
                     
     | 
| 
      
 78 
     | 
    
         
            +
                    # In Rails 6, the signature changed to pass the view & template args directly, as opposed to through the instance var
         
     | 
| 
      
 79 
     | 
    
         
            +
                    # New signature is: def render_partial(view, template)
         
     | 
| 
      
 80 
     | 
    
         
            +
                    def render_partial(*args, **kwargs)
         
     | 
| 
       79 
81 
     | 
    
         
             
                      req = ScoutApm::RequestManager.lookup
         
     | 
| 
       80 
82 
     | 
    
         | 
| 
       81 
     | 
    
         
            -
                       
     | 
| 
      
 83 
     | 
    
         
            +
                      maybe_template = args[1]
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                      template_name = @template.virtual_path rescue nil        # Works on Rails 3.2 -> end of Rails 5 series
         
     | 
| 
      
 86 
     | 
    
         
            +
                      template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.0.3
         
     | 
| 
       82 
87 
     | 
    
         
             
                      template_name ||= "Unknown Partial"
         
     | 
| 
       83 
     | 
    
         
            -
                      layer_name = template_name + "/Rendering"
         
     | 
| 
       84 
88 
     | 
    
         | 
| 
      
 89 
     | 
    
         
            +
                      layer_name = template_name + "/Rendering"
         
     | 
| 
       85 
90 
     | 
    
         
             
                      layer = ScoutApm::Layer.new("View", layer_name)
         
     | 
| 
       86 
91 
     | 
    
         
             
                      layer.subscopable!
         
     | 
| 
       87 
92 
     | 
    
         | 
| 
       88 
93 
     | 
    
         
             
                      begin
         
     | 
| 
       89 
94 
     | 
    
         
             
                        req.start_layer(layer)
         
     | 
| 
       90 
     | 
    
         
            -
                         
     | 
| 
      
 95 
     | 
    
         
            +
                        if ScoutApm::Agent.instance.context.environment.supports_kwarg_delegation?
         
     | 
| 
      
 96 
     | 
    
         
            +
                          super(*args, **kwargs)
         
     | 
| 
      
 97 
     | 
    
         
            +
                        else
         
     | 
| 
      
 98 
     | 
    
         
            +
                          super(*args)
         
     | 
| 
      
 99 
     | 
    
         
            +
                        end
         
     | 
| 
       91 
100 
     | 
    
         
             
                      ensure
         
     | 
| 
       92 
101 
     | 
    
         
             
                        req.stop_layer
         
     | 
| 
       93 
102 
     | 
    
         
             
                      end
         
     | 
| 
       94 
103 
     | 
    
         
             
                    end
         
     | 
| 
       95 
104 
     | 
    
         | 
| 
       96 
     | 
    
         
            -
                    def collection_with_template(*args)
         
     | 
| 
      
 105 
     | 
    
         
            +
                    def collection_with_template(*args, **kwargs)
         
     | 
| 
       97 
106 
     | 
    
         
             
                      req = ScoutApm::RequestManager.lookup
         
     | 
| 
       98 
107 
     | 
    
         | 
| 
       99 
108 
     | 
    
         
             
                      template_name = @template.virtual_path rescue "Unknown Collection"
         
     | 
| 
         @@ -105,7 +114,11 @@ module ScoutApm 
     | 
|
| 
       105 
114 
     | 
    
         | 
| 
       106 
115 
     | 
    
         
             
                      begin
         
     | 
| 
       107 
116 
     | 
    
         
             
                        req.start_layer(layer)
         
     | 
| 
       108 
     | 
    
         
            -
                         
     | 
| 
      
 117 
     | 
    
         
            +
                        if ScoutApm::Agent.instance.context.environment.supports_kwarg_delegation?
         
     | 
| 
      
 118 
     | 
    
         
            +
                          super(*args, **kwargs)
         
     | 
| 
      
 119 
     | 
    
         
            +
                        else
         
     | 
| 
      
 120 
     | 
    
         
            +
                          super(*args)
         
     | 
| 
      
 121 
     | 
    
         
            +
                        end
         
     | 
| 
       109 
122 
     | 
    
         
             
                      ensure
         
     | 
| 
       110 
123 
     | 
    
         
             
                        req.stop_layer
         
     | 
| 
       111 
124 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -113,7 +126,7 @@ module ScoutApm 
     | 
|
| 
       113 
126 
     | 
    
         
             
                  end
         
     | 
| 
       114 
127 
     | 
    
         | 
| 
       115 
128 
     | 
    
         
             
                  module ActionViewTemplateRendererInstruments
         
     | 
| 
       116 
     | 
    
         
            -
                    def render_template(*args)
         
     | 
| 
      
 129 
     | 
    
         
            +
                    def render_template(*args, **kwargs)
         
     | 
| 
       117 
130 
     | 
    
         
             
                      req = ScoutApm::RequestManager.lookup
         
     | 
| 
       118 
131 
     | 
    
         | 
| 
       119 
132 
     | 
    
         
             
                      template_name = args[0].virtual_path rescue "Unknown"
         
     | 
| 
         @@ -125,7 +138,7 @@ module ScoutApm 
     | 
|
| 
       125 
138 
     | 
    
         | 
| 
       126 
139 
     | 
    
         
             
                      begin
         
     | 
| 
       127 
140 
     | 
    
         
             
                        req.start_layer(layer)
         
     | 
| 
       128 
     | 
    
         
            -
                        super(*args)
         
     | 
| 
      
 141 
     | 
    
         
            +
                        super(*args, **kwargs)
         
     | 
| 
       129 
142 
     | 
    
         
             
                      ensure
         
     | 
| 
       130 
143 
     | 
    
         
             
                        req.stop_layer
         
     | 
| 
       131 
144 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -82,15 +82,8 @@ module ScoutApm 
     | 
|
| 
       82 
82 
     | 
    
         | 
| 
       83 
83 
     | 
    
         
             
                    # Install #log tracing
         
     | 
| 
       84 
84 
     | 
    
         
             
                    if Utils::KlassHelper.defined?("ActiveRecord::ConnectionAdapters::AbstractAdapter")
         
     | 
| 
       85 
     | 
    
         
            -
                       
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
     | 
    
         
            -
                        ::ActiveRecord::ConnectionAdapters::AbstractAdapter.include(Tracer)
         
     | 
| 
       88 
     | 
    
         
            -
                      else
         
     | 
| 
       89 
     | 
    
         
            -
                        ::ActiveRecord::ConnectionAdapters::AbstractAdapter.module_eval do
         
     | 
| 
       90 
     | 
    
         
            -
                          include ::ScoutApm::Instruments::ActiveRecordAliasMethodInstruments
         
     | 
| 
       91 
     | 
    
         
            -
                          include ::ScoutApm::Tracer
         
     | 
| 
       92 
     | 
    
         
            -
                        end
         
     | 
| 
       93 
     | 
    
         
            -
                      end
         
     | 
| 
      
 85 
     | 
    
         
            +
                      ::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecordInstruments)
         
     | 
| 
      
 86 
     | 
    
         
            +
                      ::ActiveRecord::ConnectionAdapters::AbstractAdapter.include(Tracer)
         
     | 
| 
       94 
87 
     | 
    
         
             
                    end
         
     | 
| 
       95 
88 
     | 
    
         | 
| 
       96 
89 
     | 
    
         
             
                    if Utils::KlassHelper.defined?("ActiveRecord::Base")
         
     | 
| 
         @@ -172,20 +165,12 @@ module ScoutApm 
     | 
|
| 
       172 
165 
     | 
    
         
             
                # to the real SQL, and an AR generated "name" for the Query
         
     | 
| 
       173 
166 
     | 
    
         
             
                #
         
     | 
| 
       174 
167 
     | 
    
         
             
                ################################################################################
         
     | 
| 
       175 
     | 
    
         
            -
                 
     | 
| 
       176 
     | 
    
         
            -
             
     | 
| 
       177 
     | 
    
         
            -
                module ActiveRecordAliasMethodInstruments
         
     | 
| 
       178 
     | 
    
         
            -
                  def self.included(instrumented_class)
         
     | 
| 
      
 168 
     | 
    
         
            +
                module ActiveRecordInstruments
         
     | 
| 
      
 169 
     | 
    
         
            +
                  def self.prepended(instrumented_class)
         
     | 
| 
       179 
170 
     | 
    
         
             
                    ScoutApm::Agent.instance.context.logger.info "Instrumenting #{instrumented_class.inspect}"
         
     | 
| 
       180 
     | 
    
         
            -
                    instrumented_class.class_eval do
         
     | 
| 
       181 
     | 
    
         
            -
                      unless instrumented_class.method_defined?(:log_without_scout_instruments)
         
     | 
| 
       182 
     | 
    
         
            -
                        alias_method :log_without_scout_instruments, :log
         
     | 
| 
       183 
     | 
    
         
            -
                        alias_method :log, :log_with_scout_instruments
         
     | 
| 
       184 
     | 
    
         
            -
                      end
         
     | 
| 
       185 
     | 
    
         
            -
                    end
         
     | 
| 
       186 
171 
     | 
    
         
             
                  end
         
     | 
| 
       187 
172 
     | 
    
         | 
| 
       188 
     | 
    
         
            -
                  def  
     | 
| 
      
 173 
     | 
    
         
            +
                  def log(*args, &block)
         
     | 
| 
       189 
174 
     | 
    
         
             
                    # Extract data from the arguments
         
     | 
| 
       190 
175 
     | 
    
         
             
                    sql, name = args
         
     | 
| 
       191 
176 
     | 
    
         
             
                    metric_name = Utils::ActiveRecordMetricName.new(sql, name)
         
     | 
| 
         @@ -216,7 +201,7 @@ module ScoutApm 
     | 
|
| 
       216 
201 
     | 
    
         
             
                      end
         
     | 
| 
       217 
202 
     | 
    
         
             
                      current_layer.desc.merge(desc)
         
     | 
| 
       218 
203 
     | 
    
         | 
| 
       219 
     | 
    
         
            -
                       
     | 
| 
      
 204 
     | 
    
         
            +
                      super(*args, &block)
         
     | 
| 
       220 
205 
     | 
    
         | 
| 
       221 
206 
     | 
    
         
             
                    # OR: Start a new layer, we didn't pick up instrumentation earlier in the stack.
         
     | 
| 
       222 
207 
     | 
    
         
             
                    else
         
     | 
| 
         @@ -224,7 +209,7 @@ module ScoutApm 
     | 
|
| 
       224 
209 
     | 
    
         
             
                      layer.desc = desc
         
     | 
| 
       225 
210 
     | 
    
         
             
                      req.start_layer(layer)
         
     | 
| 
       226 
211 
     | 
    
         
             
                      begin
         
     | 
| 
       227 
     | 
    
         
            -
                         
     | 
| 
      
 212 
     | 
    
         
            +
                        super(*args, &block)
         
     | 
| 
       228 
213 
     | 
    
         
             
                      ensure
         
     | 
| 
       229 
214 
     | 
    
         
             
                        req.stop_layer
         
     | 
| 
       230 
215 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -323,14 +308,18 @@ module ScoutApm 
     | 
|
| 
       323 
308 
     | 
    
         
             
                    end
         
     | 
| 
       324 
309 
     | 
    
         
             
                  end
         
     | 
| 
       325 
310 
     | 
    
         | 
| 
       326 
     | 
    
         
            -
                  def find_by_sql_with_scout_instruments(*args, &block)
         
     | 
| 
      
 311 
     | 
    
         
            +
                  def find_by_sql_with_scout_instruments(*args, **kwargs, &block)
         
     | 
| 
       327 
312 
     | 
    
         
             
                    req = ScoutApm::RequestManager.lookup
         
     | 
| 
       328 
313 
     | 
    
         
             
                    layer = ScoutApm::Layer.new("ActiveRecord", Utils::ActiveRecordMetricName::DEFAULT_METRIC)
         
     | 
| 
       329 
314 
     | 
    
         
             
                    layer.annotate_layer(:ignorable => true)
         
     | 
| 
       330 
315 
     | 
    
         
             
                    req.start_layer(layer)
         
     | 
| 
       331 
316 
     | 
    
         
             
                    req.ignore_children!
         
     | 
| 
       332 
317 
     | 
    
         
             
                    begin
         
     | 
| 
       333 
     | 
    
         
            -
                       
     | 
| 
      
 318 
     | 
    
         
            +
                      if ScoutApm::Agent.instance.context.environment.supports_kwarg_delegation?
         
     | 
| 
      
 319 
     | 
    
         
            +
                        find_by_sql_without_scout_instruments(*args, **kwargs, &block)
         
     | 
| 
      
 320 
     | 
    
         
            +
                      else
         
     | 
| 
      
 321 
     | 
    
         
            +
                        find_by_sql_without_scout_instruments(*args, &block)
         
     | 
| 
      
 322 
     | 
    
         
            +
                      end
         
     | 
| 
       334 
323 
     | 
    
         
             
                    ensure
         
     | 
| 
       335 
324 
     | 
    
         
             
                      req.acknowledge_children!
         
     | 
| 
       336 
325 
     | 
    
         
             
                      req.stop_layer
         
     | 
| 
         @@ -408,7 +397,7 @@ module ScoutApm 
     | 
|
| 
       408 
397 
     | 
    
         
             
                end
         
     | 
| 
       409 
398 
     | 
    
         | 
| 
       410 
399 
     | 
    
         
             
                module ActiveRecordUpdateInstruments
         
     | 
| 
       411 
     | 
    
         
            -
                  def save(*args, &block)
         
     | 
| 
      
 400 
     | 
    
         
            +
                  def save(*args, **options, &block)
         
     | 
| 
       412 
401 
     | 
    
         
             
                    model = self.class.name
         
     | 
| 
       413 
402 
     | 
    
         
             
                    operation = self.persisted? ? "Update" : "Create"
         
     | 
| 
       414 
403 
     | 
    
         | 
| 
         @@ -418,14 +407,14 @@ module ScoutApm 
     | 
|
| 
       418 
407 
     | 
    
         
             
                    req.start_layer(layer)
         
     | 
| 
       419 
408 
     | 
    
         
             
                    req.ignore_children!
         
     | 
| 
       420 
409 
     | 
    
         
             
                    begin
         
     | 
| 
       421 
     | 
    
         
            -
                      super(*args, &block)
         
     | 
| 
      
 410 
     | 
    
         
            +
                      super(*args, **options, &block)
         
     | 
| 
       422 
411 
     | 
    
         
             
                    ensure
         
     | 
| 
       423 
412 
     | 
    
         
             
                      req.acknowledge_children!
         
     | 
| 
       424 
413 
     | 
    
         
             
                      req.stop_layer
         
     | 
| 
       425 
414 
     | 
    
         
             
                    end
         
     | 
| 
       426 
415 
     | 
    
         
             
                  end
         
     | 
| 
       427 
416 
     | 
    
         | 
| 
       428 
     | 
    
         
            -
                  def save!(*args, &block)
         
     | 
| 
      
 417 
     | 
    
         
            +
                  def save!(*args, **options, &block)
         
     | 
| 
       429 
418 
     | 
    
         
             
                    model = self.class.name
         
     | 
| 
       430 
419 
     | 
    
         
             
                    operation = self.persisted? ? "Update" : "Create"
         
     | 
| 
       431 
420 
     | 
    
         | 
| 
         @@ -434,7 +423,7 @@ module ScoutApm 
     | 
|
| 
       434 
423 
     | 
    
         
             
                    req.start_layer(layer)
         
     | 
| 
       435 
424 
     | 
    
         
             
                    req.ignore_children!
         
     | 
| 
       436 
425 
     | 
    
         
             
                    begin
         
     | 
| 
       437 
     | 
    
         
            -
                      super(*args, &block)
         
     | 
| 
      
 426 
     | 
    
         
            +
                      super(*args, **options, &block)
         
     | 
| 
       438 
427 
     | 
    
         
             
                    ensure
         
     | 
| 
       439 
428 
     | 
    
         
             
                      req.acknowledge_children!
         
     | 
| 
       440 
429 
     | 
    
         
             
                      req.stop_layer
         
     | 
| 
         @@ -0,0 +1,88 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module ScoutApm
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Instruments
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Typhoeus
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_reader :context
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(context)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    @context = context
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @installed = false
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def logger
         
     | 
| 
      
 12 
     | 
    
         
            +
                    context.logger
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  def installed?
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @installed
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  def install
         
     | 
| 
      
 20 
     | 
    
         
            +
                    if defined?(::Typhoeus)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      @installed = true
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                      logger.info "Instrumenting Typhoeus"
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                      ::Typhoeus::Request.send(:prepend, TyphoeusInstrumentation)
         
     | 
| 
      
 26 
     | 
    
         
            +
                      ::Typhoeus::Hydra.send(:prepend, TyphoeusHydraInstrumentation)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    end
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  module TyphoeusHydraInstrumentation
         
     | 
| 
      
 31 
     | 
    
         
            +
                    def run(*args, &block)
         
     | 
| 
      
 32 
     | 
    
         
            +
                      req = ScoutApm::RequestManager.lookup
         
     | 
| 
      
 33 
     | 
    
         
            +
                      req.start_layer(ScoutApm::Layer.new("HTTP", "Hydra"))
         
     | 
| 
      
 34 
     | 
    
         
            +
                      current_layer = req.current_layer
         
     | 
| 
      
 35 
     | 
    
         
            +
                      current_layer.desc = scout_desc if current_layer
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 38 
     | 
    
         
            +
                        super(*args, &block)
         
     | 
| 
      
 39 
     | 
    
         
            +
                      ensure
         
     | 
| 
      
 40 
     | 
    
         
            +
                        req.stop_layer
         
     | 
| 
      
 41 
     | 
    
         
            +
                      end
         
     | 
| 
      
 42 
     | 
    
         
            +
                    end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    def scout_desc
         
     | 
| 
      
 45 
     | 
    
         
            +
                      "#{self.queued_requests.count} requests"
         
     | 
| 
      
 46 
     | 
    
         
            +
                    rescue
         
     | 
| 
      
 47 
     | 
    
         
            +
                      ""
         
     | 
| 
      
 48 
     | 
    
         
            +
                    end
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  module TyphoeusInstrumentation
         
     | 
| 
      
 52 
     | 
    
         
            +
                    def run(*args, &block)
         
     | 
| 
      
 53 
     | 
    
         
            +
                      req = ScoutApm::RequestManager.lookup
         
     | 
| 
      
 54 
     | 
    
         
            +
                      req.start_layer(ScoutApm::Layer.new("HTTP", scout_request_verb))
         
     | 
| 
      
 55 
     | 
    
         
            +
                      current_layer = req.current_layer
         
     | 
| 
      
 56 
     | 
    
         
            +
                      current_layer.desc = scout_desc(scout_request_verb, scout_request_url) if current_layer
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 59 
     | 
    
         
            +
                        super(*args, &block)
         
     | 
| 
      
 60 
     | 
    
         
            +
                      ensure
         
     | 
| 
      
 61 
     | 
    
         
            +
                        req.stop_layer
         
     | 
| 
      
 62 
     | 
    
         
            +
                      end
         
     | 
| 
      
 63 
     | 
    
         
            +
                    end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                    def scout_desc(verb, uri)
         
     | 
| 
      
 66 
     | 
    
         
            +
                      max_length = ScoutApm::Agent.instance.context.config.value('instrument_http_url_length')
         
     | 
| 
      
 67 
     | 
    
         
            +
                      (String(uri).split('?').first)[0..(max_length - 1)]
         
     | 
| 
      
 68 
     | 
    
         
            +
                    rescue
         
     | 
| 
      
 69 
     | 
    
         
            +
                      ""
         
     | 
| 
      
 70 
     | 
    
         
            +
                    end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    def scout_request_url
         
     | 
| 
      
 73 
     | 
    
         
            +
                      self.url
         
     | 
| 
      
 74 
     | 
    
         
            +
                    rescue
         
     | 
| 
      
 75 
     | 
    
         
            +
                      ""
         
     | 
| 
      
 76 
     | 
    
         
            +
                    end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                    def scout_request_verb
         
     | 
| 
      
 79 
     | 
    
         
            +
                      self.options[:method].to_s
         
     | 
| 
      
 80 
     | 
    
         
            +
                    rescue
         
     | 
| 
      
 81 
     | 
    
         
            +
                      ""
         
     | 
| 
      
 82 
     | 
    
         
            +
                    end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
              end
         
     | 
| 
      
 88 
     | 
    
         
            +
            end
         
     |