scout_apm 5.7.1 → 5.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +8 -0
- data/lib/scout_apm/background_job_integrations/delayed_job.rb +11 -1
- data/lib/scout_apm/background_job_integrations/faktory.rb +7 -1
- data/lib/scout_apm/background_job_integrations/good_job.rb +7 -1
- data/lib/scout_apm/background_job_integrations/legacy_sneakers.rb +7 -1
- data/lib/scout_apm/background_job_integrations/que.rb +7 -1
- data/lib/scout_apm/background_job_integrations/shoryuken.rb +7 -1
- data/lib/scout_apm/background_job_integrations/sneakers.rb +7 -1
- data/lib/scout_apm/background_job_integrations/solid_queue.rb +19 -1
- data/lib/scout_apm/error_service/error_record.rb +4 -0
- data/lib/scout_apm/limited_layer.rb +5 -2
- data/lib/scout_apm/logger.rb +1 -1
- data/lib/scout_apm/version.rb +1 -1
- data/lib/scout_apm.rb +0 -1
- data/test/unit/background_job_integrations/faktory_test.rb +109 -0
- data/test/unit/background_job_integrations/shoryuken_test.rb +81 -0
- data/test/unit/error_service/error_buffer_test.rb +1 -0
- data/test/unit/error_test.rb +3 -3
- data/test/unit/limited_layer_test.rb +4 -4
- metadata +3 -2
- data/lib/scout_apm/utils/time.rb +0 -12
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 80d2cac0818a8487fe1e9ca2395faccbbbadf2be368151e636e39dd7188b34ea
         | 
| 4 | 
            +
              data.tar.gz: 1c46ee7b70ac1bd2e9ff4ed4232c7304edc02088563df840619aedcb5e43384d
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: da2b3e51bea0eb7198d76edd997ef62da6719b3e0df7bd7093f56cbd51d2f0426f6798ef57d641e786a3a16732ad6e9ad141ce977fce0d9888541ff72bfbb11d
         | 
| 7 | 
            +
              data.tar.gz: 42e1bb5874b213da9c8be9b4024263737bf8c34ae7c968b2259939f87bb3f983a105036d6cd3222de02964b8f40aedab8f5e47f10248ed5053c517acddf67833
         | 
    
        data/CHANGELOG.markdown
    CHANGED
    
    | @@ -1,4 +1,12 @@ | |
| 1 1 | 
             
            # Unreleased
         | 
| 2 | 
            +
            - Enable Delayed Job error tracking (#565)
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            # 5.8.0
         | 
| 5 | 
            +
            - Add error monitoring to SolidQueue, faktory, goodjob, que, shoryuken, sneakers (#571)
         | 
| 6 | 
            +
            - Treat exclusive time as total time for limited layers in background jobs (#576)
         | 
| 7 | 
            +
            - Remove unused time util conflict with DeviseTokenAuth (#575)
         | 
| 8 | 
            +
            - Add transaction ID to error records (#568)-
         | 
| 9 | 
            +
            - Adds request method to captured error context (#577)
         | 
| 2 10 |  | 
| 3 11 | 
             
            # 5.7.1
         | 
| 4 12 | 
             
            - Update error capture API to use context (#560)
         | 
| @@ -4,6 +4,7 @@ module ScoutApm | |
| 4 4 | 
             
                  ACTIVE_JOB_KLASS = 'ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper'.freeze
         | 
| 5 5 | 
             
                  DJ_PERFORMABLE_METHOD = 'Delayed::PerformableMethod'.freeze
         | 
| 6 6 |  | 
| 7 | 
            +
             | 
| 7 8 | 
             
                  attr_reader :logger
         | 
| 8 9 |  | 
| 9 10 | 
             
                  def name
         | 
| @@ -69,8 +70,17 @@ module ScoutApm | |
| 69 70 |  | 
| 70 71 | 
             
                            # Call the job itself.
         | 
| 71 72 | 
             
                            block.call(job, *args)
         | 
| 72 | 
            -
                          rescue
         | 
| 73 | 
            +
                          rescue Exception => exception
         | 
| 74 | 
            +
                            # Capture the error for further processing and shipping
         | 
| 73 75 | 
             
                            req.error!
         | 
| 76 | 
            +
                            # Abusing this key to pass job info
         | 
| 77 | 
            +
                            params_key = 'action_dispatch.request.parameters'
         | 
| 78 | 
            +
                            env = {}
         | 
| 79 | 
            +
                            env[params_key] = job.payload_object.job_data
         | 
| 80 | 
            +
                            env[:custom_controller] = name
         | 
| 81 | 
            +
                            env[:custom_action] = queue
         | 
| 82 | 
            +
                            context = ScoutApm::Agent.instance.context
         | 
| 83 | 
            +
                            context.error_buffer.capture(exception, env)
         | 
| 74 84 | 
             
                            raise
         | 
| 75 85 | 
             
                          ensure
         | 
| 76 86 | 
             
                            req.stop_layer if started_job
         | 
| @@ -60,8 +60,14 @@ module ScoutApm | |
| 60 60 | 
             
                      started_job = true
         | 
| 61 61 |  | 
| 62 62 | 
             
                      yield
         | 
| 63 | 
            -
                    rescue
         | 
| 63 | 
            +
                    rescue Exception => exception
         | 
| 64 64 | 
             
                      req.error!
         | 
| 65 | 
            +
                      env = {
         | 
| 66 | 
            +
                        :custom_controller => job_class(job),
         | 
| 67 | 
            +
                        :custom_action => queue
         | 
| 68 | 
            +
                      }
         | 
| 69 | 
            +
                      context = ScoutApm::Agent.instance.context
         | 
| 70 | 
            +
                      context.error_buffer.capture(exception, env)
         | 
| 65 71 | 
             
                      raise
         | 
| 66 72 | 
             
                    ensure
         | 
| 67 73 | 
             
                      req.stop_layer if started_job
         | 
| @@ -34,8 +34,14 @@ module ScoutApm | |
| 34 34 | 
             
                          started_job = true # Following Convention
         | 
| 35 35 |  | 
| 36 36 | 
             
                          block.call
         | 
| 37 | 
            -
                        rescue
         | 
| 37 | 
            +
                        rescue Exception => exception
         | 
| 38 38 | 
             
                          req.error!
         | 
| 39 | 
            +
                          env = {
         | 
| 40 | 
            +
                            :custom_controller => job.class.name,
         | 
| 41 | 
            +
                            :custom_action => job.queue_name.presence || UNKNOWN_QUEUE_PLACEHOLDER
         | 
| 42 | 
            +
                          }
         | 
| 43 | 
            +
                          context = ScoutApm::Agent.instance.context
         | 
| 44 | 
            +
                          context.error_buffer.capture(exception, env)
         | 
| 39 45 | 
             
                          raise
         | 
| 40 46 | 
             
                        ensure
         | 
| 41 47 | 
             
                          req.stop_layer if started_job
         | 
| @@ -42,8 +42,14 @@ module ScoutApm | |
| 42 42 | 
             
                      else
         | 
| 43 43 | 
             
                        super
         | 
| 44 44 | 
             
                      end
         | 
| 45 | 
            -
                    rescue Exception
         | 
| 45 | 
            +
                    rescue Exception => exception
         | 
| 46 46 | 
             
                      req.error!
         | 
| 47 | 
            +
                      env = {
         | 
| 48 | 
            +
                        :custom_controller => job_class,
         | 
| 49 | 
            +
                        :custom_action => queue
         | 
| 50 | 
            +
                      }
         | 
| 51 | 
            +
                      context = ScoutApm::Agent.instance.context
         | 
| 52 | 
            +
                      context.error_buffer.capture(exception, env)
         | 
| 47 53 | 
             
                      raise
         | 
| 48 54 | 
             
                    ensure
         | 
| 49 55 | 
             
                      req.stop_layer if started_job
         | 
| @@ -115,8 +115,14 @@ module ScoutApm | |
| 115 115 | 
             
                          started_job = true
         | 
| 116 116 |  | 
| 117 117 | 
             
                          _run_without_scout(*args)
         | 
| 118 | 
            -
                        rescue Exception =>  | 
| 118 | 
            +
                        rescue Exception => exception
         | 
| 119 119 | 
             
                          req.error!
         | 
| 120 | 
            +
                          env = {
         | 
| 121 | 
            +
                            :custom_controller => job_class,
         | 
| 122 | 
            +
                            :custom_action => queue
         | 
| 123 | 
            +
                          }
         | 
| 124 | 
            +
                          context = ScoutApm::Agent.instance.context
         | 
| 125 | 
            +
                          context.error_buffer.capture(exception, env)
         | 
| 120 126 | 
             
                          raise
         | 
| 121 127 | 
             
                        ensure
         | 
| 122 128 | 
             
                          req.stop_layer if started_job
         | 
| @@ -79,8 +79,14 @@ module ScoutApm | |
| 79 79 | 
             
                      started_job = true
         | 
| 80 80 |  | 
| 81 81 | 
             
                      yield
         | 
| 82 | 
            -
                    rescue Exception =>  | 
| 82 | 
            +
                    rescue Exception => exception
         | 
| 83 83 | 
             
                      req.error!
         | 
| 84 | 
            +
                      env = {
         | 
| 85 | 
            +
                        :custom_controller => job_class,
         | 
| 86 | 
            +
                        :custom_action => queue
         | 
| 87 | 
            +
                      }
         | 
| 88 | 
            +
                      context = ScoutApm::Agent.instance.context
         | 
| 89 | 
            +
                      context.error_buffer.capture(exception, env)
         | 
| 84 90 | 
             
                      raise
         | 
| 85 91 | 
             
                    ensure
         | 
| 86 92 | 
             
                      req.stop_layer if started_job
         | 
| @@ -65,8 +65,14 @@ module ScoutApm | |
| 65 65 | 
             
                          started_job = true
         | 
| 66 66 |  | 
| 67 67 | 
             
                          process_work_without_scout(*args)
         | 
| 68 | 
            -
                        rescue Exception =>  | 
| 68 | 
            +
                        rescue Exception => exception
         | 
| 69 69 | 
             
                          req.error!
         | 
| 70 | 
            +
                          env = {
         | 
| 71 | 
            +
                            :custom_controller => job_class,
         | 
| 72 | 
            +
                            :custom_action => queue
         | 
| 73 | 
            +
                          }
         | 
| 74 | 
            +
                          context = ScoutApm::Agent.instance.context
         | 
| 75 | 
            +
                          context.error_buffer.capture(exception, env)
         | 
| 70 76 | 
             
                          raise
         | 
| 71 77 | 
             
                        ensure
         | 
| 72 78 | 
             
                          req.stop_layer if started_job
         | 
| @@ -32,8 +32,26 @@ module ScoutApm | |
| 32 32 | 
             
                          started_job = true # Following Convention
         | 
| 33 33 |  | 
| 34 34 | 
             
                          block.call
         | 
| 35 | 
            -
                        rescue
         | 
| 35 | 
            +
                        rescue Exception => exception
         | 
| 36 36 | 
             
                          req.error!
         | 
| 37 | 
            +
                          # Extract job parameters like DelayedJob does
         | 
| 38 | 
            +
                          params_key = 'action_dispatch.request.parameters'
         | 
| 39 | 
            +
                          job_args = begin
         | 
| 40 | 
            +
                            {
         | 
| 41 | 
            +
                              arguments: job.arguments,
         | 
| 42 | 
            +
                              job_id: job.job_id,
         | 
| 43 | 
            +
                            }
         | 
| 44 | 
            +
                          rescue => e
         | 
| 45 | 
            +
                            { error_extracting_params: e.message }
         | 
| 46 | 
            +
                          end
         | 
| 47 | 
            +
                          
         | 
| 48 | 
            +
                          env = {
         | 
| 49 | 
            +
                            params_key => job_args,
         | 
| 50 | 
            +
                            :custom_controller => job.class.name,
         | 
| 51 | 
            +
                            :custom_action => job.queue_name.presence || UNKNOWN_QUEUE_PLACEHOLDER
         | 
| 52 | 
            +
                          }
         | 
| 53 | 
            +
                          context = ScoutApm::Agent.instance.context
         | 
| 54 | 
            +
                          context.error_buffer.capture(exception, env)
         | 
| 37 55 | 
             
                          raise
         | 
| 38 56 | 
             
                        ensure
         | 
| 39 57 | 
             
                          req.stop_layer if started_job
         | 
| @@ -22,6 +22,8 @@ module ScoutApm | |
| 22 22 | 
             
                    else
         | 
| 23 23 | 
             
                      {}
         | 
| 24 24 | 
             
                    end
         | 
| 25 | 
            +
                    # Add the transaction_id, as it won't be added to the context normally until the request has been recorded.
         | 
| 26 | 
            +
                    @context[:transaction_id] ||= RequestManager.lookup.transaction_id
         | 
| 25 27 |  | 
| 26 28 | 
             
                    @exception_class = LengthLimit.new(exception.class.name).to_s
         | 
| 27 29 | 
             
                    @message = LengthLimit.new(exception.message, 100).to_s
         | 
| @@ -46,6 +48,7 @@ module ScoutApm | |
| 46 48 | 
             
                    # For background workers like sidekiq
         | 
| 47 49 | 
             
                    # TODO: extract data creation for background jobs
         | 
| 48 50 | 
             
                    components[:controller] ||= env[:custom_controller]
         | 
| 51 | 
            +
                    components[:action] ||= env[:custom_action]
         | 
| 49 52 |  | 
| 50 53 | 
             
                    components
         | 
| 51 54 | 
             
                  end
         | 
| @@ -92,6 +95,7 @@ module ScoutApm | |
| 92 95 |  | 
| 93 96 | 
             
                  # Capture params from env
         | 
| 94 97 | 
             
                  KEYS_TO_KEEP = [
         | 
| 98 | 
            +
                    "REQUEST_METHOD",
         | 
| 95 99 | 
             
                    "HTTP_USER_AGENT",
         | 
| 96 100 | 
             
                    "HTTP_REFERER",
         | 
| 97 101 | 
             
                    "HTTP_ACCEPT_ENCODING",
         | 
| @@ -21,10 +21,13 @@ module ScoutApm | |
| 21 21 | 
             
                  @total_layers += 1
         | 
| 22 22 |  | 
| 23 23 | 
             
                  @total_call_time += layer.total_call_time
         | 
| 24 | 
            -
                   | 
| 24 | 
            +
                  # For limited layers, exclusive time should equal total time since limited layers
         | 
| 25 | 
            +
                  # report no children. As such, we need to consider all absorbed time as exclusive.
         | 
| 26 | 
            +
                  @total_exclusive_time += layer.total_call_time
         | 
| 25 27 |  | 
| 26 28 | 
             
                  @total_allocations += layer.total_allocations
         | 
| 27 | 
            -
                   | 
| 29 | 
            +
                  # Same logic applies to allocations
         | 
| 30 | 
            +
                  @total_exclusive_allocations += layer.total_allocations
         | 
| 28 31 | 
             
                end
         | 
| 29 32 |  | 
| 30 33 | 
             
                def total_call_time
         | 
    
        data/lib/scout_apm/logger.rb
    CHANGED
    
    | @@ -145,7 +145,7 @@ module ScoutApm | |
| 145 145 | 
             
                  def call(severity, time, progname, msg)
         | 
| 146 146 | 
             
                    # since STDOUT isn't exclusive like the scout_apm.log file, apply a prefix.
         | 
| 147 147 | 
             
                    # XXX: Pass in context to the formatter
         | 
| 148 | 
            -
                    "[#{ | 
| 148 | 
            +
                    "[#{time.strftime("%m/%d/%y %H:%M:%S %z")} #{ScoutApm::Agent.instance.context.environment.hostname} (#{$$})] #{severity} : #{msg}\n"
         | 
| 149 149 | 
             
                  end
         | 
| 150 150 | 
             
                end
         | 
| 151 151 |  | 
    
        data/lib/scout_apm/version.rb
    CHANGED
    
    
    
        data/lib/scout_apm.rb
    CHANGED
    
    | @@ -118,7 +118,6 @@ require 'scout_apm/utils/installed_gems' | |
| 118 118 | 
             
            require 'scout_apm/utils/klass_helper'
         | 
| 119 119 | 
             
            require 'scout_apm/utils/scm'
         | 
| 120 120 | 
             
            require 'scout_apm/utils/sql_sanitizer'
         | 
| 121 | 
            -
            require 'scout_apm/utils/time'
         | 
| 122 121 | 
             
            require 'scout_apm/utils/unique_id'
         | 
| 123 122 | 
             
            require 'scout_apm/utils/numbers'
         | 
| 124 123 | 
             
            require 'scout_apm/utils/gzip_helper'
         | 
| @@ -0,0 +1,109 @@ | |
| 1 | 
            +
            require 'test_helper'
         | 
| 2 | 
            +
            require 'scout_apm/background_job_integrations/faktory'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class FaktoryTest < Minitest::Test
         | 
| 5 | 
            +
              FaktoryMiddleware = ScoutApm::BackgroundJobIntegrations::FaktoryMiddleware
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              def test_middleware_call_job_exception_with_error_monitoring
         | 
| 8 | 
            +
                # Test that error buffer is called on exception
         | 
| 9 | 
            +
                fake_request = mock
         | 
| 10 | 
            +
                fake_request.expects(:annotate_request)
         | 
| 11 | 
            +
                fake_request.expects(:start_layer).twice
         | 
| 12 | 
            +
                fake_request.expects(:stop_layer).twice
         | 
| 13 | 
            +
                fake_request.expects(:error!)
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                fake_context = mock
         | 
| 16 | 
            +
                fake_error_buffer = mock
         | 
| 17 | 
            +
                fake_context.expects(:error_buffer).returns(fake_error_buffer)
         | 
| 18 | 
            +
                
         | 
| 19 | 
            +
                expected_env = {
         | 
| 20 | 
            +
                  :custom_controller => "TestJob",
         | 
| 21 | 
            +
                  :custom_action => "critical"
         | 
| 22 | 
            +
                }
         | 
| 23 | 
            +
                fake_error_buffer.expects(:capture).with(kind_of(RuntimeError), expected_env)
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                ScoutApm::RequestManager.stubs(:lookup).returns(fake_request)
         | 
| 26 | 
            +
                ScoutApm::Agent.instance.expects(:context).returns(fake_context)
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                worker_instance = mock
         | 
| 29 | 
            +
                job = {
         | 
| 30 | 
            +
                  "queue" => "critical",
         | 
| 31 | 
            +
                  "jobtype" => "TestJob",
         | 
| 32 | 
            +
                  "enqueued_at" => Time.now.iso8601
         | 
| 33 | 
            +
                }
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                assert_raises RuntimeError do
         | 
| 36 | 
            +
                  FaktoryMiddleware.new.call(worker_instance, job) do
         | 
| 37 | 
            +
                    raise RuntimeError, "Job failed"
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              def test_middleware_call_activejob_wrapper
         | 
| 43 | 
            +
                # Test ActiveJob job class extraction
         | 
| 44 | 
            +
                fake_request = mock
         | 
| 45 | 
            +
                fake_request.expects(:annotate_request)
         | 
| 46 | 
            +
                fake_request.expects(:start_layer).twice
         | 
| 47 | 
            +
                fake_request.expects(:stop_layer).twice
         | 
| 48 | 
            +
                fake_request.expects(:error!)
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                fake_context = mock
         | 
| 51 | 
            +
                fake_error_buffer = mock
         | 
| 52 | 
            +
                fake_context.expects(:error_buffer).returns(fake_error_buffer)
         | 
| 53 | 
            +
                
         | 
| 54 | 
            +
                expected_env = {
         | 
| 55 | 
            +
                  :custom_controller => "MyRealJob",  # Should extract from custom.wrapped
         | 
| 56 | 
            +
                  :custom_action => "default"
         | 
| 57 | 
            +
                }
         | 
| 58 | 
            +
                fake_error_buffer.expects(:capture).with(kind_of(RuntimeError), expected_env)
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                ScoutApm::RequestManager.stubs(:lookup).returns(fake_request)
         | 
| 61 | 
            +
                ScoutApm::Agent.instance.expects(:context).returns(fake_context)
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                # ActiveJob wrapper scenario
         | 
| 64 | 
            +
                worker_instance = mock
         | 
| 65 | 
            +
                job = {
         | 
| 66 | 
            +
                  "queue" => "default", 
         | 
| 67 | 
            +
                  "jobtype" => "ActiveJob::QueueAdapters::FaktoryAdapter::JobWrapper",
         | 
| 68 | 
            +
                  "custom" => { "wrapped" => "MyRealJob" },
         | 
| 69 | 
            +
                  "created_at" => Time.now.iso8601
         | 
| 70 | 
            +
                }
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                assert_raises RuntimeError do
         | 
| 73 | 
            +
                  FaktoryMiddleware.new.call(worker_instance, job) do
         | 
| 74 | 
            +
                    raise RuntimeError, "ActiveJob failed"
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              def test_middleware_call_missing_queue_fallback
         | 
| 80 | 
            +
                # Test behavior when queue is missing
         | 
| 81 | 
            +
                fake_request = mock
         | 
| 82 | 
            +
                fake_request.expects(:annotate_request)
         | 
| 83 | 
            +
                fake_request.expects(:start_layer).twice
         | 
| 84 | 
            +
                fake_request.expects(:stop_layer).twice
         | 
| 85 | 
            +
                fake_request.expects(:error!)
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                fake_context = mock
         | 
| 88 | 
            +
                fake_error_buffer = mock
         | 
| 89 | 
            +
                fake_context.expects(:error_buffer).returns(fake_error_buffer)
         | 
| 90 | 
            +
                
         | 
| 91 | 
            +
                expected_env = {
         | 
| 92 | 
            +
                  :custom_controller => "UnknownJob",  # Fallback when jobtype missing
         | 
| 93 | 
            +
                  :custom_action => nil  # No queue
         | 
| 94 | 
            +
                }
         | 
| 95 | 
            +
                fake_error_buffer.expects(:capture).with(kind_of(RuntimeError), expected_env)
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                ScoutApm::RequestManager.stubs(:lookup).returns(fake_request)
         | 
| 98 | 
            +
                ScoutApm::Agent.instance.expects(:context).returns(fake_context)
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                worker_instance = mock
         | 
| 101 | 
            +
                job = {}  # Empty job data
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                assert_raises RuntimeError do
         | 
| 104 | 
            +
                  FaktoryMiddleware.new.call(worker_instance, job) do
         | 
| 105 | 
            +
                    raise RuntimeError, "Job failed"
         | 
| 106 | 
            +
                  end
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
              end
         | 
| 109 | 
            +
            end
         | 
| @@ -0,0 +1,81 @@ | |
| 1 | 
            +
            require 'test_helper'
         | 
| 2 | 
            +
            require 'scout_apm/background_job_integrations/shoryuken'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class ShoryukenTest < Minitest::Test
         | 
| 5 | 
            +
              ShoryukenIntegration = ScoutApm::BackgroundJobIntegrations::Shoryuken
         | 
| 6 | 
            +
              ShoryukenMiddleware = ScoutApm::BackgroundJobIntegrations::ShoryukenMiddleware
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def test_middleware_call_job_exception_with_error_monitoring
         | 
| 9 | 
            +
                # Test that error buffer is called on exception
         | 
| 10 | 
            +
                fake_request = mock
         | 
| 11 | 
            +
                fake_request.expects(:annotate_request)
         | 
| 12 | 
            +
                fake_request.expects(:start_layer).twice
         | 
| 13 | 
            +
                fake_request.expects(:stop_layer).twice  
         | 
| 14 | 
            +
                fake_request.expects(:error!)
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                fake_context = mock
         | 
| 17 | 
            +
                fake_error_buffer = mock
         | 
| 18 | 
            +
                fake_context.expects(:error_buffer).returns(fake_error_buffer)
         | 
| 19 | 
            +
                
         | 
| 20 | 
            +
                expected_env = {
         | 
| 21 | 
            +
                  :custom_controller => "TestWorker",
         | 
| 22 | 
            +
                  :custom_action => "test-queue"
         | 
| 23 | 
            +
                }
         | 
| 24 | 
            +
                fake_error_buffer.expects(:capture).with(kind_of(RuntimeError), expected_env)
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                ScoutApm::RequestManager.stubs(:lookup).returns(fake_request)
         | 
| 27 | 
            +
                ScoutApm::Agent.instance.expects(:context).returns(fake_context)
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                worker_instance = mock
         | 
| 30 | 
            +
                mock_class = mock
         | 
| 31 | 
            +
                mock_class.expects(:to_s).twice.returns("TestWorker")
         | 
| 32 | 
            +
                worker_instance.expects(:class).twice.returns(mock_class)
         | 
| 33 | 
            +
                queue = "test-queue"
         | 
| 34 | 
            +
                msg = mock
         | 
| 35 | 
            +
                msg.expects(:attributes).returns({'SentTimestamp' => '1534873927868'})
         | 
| 36 | 
            +
                body = {}
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                assert_raises RuntimeError do
         | 
| 39 | 
            +
                  ShoryukenMiddleware.new.call(worker_instance, queue, msg, body) do
         | 
| 40 | 
            +
                    raise RuntimeError, "Job failed"
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              def test_middleware_call_activejob_wrapper
         | 
| 46 | 
            +
                # Test ActiveJob job class extraction
         | 
| 47 | 
            +
                fake_request = mock
         | 
| 48 | 
            +
                fake_request.expects(:annotate_request)
         | 
| 49 | 
            +
                fake_request.expects(:start_layer).twice
         | 
| 50 | 
            +
                fake_request.expects(:stop_layer).twice
         | 
| 51 | 
            +
                fake_request.expects(:error!)
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                fake_context = mock
         | 
| 54 | 
            +
                fake_error_buffer = mock
         | 
| 55 | 
            +
                fake_context.expects(:error_buffer).returns(fake_error_buffer)
         | 
| 56 | 
            +
                
         | 
| 57 | 
            +
                expected_env = {
         | 
| 58 | 
            +
                  :custom_controller => "MyRealJob",  # Should extract from body
         | 
| 59 | 
            +
                  :custom_action => "priority-queue"
         | 
| 60 | 
            +
                }
         | 
| 61 | 
            +
                fake_error_buffer.expects(:capture).with(kind_of(RuntimeError), expected_env)
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                ScoutApm::RequestManager.stubs(:lookup).returns(fake_request)
         | 
| 64 | 
            +
                ScoutApm::Agent.instance.expects(:context).returns(fake_context)
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                worker_instance = mock
         | 
| 67 | 
            +
                mock_class = mock
         | 
| 68 | 
            +
                mock_class.expects(:to_s).returns("ActiveJob::QueueAdapters::ShoryukenAdapter::JobWrapper")
         | 
| 69 | 
            +
                worker_instance.expects(:class).returns(mock_class)
         | 
| 70 | 
            +
                queue = "priority-queue"
         | 
| 71 | 
            +
                msg = mock
         | 
| 72 | 
            +
                msg.expects(:attributes).returns({'SentTimestamp' => '1534873927868'})
         | 
| 73 | 
            +
                body = { "job_class" => "MyRealJob" }
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                assert_raises RuntimeError do
         | 
| 76 | 
            +
                  ShoryukenMiddleware.new.call(worker_instance, queue, msg, body) do
         | 
| 77 | 
            +
                    raise RuntimeError, "ActiveJob failed"
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
              end
         | 
| 81 | 
            +
            end
         | 
    
        data/test/unit/error_test.rb
    CHANGED
    
    | @@ -55,7 +55,7 @@ class ErrorTest < Minitest::Test | |
| 55 55 |  | 
| 56 56 | 
             
                  assert_equal "No context or env", exceptions[3].message
         | 
| 57 57 | 
             
                  assert_equal "AnotherError", exceptions[3].exception_class
         | 
| 58 | 
            -
                  assert_equal  | 
| 58 | 
            +
                  assert_equal assert_default_context, exceptions[3].context
         | 
| 59 59 |  | 
| 60 60 | 
             
                  assert_equal "Whoops", exceptions[4].message
         | 
| 61 61 | 
             
                  assert_equal "ErrorTest::FakeError", exceptions[4].exception_class
         | 
| @@ -84,8 +84,8 @@ class ErrorTest < Minitest::Test | |
| 84 84 | 
             
                }
         | 
| 85 85 | 
             
              end
         | 
| 86 86 |  | 
| 87 | 
            -
              def  | 
| 88 | 
            -
                {user: {}}
         | 
| 87 | 
            +
              def assert_default_context
         | 
| 88 | 
            +
                {user: {}, transaction_id: ScoutApm::RequestManager.lookup.transaction_id}
         | 
| 89 89 | 
             
              end
         | 
| 90 90 |  | 
| 91 91 | 
             
              def ex(msg="Whoops")
         | 
| @@ -18,16 +18,16 @@ class LimitedLayerTest < Minitest::Test | |
| 18 18 | 
             
                ll = ScoutApm::LimitedLayer.new("ActiveRecord")
         | 
| 19 19 |  | 
| 20 20 | 
             
                ll.absorb faux_layer("ActiveRecord", "User#Find", 2, 1, 200, 100)
         | 
| 21 | 
            -
                assert_equal  | 
| 21 | 
            +
                assert_equal 2, ll.total_exclusive_time
         | 
| 22 22 | 
             
                assert_equal 2, ll.total_call_time
         | 
| 23 | 
            -
                assert_equal  | 
| 23 | 
            +
                assert_equal 200, ll.total_exclusive_allocations
         | 
| 24 24 | 
             
                assert_equal 200, ll.total_allocations
         | 
| 25 25 |  | 
| 26 26 |  | 
| 27 27 | 
             
                ll.absorb faux_layer("ActiveRecord", "User#Find", 4, 3, 400, 300)
         | 
| 28 | 
            -
                assert_equal  | 
| 28 | 
            +
                assert_equal 6, ll.total_exclusive_time           # 4 + 2 (for limited layers, should equal total time)
         | 
| 29 29 | 
             
                assert_equal 6, ll.total_call_time                # 4 + 2
         | 
| 30 | 
            -
                assert_equal  | 
| 30 | 
            +
                assert_equal 600, ll.total_exclusive_allocations  # 400 + 200 (same goes for allocations)
         | 
| 31 31 | 
             
                assert_equal 600, ll.total_allocations            # 400 + 200
         | 
| 32 32 | 
             
              end
         | 
| 33 33 |  | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: scout_apm
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 5. | 
| 4 | 
            +
              version: 5.8.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Derek Haynes
         | 
| @@ -413,7 +413,6 @@ files: | |
| 413 413 | 
             
            - lib/scout_apm/utils/numbers.rb
         | 
| 414 414 | 
             
            - lib/scout_apm/utils/scm.rb
         | 
| 415 415 | 
             
            - lib/scout_apm/utils/sql_sanitizer.rb
         | 
| 416 | 
            -
            - lib/scout_apm/utils/time.rb
         | 
| 417 416 | 
             
            - lib/scout_apm/utils/unique_id.rb
         | 
| 418 417 | 
             
            - lib/scout_apm/version.rb
         | 
| 419 418 | 
             
            - lib/tasks/doctor.rake
         | 
| @@ -435,6 +434,8 @@ files: | |
| 435 434 | 
             
            - test/unit/auto_instrument/rescue_from-instrumented.rb
         | 
| 436 435 | 
             
            - test/unit/auto_instrument/rescue_from.rb
         | 
| 437 436 | 
             
            - test/unit/auto_instrument_test.rb
         | 
| 437 | 
            +
            - test/unit/background_job_integrations/faktory_test.rb
         | 
| 438 | 
            +
            - test/unit/background_job_integrations/shoryuken_test.rb
         | 
| 438 439 | 
             
            - test/unit/background_job_integrations/sidekiq_test.rb
         | 
| 439 440 | 
             
            - test/unit/config_test.rb
         | 
| 440 441 | 
             
            - test/unit/context_test.rb
         | 
    
        data/lib/scout_apm/utils/time.rb
    DELETED
    
    | @@ -1,12 +0,0 @@ | |
| 1 | 
            -
            module ScoutApm
         | 
| 2 | 
            -
              module Utils
         | 
| 3 | 
            -
                class Time
         | 
| 4 | 
            -
                  # Handles both integer (unix) time and Time objects
         | 
| 5 | 
            -
                  # example output:  "09/10/15 04:34:28 -0600"
         | 
| 6 | 
            -
                  def self.to_s(time)
         | 
| 7 | 
            -
                    return to_s(::Time.at(time)) if time.is_a? Integer
         | 
| 8 | 
            -
                    time.strftime("%m/%d/%y %H:%M:%S %z")
         | 
| 9 | 
            -
                  end
         | 
| 10 | 
            -
                end
         | 
| 11 | 
            -
              end
         | 
| 12 | 
            -
            end
         |