appsignal 2.11.1 → 2.11.6
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/.rubocop.yml +2 -0
 - data/.semaphore/semaphore.yml +197 -0
 - data/CHANGELOG.md +23 -0
 - data/README.md +9 -0
 - data/Rakefile +9 -6
 - data/build_matrix.yml +13 -0
 - data/ext/agent.yml +17 -17
 - data/ext/base.rb +12 -9
 - data/gemfiles/no_dependencies.gemfile +7 -0
 - data/gemfiles/resque-2.gemfile +0 -1
 - data/gemfiles/webmachine.gemfile +1 -0
 - data/lib/appsignal/cli/diagnose/utils.rb +8 -11
 - data/lib/appsignal/cli/install.rb +5 -8
 - data/lib/appsignal/helpers/instrumentation.rb +32 -0
 - data/lib/appsignal/hooks/shoryuken.rb +43 -4
 - data/lib/appsignal/hooks/sidekiq.rb +5 -1
 - data/lib/appsignal/integrations/object.rb +4 -34
 - data/lib/appsignal/integrations/object_ruby_19.rb +37 -0
 - data/lib/appsignal/integrations/object_ruby_modern.rb +64 -0
 - data/lib/appsignal/system.rb +4 -0
 - data/lib/appsignal/transaction.rb +30 -2
 - data/lib/appsignal/version.rb +1 -1
 - data/spec/lib/appsignal/hooks/resque_spec.rb +10 -2
 - data/spec/lib/appsignal/hooks/shoryuken_spec.rb +151 -104
 - data/spec/lib/appsignal/hooks/sidekiq_spec.rb +65 -9
 - data/spec/lib/appsignal/integrations/object_19_spec.rb +266 -0
 - data/spec/lib/appsignal/integrations/object_spec.rb +29 -10
 - data/spec/lib/appsignal/transaction_spec.rb +55 -0
 - data/spec/lib/appsignal_spec.rb +30 -0
 - metadata +10 -6
 
    
        data/lib/appsignal/version.rb
    CHANGED
    
    
| 
         @@ -60,7 +60,10 @@ describe Appsignal::Hooks::ResqueHook do 
     | 
|
| 
       60 
60 
     | 
    
         
             
                      "error" => nil,
         
     | 
| 
       61 
61 
     | 
    
         
             
                      "namespace" => namespace,
         
     | 
| 
       62 
62 
     | 
    
         
             
                      "metadata" => {},
         
     | 
| 
       63 
     | 
    
         
            -
                      "sample_data" => { 
     | 
| 
      
 63 
     | 
    
         
            +
                      "sample_data" => {
         
     | 
| 
      
 64 
     | 
    
         
            +
                        "breadcrumbs" => [],
         
     | 
| 
      
 65 
     | 
    
         
            +
                        "tags" => { "queue" => queue }
         
     | 
| 
      
 66 
     | 
    
         
            +
                      }
         
     | 
| 
       64 
67 
     | 
    
         
             
                    )
         
     | 
| 
       65 
68 
     | 
    
         
             
                    expect(transaction_hash["events"].map { |e| e["name"] })
         
     | 
| 
       66 
69 
     | 
    
         
             
                      .to eql(["perform.resque"])
         
     | 
| 
         @@ -84,7 +87,10 @@ describe Appsignal::Hooks::ResqueHook do 
     | 
|
| 
       84 
87 
     | 
    
         
             
                        },
         
     | 
| 
       85 
88 
     | 
    
         
             
                        "namespace" => namespace,
         
     | 
| 
       86 
89 
     | 
    
         
             
                        "metadata" => {},
         
     | 
| 
       87 
     | 
    
         
            -
                        "sample_data" => { 
     | 
| 
      
 90 
     | 
    
         
            +
                        "sample_data" => {
         
     | 
| 
      
 91 
     | 
    
         
            +
                          "breadcrumbs" => [],
         
     | 
| 
      
 92 
     | 
    
         
            +
                          "tags" => { "queue" => queue }
         
     | 
| 
      
 93 
     | 
    
         
            +
                        }
         
     | 
| 
       88 
94 
     | 
    
         
             
                      )
         
     | 
| 
       89 
95 
     | 
    
         
             
                    end
         
     | 
| 
       90 
96 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -118,6 +124,7 @@ describe Appsignal::Hooks::ResqueHook do 
     | 
|
| 
       118 
124 
     | 
    
         
             
                        "metadata" => {},
         
     | 
| 
       119 
125 
     | 
    
         
             
                        "sample_data" => {
         
     | 
| 
       120 
126 
     | 
    
         
             
                          "tags" => { "queue" => queue },
         
     | 
| 
      
 127 
     | 
    
         
            +
                          "breadcrumbs" => [],
         
     | 
| 
       121 
128 
     | 
    
         
             
                          "params" => [
         
     | 
| 
       122 
129 
     | 
    
         
             
                            "foo",
         
     | 
| 
       123 
130 
     | 
    
         
             
                            {
         
     | 
| 
         @@ -174,6 +181,7 @@ describe Appsignal::Hooks::ResqueHook do 
     | 
|
| 
       174 
181 
     | 
    
         
             
                        "namespace" => namespace,
         
     | 
| 
       175 
182 
     | 
    
         
             
                        "metadata" => {},
         
     | 
| 
       176 
183 
     | 
    
         
             
                        "sample_data" => {
         
     | 
| 
      
 184 
     | 
    
         
            +
                          "breadcrumbs" => [],
         
     | 
| 
       177 
185 
     | 
    
         
             
                          "tags" => { "queue" => queue }
         
     | 
| 
       178 
186 
     | 
    
         
             
                          # Params will be set by the ActiveJob integration
         
     | 
| 
       179 
187 
     | 
    
         
             
                        }
         
     | 
| 
         @@ -1,55 +1,73 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            describe Appsignal::Hooks::ShoryukenMiddleware do
         
     | 
| 
       2 
     | 
    
         
            -
              let(:current_transaction) { background_job_transaction }
         
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
2 
     | 
    
         
             
              class DemoShoryukenWorker
         
     | 
| 
       5 
3 
     | 
    
         
             
              end
         
     | 
| 
       6 
4 
     | 
    
         | 
| 
      
 5 
     | 
    
         
            +
              let(:time) { "2010-01-01 10:01:00UTC" }
         
     | 
| 
       7 
6 
     | 
    
         
             
              let(:worker_instance) { DemoShoryukenWorker.new }
         
     | 
| 
       8 
     | 
    
         
            -
              let(:queue) {  
     | 
| 
       9 
     | 
    
         
            -
              let(:sqs_msg) { double(:attributes => {}) }
         
     | 
| 
      
 7 
     | 
    
         
            +
              let(:queue) { "some-funky-queue-name" }
         
     | 
| 
      
 8 
     | 
    
         
            +
              let(:sqs_msg) { double(:message_id => "msg1", :attributes => {}) }
         
     | 
| 
       10 
9 
     | 
    
         
             
              let(:body) { {} }
         
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
               
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
      
 10 
     | 
    
         
            +
              before(:context) { start_agent }
         
     | 
| 
      
 11 
     | 
    
         
            +
              around { |example| keep_transactions { example.run } }
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              def perform_job(&block)
         
     | 
| 
      
 14 
     | 
    
         
            +
                block ||= lambda {}
         
     | 
| 
      
 15 
     | 
    
         
            +
                Timecop.freeze(Time.parse(time)) do
         
     | 
| 
      
 16 
     | 
    
         
            +
                  Appsignal::Hooks::ShoryukenMiddleware.new.call(
         
     | 
| 
      
 17 
     | 
    
         
            +
                    worker_instance,
         
     | 
| 
      
 18 
     | 
    
         
            +
                    queue,
         
     | 
| 
      
 19 
     | 
    
         
            +
                    sqs_msg,
         
     | 
| 
      
 20 
     | 
    
         
            +
                    body,
         
     | 
| 
      
 21 
     | 
    
         
            +
                    &block
         
     | 
| 
      
 22 
     | 
    
         
            +
                  )
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
       15 
24 
     | 
    
         
             
              end
         
     | 
| 
       16 
25 
     | 
    
         | 
| 
       17 
26 
     | 
    
         
             
              context "with a performance call" do
         
     | 
| 
       18 
     | 
    
         
            -
                let(: 
     | 
| 
      
 27 
     | 
    
         
            +
                let(:sent_timestamp) { Time.parse("1976-11-18 0:00:00UTC").to_i * 1000 }
         
     | 
| 
       19 
28 
     | 
    
         
             
                let(:sqs_msg) do
         
     | 
| 
       20 
     | 
    
         
            -
                  double(:attributes => { "SentTimestamp" =>  
     | 
| 
      
 29 
     | 
    
         
            +
                  double(:message_id => "msg1", :attributes => { "SentTimestamp" => sent_timestamp })
         
     | 
| 
       21 
30 
     | 
    
         
             
                end
         
     | 
| 
       22 
31 
     | 
    
         | 
| 
       23 
32 
     | 
    
         
             
                context "with complex argument" do
         
     | 
| 
       24 
     | 
    
         
            -
                  let(:body)  
     | 
| 
       25 
     | 
    
         
            -
                    {
         
     | 
| 
       26 
     | 
    
         
            -
                      :foo => "Foo",
         
     | 
| 
       27 
     | 
    
         
            -
                      :bar => "Bar"
         
     | 
| 
       28 
     | 
    
         
            -
                    }
         
     | 
| 
       29 
     | 
    
         
            -
                  end
         
     | 
| 
       30 
     | 
    
         
            -
                  after do
         
     | 
| 
       31 
     | 
    
         
            -
                    Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
         
     | 
| 
       32 
     | 
    
         
            -
                      Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
         
     | 
| 
       33 
     | 
    
         
            -
                        # nothing
         
     | 
| 
       34 
     | 
    
         
            -
                      end
         
     | 
| 
       35 
     | 
    
         
            -
                    end
         
     | 
| 
       36 
     | 
    
         
            -
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
                  let(:body) { { :foo => "Foo", :bar => "Bar" } }
         
     | 
| 
       37 
34 
     | 
    
         | 
| 
       38 
35 
     | 
    
         
             
                  it "wraps the job in a transaction with the correct params" do
         
     | 
| 
       39 
     | 
    
         
            -
                     
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
                       
     | 
| 
       47 
     | 
    
         
            -
                       
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
                      },
         
     | 
| 
       51 
     | 
    
         
            -
                      :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
         
     | 
| 
      
 36 
     | 
    
         
            +
                    allow_any_instance_of(Appsignal::Transaction).to receive(:set_queue_start).and_call_original
         
     | 
| 
      
 37 
     | 
    
         
            +
                    expect { perform_job }.to change { created_transactions.length }.by(1)
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    transaction = last_transaction
         
     | 
| 
      
 40 
     | 
    
         
            +
                    expect(transaction).to be_completed
         
     | 
| 
      
 41 
     | 
    
         
            +
                    transaction_hash = transaction.to_h
         
     | 
| 
      
 42 
     | 
    
         
            +
                    expect(transaction_hash).to include(
         
     | 
| 
      
 43 
     | 
    
         
            +
                      "action" => "DemoShoryukenWorker#perform",
         
     | 
| 
      
 44 
     | 
    
         
            +
                      "id" => kind_of(String), # AppSignal generated id
         
     | 
| 
      
 45 
     | 
    
         
            +
                      "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
         
     | 
| 
      
 46 
     | 
    
         
            +
                      "error" => nil
         
     | 
| 
       52 
47 
     | 
    
         
             
                    )
         
     | 
| 
      
 48 
     | 
    
         
            +
                    expect(transaction_hash["events"].first).to include(
         
     | 
| 
      
 49 
     | 
    
         
            +
                      "allocation_count" => kind_of(Integer),
         
     | 
| 
      
 50 
     | 
    
         
            +
                      "body" => "",
         
     | 
| 
      
 51 
     | 
    
         
            +
                      "body_format" => Appsignal::EventFormatter::DEFAULT,
         
     | 
| 
      
 52 
     | 
    
         
            +
                      "child_allocation_count" => kind_of(Integer),
         
     | 
| 
      
 53 
     | 
    
         
            +
                      "child_duration" => kind_of(Float),
         
     | 
| 
      
 54 
     | 
    
         
            +
                      "child_gc_duration" => kind_of(Float),
         
     | 
| 
      
 55 
     | 
    
         
            +
                      "count" => 1,
         
     | 
| 
      
 56 
     | 
    
         
            +
                      "gc_duration" => kind_of(Float),
         
     | 
| 
      
 57 
     | 
    
         
            +
                      "start" => kind_of(Float),
         
     | 
| 
      
 58 
     | 
    
         
            +
                      "duration" => kind_of(Float),
         
     | 
| 
      
 59 
     | 
    
         
            +
                      "name" => "perform_job.shoryuken",
         
     | 
| 
      
 60 
     | 
    
         
            +
                      "title" => ""
         
     | 
| 
      
 61 
     | 
    
         
            +
                    )
         
     | 
| 
      
 62 
     | 
    
         
            +
                    expect(transaction_hash["sample_data"]).to include(
         
     | 
| 
      
 63 
     | 
    
         
            +
                      "params" => { "foo" => "Foo", "bar" => "Bar" },
         
     | 
| 
      
 64 
     | 
    
         
            +
                      "metadata" => {
         
     | 
| 
      
 65 
     | 
    
         
            +
                        "message_id" => "msg1",
         
     | 
| 
      
 66 
     | 
    
         
            +
                        "queue" => queue,
         
     | 
| 
      
 67 
     | 
    
         
            +
                        "SentTimestamp" => sent_timestamp
         
     | 
| 
      
 68 
     | 
    
         
            +
                      }
         
     | 
| 
      
 69 
     | 
    
         
            +
                    )
         
     | 
| 
      
 70 
     | 
    
         
            +
                    expect(transaction).to have_received(:set_queue_start).with(sent_timestamp)
         
     | 
| 
       53 
71 
     | 
    
         
             
                  end
         
     | 
| 
       54 
72 
     | 
    
         | 
| 
       55 
73 
     | 
    
         
             
                  context "with parameter filtering" do
         
     | 
| 
         @@ -57,21 +75,16 @@ describe Appsignal::Hooks::ShoryukenMiddleware do 
     | 
|
| 
       57 
75 
     | 
    
         
             
                      Appsignal.config = project_fixture_config("production")
         
     | 
| 
       58 
76 
     | 
    
         
             
                      Appsignal.config[:filter_parameters] = ["foo"]
         
     | 
| 
       59 
77 
     | 
    
         
             
                    end
         
     | 
| 
      
 78 
     | 
    
         
            +
                    after do
         
     | 
| 
      
 79 
     | 
    
         
            +
                      Appsignal.config[:filter_parameters] = []
         
     | 
| 
      
 80 
     | 
    
         
            +
                    end
         
     | 
| 
       60 
81 
     | 
    
         | 
| 
       61 
82 
     | 
    
         
             
                    it "filters selected arguments" do
         
     | 
| 
       62 
     | 
    
         
            -
                       
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
             
     | 
| 
       66 
     | 
    
         
            -
                         
     | 
| 
       67 
     | 
    
         
            -
                          :queue => "some-funky-queue-name",
         
     | 
| 
       68 
     | 
    
         
            -
                          "SentTimestamp" => 217_123_200_000
         
     | 
| 
       69 
     | 
    
         
            -
                        },
         
     | 
| 
       70 
     | 
    
         
            -
                        :params => {
         
     | 
| 
       71 
     | 
    
         
            -
                          :foo => "[FILTERED]",
         
     | 
| 
       72 
     | 
    
         
            -
                          :bar => "Bar"
         
     | 
| 
       73 
     | 
    
         
            -
                        },
         
     | 
| 
       74 
     | 
    
         
            -
                        :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
         
     | 
| 
      
 83 
     | 
    
         
            +
                      perform_job
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                      transaction_hash = last_transaction.to_h
         
     | 
| 
      
 86 
     | 
    
         
            +
                      expect(transaction_hash["sample_data"]).to include(
         
     | 
| 
      
 87 
     | 
    
         
            +
                        "params" => { "foo" => "[FILTERED]", "bar" => "Bar" }
         
     | 
| 
       75 
88 
     | 
    
         
             
                      )
         
     | 
| 
       76 
89 
     | 
    
         
             
                    end
         
     | 
| 
       77 
90 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -81,23 +94,12 @@ describe Appsignal::Hooks::ShoryukenMiddleware do 
     | 
|
| 
       81 
94 
     | 
    
         
             
                  let(:body) { "foo bar" }
         
     | 
| 
       82 
95 
     | 
    
         | 
| 
       83 
96 
     | 
    
         
             
                  it "handles string arguments" do
         
     | 
| 
       84 
     | 
    
         
            -
                     
     | 
| 
       85 
     | 
    
         
            -
                      "perform_job.shoryuken",
         
     | 
| 
       86 
     | 
    
         
            -
                      :class => "DemoShoryukenWorker",
         
     | 
| 
       87 
     | 
    
         
            -
                      :method => "perform",
         
     | 
| 
       88 
     | 
    
         
            -
                      :metadata => {
         
     | 
| 
       89 
     | 
    
         
            -
                        :queue => "some-funky-queue-name",
         
     | 
| 
       90 
     | 
    
         
            -
                        "SentTimestamp" => 217_123_200_000
         
     | 
| 
       91 
     | 
    
         
            -
                      },
         
     | 
| 
       92 
     | 
    
         
            -
                      :params => { :params => body },
         
     | 
| 
       93 
     | 
    
         
            -
                      :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
         
     | 
| 
       94 
     | 
    
         
            -
                    )
         
     | 
| 
      
 97 
     | 
    
         
            +
                    perform_job
         
     | 
| 
       95 
98 
     | 
    
         | 
| 
       96 
     | 
    
         
            -
                     
     | 
| 
       97 
     | 
    
         
            -
             
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
                    end
         
     | 
| 
      
 99 
     | 
    
         
            +
                    transaction_hash = last_transaction.to_h
         
     | 
| 
      
 100 
     | 
    
         
            +
                    expect(transaction_hash["sample_data"]).to include(
         
     | 
| 
      
 101 
     | 
    
         
            +
                      "params" => { "params" => body }
         
     | 
| 
      
 102 
     | 
    
         
            +
                    )
         
     | 
| 
       101 
103 
     | 
    
         
             
                  end
         
     | 
| 
       102 
104 
     | 
    
         
             
                end
         
     | 
| 
       103 
105 
     | 
    
         | 
| 
         @@ -105,58 +107,103 @@ describe Appsignal::Hooks::ShoryukenMiddleware do 
     | 
|
| 
       105 
107 
     | 
    
         
             
                  let(:body) { 1 }
         
     | 
| 
       106 
108 
     | 
    
         | 
| 
       107 
109 
     | 
    
         
             
                  it "handles primitive types as arguments" do
         
     | 
| 
       108 
     | 
    
         
            -
                     
     | 
| 
       109 
     | 
    
         
            -
                      "perform_job.shoryuken",
         
     | 
| 
       110 
     | 
    
         
            -
                      :class => "DemoShoryukenWorker",
         
     | 
| 
       111 
     | 
    
         
            -
                      :method => "perform",
         
     | 
| 
       112 
     | 
    
         
            -
                      :metadata => {
         
     | 
| 
       113 
     | 
    
         
            -
                        :queue => "some-funky-queue-name",
         
     | 
| 
       114 
     | 
    
         
            -
                        "SentTimestamp" => 217_123_200_000
         
     | 
| 
       115 
     | 
    
         
            -
                      },
         
     | 
| 
       116 
     | 
    
         
            -
                      :params => { :params => body },
         
     | 
| 
       117 
     | 
    
         
            -
                      :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
         
     | 
| 
       118 
     | 
    
         
            -
                    )
         
     | 
| 
      
 110 
     | 
    
         
            +
                    perform_job
         
     | 
| 
       119 
111 
     | 
    
         | 
| 
       120 
     | 
    
         
            -
                     
     | 
| 
       121 
     | 
    
         
            -
             
     | 
| 
       122 
     | 
    
         
            -
             
     | 
| 
       123 
     | 
    
         
            -
             
     | 
| 
       124 
     | 
    
         
            -
                    end
         
     | 
| 
      
 112 
     | 
    
         
            +
                    transaction_hash = last_transaction.to_h
         
     | 
| 
      
 113 
     | 
    
         
            +
                    expect(transaction_hash["sample_data"]).to include(
         
     | 
| 
      
 114 
     | 
    
         
            +
                      "params" => { "params" => body }
         
     | 
| 
      
 115 
     | 
    
         
            +
                    )
         
     | 
| 
       125 
116 
     | 
    
         
             
                  end
         
     | 
| 
       126 
117 
     | 
    
         
             
                end
         
     | 
| 
       127 
118 
     | 
    
         
             
              end
         
     | 
| 
       128 
119 
     | 
    
         | 
| 
       129 
120 
     | 
    
         
             
              context "with exception" do
         
     | 
| 
       130 
     | 
    
         
            -
                 
     | 
| 
       131 
     | 
    
         
            -
                   
     | 
| 
       132 
     | 
    
         
            -
                     
     | 
| 
       133 
     | 
    
         
            -
             
     | 
| 
       134 
     | 
    
         
            -
                     
     | 
| 
      
 121 
     | 
    
         
            +
                it "sets the exception on the transaction" do
         
     | 
| 
      
 122 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 123 
     | 
    
         
            +
                    expect do
         
     | 
| 
      
 124 
     | 
    
         
            +
                      perform_job { raise ExampleException, "error message" }
         
     | 
| 
      
 125 
     | 
    
         
            +
                    end.to raise_error(ExampleException)
         
     | 
| 
      
 126 
     | 
    
         
            +
                  end.to change { created_transactions.length }.by(1)
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                  transaction = last_transaction
         
     | 
| 
      
 129 
     | 
    
         
            +
                  expect(transaction).to be_completed
         
     | 
| 
      
 130 
     | 
    
         
            +
                  transaction_hash = transaction.to_h
         
     | 
| 
      
 131 
     | 
    
         
            +
                  expect(transaction_hash).to include(
         
     | 
| 
      
 132 
     | 
    
         
            +
                    "action" => "DemoShoryukenWorker#perform",
         
     | 
| 
      
 133 
     | 
    
         
            +
                    "id" => kind_of(String), # AppSignal generated id
         
     | 
| 
      
 134 
     | 
    
         
            +
                    "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
         
     | 
| 
      
 135 
     | 
    
         
            +
                    "error" => {
         
     | 
| 
      
 136 
     | 
    
         
            +
                      "name" => "ExampleException",
         
     | 
| 
      
 137 
     | 
    
         
            +
                      "message" => "error message",
         
     | 
| 
      
 138 
     | 
    
         
            +
                      "backtrace" => kind_of(String)
         
     | 
| 
      
 139 
     | 
    
         
            +
                    }
         
     | 
| 
       135 
140 
     | 
    
         
             
                  )
         
     | 
| 
       136 
141 
     | 
    
         
             
                end
         
     | 
| 
      
 142 
     | 
    
         
            +
              end
         
     | 
| 
       137 
143 
     | 
    
         | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
             
     | 
| 
       140 
     | 
    
         
            -
                   
     | 
| 
       141 
     | 
    
         
            -
                     
     | 
| 
       142 
     | 
    
         
            -
                       
     | 
| 
       143 
     | 
    
         
            -
                       
     | 
| 
       144 
     | 
    
         
            -
             
     | 
| 
       145 
     | 
    
         
            -
                     
     | 
| 
      
 144 
     | 
    
         
            +
              context "with batched jobs" do
         
     | 
| 
      
 145 
     | 
    
         
            +
                let(:sqs_msg) do
         
     | 
| 
      
 146 
     | 
    
         
            +
                  [
         
     | 
| 
      
 147 
     | 
    
         
            +
                    double(
         
     | 
| 
      
 148 
     | 
    
         
            +
                      :message_id => "msg2",
         
     | 
| 
      
 149 
     | 
    
         
            +
                      :attributes => { "SentTimestamp" => (Time.parse("1976-11-18 01:00:00UTC").to_i * 1000).to_s }
         
     | 
| 
      
 150 
     | 
    
         
            +
                    ),
         
     | 
| 
      
 151 
     | 
    
         
            +
                    double(
         
     | 
| 
      
 152 
     | 
    
         
            +
                      :message_id => "msg1",
         
     | 
| 
      
 153 
     | 
    
         
            +
                      :attributes => { "SentTimestamp" => sent_timestamp.to_s }
         
     | 
| 
      
 154 
     | 
    
         
            +
                    )
         
     | 
| 
      
 155 
     | 
    
         
            +
                  ]
         
     | 
| 
       146 
156 
     | 
    
         
             
                end
         
     | 
| 
       147 
     | 
    
         
            -
             
     | 
| 
       148 
     | 
    
         
            -
             
     | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
      
 157 
     | 
    
         
            +
                let(:body) do
         
     | 
| 
      
 158 
     | 
    
         
            +
                  [
         
     | 
| 
      
 159 
     | 
    
         
            +
                    "foo bar",
         
     | 
| 
      
 160 
     | 
    
         
            +
                    { :id => "123", :foo => "Foo", :bar => "Bar" }
         
     | 
| 
      
 161 
     | 
    
         
            +
                  ]
         
     | 
| 
       150 
162 
     | 
    
         
             
                end
         
     | 
| 
      
 163 
     | 
    
         
            +
                let(:sent_timestamp) { Time.parse("1976-11-18 01:00:00UTC").to_i * 1000 }
         
     | 
| 
       151 
164 
     | 
    
         | 
| 
       152 
     | 
    
         
            -
                 
     | 
| 
      
 165 
     | 
    
         
            +
                it "creates a transaction for the batch" do
         
     | 
| 
      
 166 
     | 
    
         
            +
                  allow_any_instance_of(Appsignal::Transaction).to receive(:set_queue_start).and_call_original
         
     | 
| 
       153 
167 
     | 
    
         
             
                  expect do
         
     | 
| 
       154 
     | 
    
         
            -
                     
     | 
| 
       155 
     | 
    
         
            -
             
     | 
| 
       156 
     | 
    
         
            -
             
     | 
| 
       157 
     | 
    
         
            -
             
     | 
| 
       158 
     | 
    
         
            -
             
     | 
| 
       159 
     | 
    
         
            -
                   
     | 
| 
      
 168 
     | 
    
         
            +
                    perform_job {}
         
     | 
| 
      
 169 
     | 
    
         
            +
                  end.to change { created_transactions.length }.by(1)
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                  transaction = last_transaction
         
     | 
| 
      
 172 
     | 
    
         
            +
                  expect(transaction).to be_completed
         
     | 
| 
      
 173 
     | 
    
         
            +
                  transaction_hash = transaction.to_h
         
     | 
| 
      
 174 
     | 
    
         
            +
                  expect(transaction_hash).to include(
         
     | 
| 
      
 175 
     | 
    
         
            +
                    "action" => "DemoShoryukenWorker#perform",
         
     | 
| 
      
 176 
     | 
    
         
            +
                    "id" => kind_of(String), # AppSignal generated id
         
     | 
| 
      
 177 
     | 
    
         
            +
                    "namespace" => Appsignal::Transaction::BACKGROUND_JOB,
         
     | 
| 
      
 178 
     | 
    
         
            +
                    "error" => nil
         
     | 
| 
      
 179 
     | 
    
         
            +
                  )
         
     | 
| 
      
 180 
     | 
    
         
            +
                  expect(transaction_hash["events"].first).to include(
         
     | 
| 
      
 181 
     | 
    
         
            +
                    "allocation_count" => kind_of(Integer),
         
     | 
| 
      
 182 
     | 
    
         
            +
                    "body" => "",
         
     | 
| 
      
 183 
     | 
    
         
            +
                    "body_format" => Appsignal::EventFormatter::DEFAULT,
         
     | 
| 
      
 184 
     | 
    
         
            +
                    "child_allocation_count" => kind_of(Integer),
         
     | 
| 
      
 185 
     | 
    
         
            +
                    "child_duration" => kind_of(Float),
         
     | 
| 
      
 186 
     | 
    
         
            +
                    "child_gc_duration" => kind_of(Float),
         
     | 
| 
      
 187 
     | 
    
         
            +
                    "count" => 1,
         
     | 
| 
      
 188 
     | 
    
         
            +
                    "gc_duration" => kind_of(Float),
         
     | 
| 
      
 189 
     | 
    
         
            +
                    "start" => kind_of(Float),
         
     | 
| 
      
 190 
     | 
    
         
            +
                    "duration" => kind_of(Float),
         
     | 
| 
      
 191 
     | 
    
         
            +
                    "name" => "perform_job.shoryuken",
         
     | 
| 
      
 192 
     | 
    
         
            +
                    "title" => ""
         
     | 
| 
      
 193 
     | 
    
         
            +
                  )
         
     | 
| 
      
 194 
     | 
    
         
            +
                  expect(transaction_hash["sample_data"]).to include(
         
     | 
| 
      
 195 
     | 
    
         
            +
                    "params" => {
         
     | 
| 
      
 196 
     | 
    
         
            +
                      "msg2" => "foo bar",
         
     | 
| 
      
 197 
     | 
    
         
            +
                      "msg1" => { "id" => "123", "foo" => "Foo", "bar" => "Bar" }
         
     | 
| 
      
 198 
     | 
    
         
            +
                    },
         
     | 
| 
      
 199 
     | 
    
         
            +
                    "metadata" => {
         
     | 
| 
      
 200 
     | 
    
         
            +
                      "batch" => true,
         
     | 
| 
      
 201 
     | 
    
         
            +
                      "queue" => "some-funky-queue-name",
         
     | 
| 
      
 202 
     | 
    
         
            +
                      "SentTimestamp" => sent_timestamp.to_s # Earliest/oldest timestamp from messages
         
     | 
| 
      
 203 
     | 
    
         
            +
                    }
         
     | 
| 
      
 204 
     | 
    
         
            +
                  )
         
     | 
| 
      
 205 
     | 
    
         
            +
                  # Queue time based on earliest/oldest timestamp from messages
         
     | 
| 
      
 206 
     | 
    
         
            +
                  expect(transaction).to have_received(:set_queue_start).with(sent_timestamp)
         
     | 
| 
       160 
207 
     | 
    
         
             
                end
         
     | 
| 
       161 
208 
     | 
    
         
             
              end
         
     | 
| 
       162 
209 
     | 
    
         
             
            end
         
     | 
| 
         @@ -16,14 +16,31 @@ describe Appsignal::Hooks::SidekiqHook do 
     | 
|
| 
       16 
16 
     | 
    
         
             
              end
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
       18 
18 
     | 
    
         
             
              describe "#install" do
         
     | 
| 
       19 
     | 
    
         
            -
                class  
     | 
| 
       20 
     | 
    
         
            -
                   
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
      
 19 
     | 
    
         
            +
                class SidekiqMiddlewareMockWithPrepend < Array
         
     | 
| 
      
 20 
     | 
    
         
            +
                  alias add <<
         
     | 
| 
      
 21 
     | 
    
         
            +
                  alias exists? include?
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  unless method_defined? :prepend
         
     | 
| 
      
 24 
     | 
    
         
            +
                    def prepend(middleware) # For Ruby < 2.5
         
     | 
| 
      
 25 
     | 
    
         
            +
                      insert(0, middleware)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    end
         
     | 
| 
       22 
27 
     | 
    
         
             
                  end
         
     | 
| 
       23 
28 
     | 
    
         
             
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                class SidekiqMiddlewareMockWithoutPrepend < Array
         
     | 
| 
      
 31 
     | 
    
         
            +
                  alias add <<
         
     | 
| 
      
 32 
     | 
    
         
            +
                  alias exists? include?
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  undef_method :prepend if method_defined? :prepend # For Ruby >= 2.5
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
       24 
37 
     | 
    
         
             
                module SidekiqMock
         
     | 
| 
      
 38 
     | 
    
         
            +
                  def self.middleware_mock=(mock)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    @middlewares = mock.new
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
       25 
42 
     | 
    
         
             
                  def self.middlewares
         
     | 
| 
       26 
     | 
    
         
            -
                    @middlewares 
     | 
| 
      
 43 
     | 
    
         
            +
                    @middlewares
         
     | 
| 
       27 
44 
     | 
    
         
             
                  end
         
     | 
| 
       28 
45 
     | 
    
         | 
| 
       29 
46 
     | 
    
         
             
                  def self.configure_server
         
     | 
| 
         @@ -36,15 +53,52 @@ describe Appsignal::Hooks::SidekiqHook do 
     | 
|
| 
       36 
53 
     | 
    
         
             
                  end
         
     | 
| 
       37 
54 
     | 
    
         
             
                end
         
     | 
| 
       38 
55 
     | 
    
         | 
| 
      
 56 
     | 
    
         
            +
                def add_middleware(middleware)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  Sidekiq.configure_server do |sidekiq_config|
         
     | 
| 
      
 58 
     | 
    
         
            +
                    sidekiq_config.middlewares.add(middleware)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
       39 
62 
     | 
    
         
             
                before do
         
     | 
| 
       40 
63 
     | 
    
         
             
                  Appsignal.config = project_fixture_config
         
     | 
| 
       41 
64 
     | 
    
         
             
                  stub_const "Sidekiq", SidekiqMock
         
     | 
| 
       42 
65 
     | 
    
         
             
                end
         
     | 
| 
       43 
66 
     | 
    
         | 
| 
       44 
     | 
    
         
            -
                 
     | 
| 
       45 
     | 
    
         
            -
                   
     | 
| 
      
 67 
     | 
    
         
            +
                context "when Sidekiq middleware responds to prepend method" do # Sidekiq 3.3.0 and newer
         
     | 
| 
      
 68 
     | 
    
         
            +
                  before { Sidekiq.middleware_mock = SidekiqMiddlewareMockWithPrepend }
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                  it "adds the AppSignal SidekiqPlugin to the Sidekiq middleware chain in the first position" do
         
     | 
| 
      
 71 
     | 
    
         
            +
                    user_middleware1 = proc {}
         
     | 
| 
      
 72 
     | 
    
         
            +
                    add_middleware(user_middleware1)
         
     | 
| 
      
 73 
     | 
    
         
            +
                    described_class.new.install
         
     | 
| 
      
 74 
     | 
    
         
            +
                    user_middleware2 = proc {}
         
     | 
| 
      
 75 
     | 
    
         
            +
                    add_middleware(user_middleware2)
         
     | 
| 
       46 
76 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
      
 77 
     | 
    
         
            +
                    expect(Sidekiq.server_middleware).to eql([
         
     | 
| 
      
 78 
     | 
    
         
            +
                      Appsignal::Hooks::SidekiqPlugin, # Prepend makes it the first entry
         
     | 
| 
      
 79 
     | 
    
         
            +
                      user_middleware1,
         
     | 
| 
      
 80 
     | 
    
         
            +
                      user_middleware2
         
     | 
| 
      
 81 
     | 
    
         
            +
                    ])
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
                end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                context "when Sidekiq middleware does not respond to prepend method" do
         
     | 
| 
      
 86 
     | 
    
         
            +
                  before { Sidekiq.middleware_mock = SidekiqMiddlewareMockWithoutPrepend }
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  it "adds the AppSignal SidekiqPlugin to the Sidekiq middleware chain" do
         
     | 
| 
      
 89 
     | 
    
         
            +
                    user_middleware1 = proc {}
         
     | 
| 
      
 90 
     | 
    
         
            +
                    add_middleware(user_middleware1)
         
     | 
| 
      
 91 
     | 
    
         
            +
                    described_class.new.install
         
     | 
| 
      
 92 
     | 
    
         
            +
                    user_middleware2 = proc {}
         
     | 
| 
      
 93 
     | 
    
         
            +
                    add_middleware(user_middleware2)
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                    # Add middlewares in whatever order they were added
         
     | 
| 
      
 96 
     | 
    
         
            +
                    expect(Sidekiq.server_middleware).to eql([
         
     | 
| 
      
 97 
     | 
    
         
            +
                      user_middleware1,
         
     | 
| 
      
 98 
     | 
    
         
            +
                      Appsignal::Hooks::SidekiqPlugin,
         
     | 
| 
      
 99 
     | 
    
         
            +
                      user_middleware2
         
     | 
| 
      
 100 
     | 
    
         
            +
                    ])
         
     | 
| 
      
 101 
     | 
    
         
            +
                  end
         
     | 
| 
       48 
102 
     | 
    
         
             
                end
         
     | 
| 
       49 
103 
     | 
    
         
             
              end
         
     | 
| 
       50 
104 
     | 
    
         
             
            end
         
     | 
| 
         @@ -262,7 +316,8 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do 
     | 
|
| 
       262 
316 
     | 
    
         
             
                    "sample_data" => {
         
     | 
| 
       263 
317 
     | 
    
         
             
                      "environment" => {},
         
     | 
| 
       264 
318 
     | 
    
         
             
                      "params" => expected_args,
         
     | 
| 
       265 
     | 
    
         
            -
                      "tags" => {}
         
     | 
| 
      
 319 
     | 
    
         
            +
                      "tags" => {},
         
     | 
| 
      
 320 
     | 
    
         
            +
                      "breadcrumbs" => []
         
     | 
| 
       266 
321 
     | 
    
         
             
                    }
         
     | 
| 
       267 
322 
     | 
    
         
             
                  )
         
     | 
| 
       268 
323 
     | 
    
         
             
                  expect_transaction_to_have_sidekiq_event(transaction_hash)
         
     | 
| 
         @@ -290,7 +345,8 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do 
     | 
|
| 
       290 
345 
     | 
    
         
             
                    "sample_data" => {
         
     | 
| 
       291 
346 
     | 
    
         
             
                      "environment" => {},
         
     | 
| 
       292 
347 
     | 
    
         
             
                      "params" => expected_args,
         
     | 
| 
       293 
     | 
    
         
            -
                      "tags" => {}
         
     | 
| 
      
 348 
     | 
    
         
            +
                      "tags" => {},
         
     | 
| 
      
 349 
     | 
    
         
            +
                      "breadcrumbs" => []
         
     | 
| 
       294 
350 
     | 
    
         
             
                    }
         
     | 
| 
       295 
351 
     | 
    
         
             
                  )
         
     | 
| 
       296 
352 
     | 
    
         
             
                  # TODO: Not available in transaction.to_h yet.
         
     |