appsignal 2.2.1 → 2.3.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/appsignal.gemspec +1 -2
  4. data/ext/agent.yml +11 -11
  5. data/ext/appsignal_extension.c +17 -1
  6. data/lib/appsignal/config.rb +7 -4
  7. data/lib/appsignal/hooks.rb +1 -6
  8. data/lib/appsignal/hooks/action_cable.rb +113 -0
  9. data/lib/appsignal/hooks/active_support_notifications.rb +12 -4
  10. data/lib/appsignal/hooks/shoryuken.rb +11 -11
  11. data/lib/appsignal/hooks/sidekiq.rb +5 -3
  12. data/lib/appsignal/integrations/delayed_job_plugin.rb +5 -1
  13. data/lib/appsignal/integrations/resque_active_job.rb +4 -1
  14. data/lib/appsignal/transaction.rb +40 -13
  15. data/lib/appsignal/version.rb +1 -1
  16. data/spec/lib/appsignal/config_spec.rb +8 -1
  17. data/spec/lib/appsignal/hooks/action_cable_spec.rb +370 -0
  18. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +39 -7
  19. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +179 -34
  20. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +125 -30
  21. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +140 -21
  22. data/spec/lib/appsignal/hooks_spec.rb +0 -21
  23. data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +62 -17
  24. data/spec/lib/appsignal/integrations/resque_spec.rb +24 -12
  25. data/spec/lib/appsignal/transaction_spec.rb +230 -91
  26. data/spec/spec_helper.rb +8 -2
  27. data/spec/support/helpers/dependency_helper.rb +4 -0
  28. data/spec/support/helpers/env_helpers.rb +1 -1
  29. data/spec/support/helpers/log_helpers.rb +17 -0
  30. data/spec/support/matchers/contains_log.rb +7 -0
  31. data/spec/support/shared_examples/instrument.rb +2 -2
  32. metadata +13 -20
@@ -25,6 +25,10 @@ describe Appsignal::Hooks::DelayedJobHook do
25
25
  it { is_expected.to be_truthy }
26
26
  end
27
27
 
28
+ it "adds the plugin" do
29
+ expect(::Delayed::Worker.plugins).to include Appsignal::Hooks::DelayedJobPlugin
30
+ end
31
+
28
32
  # We haven't found a way to test the hooks, we'll have to do that manually
29
33
 
30
34
  describe ".invoke_with_instrumentation" do
@@ -38,17 +42,16 @@ describe Appsignal::Hooks::DelayedJobHook do
38
42
  :attempts => 1,
39
43
  :queue => "default",
40
44
  :created_at => time - 60_000,
41
- :payload_object => double(:args => ["argument"])
45
+ :payload_object => double(:args => args)
42
46
  }
43
47
  end
48
+ let(:args) { ["argument"] }
44
49
  let(:job) { double(job_data) }
45
50
  let(:invoked_block) { proc {} }
46
- let(:error) { StandardError.new }
47
51
 
48
52
  context "with a normal call" do
49
- it "should wrap in a transaction with the correct params" do
50
- expect(Appsignal).to receive(:monitor_transaction).with(
51
- "perform_job.delayed_job",
53
+ let(:default_params) do
54
+ {
52
55
  :class => "TestClass",
53
56
  :method => "perform",
54
57
  :metadata => {
@@ -57,21 +60,68 @@ describe Appsignal::Hooks::DelayedJobHook do
57
60
  :queue => "default",
58
61
  :id => "123"
59
62
  },
60
- :params => ["argument"],
61
63
  :queue_start => time - 60_000
62
- )
63
-
64
+ }
65
+ end
66
+ after do
64
67
  Timecop.freeze(time) do
65
68
  plugin.invoke_with_instrumentation(job, invoked_block)
66
69
  end
67
70
  end
68
71
 
72
+ it "wraps it in a transaction with the correct params" do
73
+ expect(Appsignal).to receive(:monitor_transaction).with(
74
+ "perform_job.delayed_job",
75
+ default_params.merge(:params => ["argument"])
76
+ )
77
+ end
78
+
79
+ context "with more complex params" do
80
+ let(:args) do
81
+ {
82
+ :foo => "Foo",
83
+ :bar => "Bar"
84
+ }
85
+ end
86
+
87
+ it "adds the more complex arguments" do
88
+ expect(Appsignal).to receive(:monitor_transaction).with(
89
+ "perform_job.delayed_job",
90
+ default_params.merge(
91
+ :params => {
92
+ :foo => "Foo",
93
+ :bar => "Bar"
94
+ }
95
+ )
96
+ )
97
+ end
98
+
99
+ context "with parameter filtering" do
100
+ before do
101
+ Appsignal.config = project_fixture_config("production")
102
+ Appsignal.config[:filter_parameters] = ["foo"]
103
+ end
104
+
105
+ it "filters selected arguments" do
106
+ expect(Appsignal).to receive(:monitor_transaction).with(
107
+ "perform_job.delayed_job",
108
+ default_params.merge(
109
+ :params => {
110
+ :foo => "[FILTERED]",
111
+ :bar => "Bar"
112
+ }
113
+ )
114
+ )
115
+ end
116
+ end
117
+ end
118
+
69
119
  context "with custom name call" do
70
120
  let(:job_data) do
71
121
  {
72
122
  :payload_object => double(
73
123
  :appsignal_name => "CustomClass#perform",
74
- :args => ["argument"]
124
+ :args => args
75
125
  ),
76
126
  :id => "123",
77
127
  :name => "TestClass#perform",
@@ -81,9 +131,8 @@ describe Appsignal::Hooks::DelayedJobHook do
81
131
  :created_at => time - 60_000
82
132
  }
83
133
  end
84
- it "should wrap in a transaction with the correct params" do
85
- expect(Appsignal).to receive(:monitor_transaction).with(
86
- "perform_job.delayed_job",
134
+ let(:default_params) do
135
+ {
87
136
  :class => "CustomClass",
88
137
  :method => "perform",
89
138
  :metadata => {
@@ -92,12 +141,56 @@ describe Appsignal::Hooks::DelayedJobHook do
92
141
  :queue => "default",
93
142
  :id => "123"
94
143
  },
95
- :params => ["argument"],
96
144
  :queue_start => time - 60_000
145
+ }
146
+ end
147
+
148
+ it "wraps it in a transaction with the correct params" do
149
+ expect(Appsignal).to receive(:monitor_transaction).with(
150
+ "perform_job.delayed_job",
151
+ default_params.merge(
152
+ :params => ["argument"]
153
+ )
97
154
  )
155
+ end
98
156
 
99
- Timecop.freeze(time) do
100
- plugin.invoke_with_instrumentation(job, invoked_block)
157
+ context "with more complex params" do
158
+ let(:args) do
159
+ {
160
+ :foo => "Foo",
161
+ :bar => "Bar"
162
+ }
163
+ end
164
+
165
+ it "adds the more complex arguments" do
166
+ expect(Appsignal).to receive(:monitor_transaction).with(
167
+ "perform_job.delayed_job",
168
+ default_params.merge(
169
+ :params => {
170
+ :foo => "Foo",
171
+ :bar => "Bar"
172
+ }
173
+ )
174
+ )
175
+ end
176
+
177
+ context "with parameter filtering" do
178
+ before do
179
+ Appsignal.config = project_fixture_config("production")
180
+ Appsignal.config[:filter_parameters] = ["foo"]
181
+ end
182
+
183
+ it "filters selected arguments" do
184
+ expect(Appsignal).to receive(:monitor_transaction).with(
185
+ "perform_job.delayed_job",
186
+ default_params.merge(
187
+ :params => {
188
+ :foo => "[FILTERED]",
189
+ :bar => "Bar"
190
+ }
191
+ )
192
+ )
193
+ end
101
194
  end
102
195
  end
103
196
  end
@@ -106,14 +199,9 @@ describe Appsignal::Hooks::DelayedJobHook do
106
199
  require "active_job"
107
200
 
108
201
  context "when wrapped by ActiveJob" do
109
- before do
110
- job_data[:args] = ["argument"]
111
- end
112
202
  let(:job) { ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper.new(job_data) }
113
-
114
- it "should wrap in a transaction with the correct params" do
115
- expect(Appsignal).to receive(:monitor_transaction).with(
116
- "perform_job.delayed_job",
203
+ let(:default_params) do
204
+ {
117
205
  :class => "TestClass",
118
206
  :method => "perform",
119
207
  :metadata => {
@@ -122,12 +210,55 @@ describe Appsignal::Hooks::DelayedJobHook do
122
210
  :queue => "default",
123
211
  :id => "123"
124
212
  },
125
- :params => ["argument"],
126
213
  :queue_start => time - 60_000
214
+ }
215
+ end
216
+ before { job_data[:args] = args }
217
+
218
+ it "wraps it in a transaction with the correct params" do
219
+ expect(Appsignal).to receive(:monitor_transaction).with(
220
+ "perform_job.delayed_job",
221
+ default_params.merge(:params => ["argument"])
127
222
  )
223
+ end
224
+
225
+ context "with more complex params" do
226
+ let(:args) do
227
+ {
228
+ :foo => "Foo",
229
+ :bar => "Bar"
230
+ }
231
+ end
232
+
233
+ it "adds the more complex arguments" do
234
+ expect(Appsignal).to receive(:monitor_transaction).with(
235
+ "perform_job.delayed_job",
236
+ default_params.merge(
237
+ :params => {
238
+ :foo => "Foo",
239
+ :bar => "Bar"
240
+ }
241
+ )
242
+ )
243
+ end
244
+
245
+ context "with parameter filtering" do
246
+ before do
247
+ Appsignal.config = project_fixture_config("production")
248
+ Appsignal.config[:filter_parameters] = ["foo"]
249
+ end
128
250
 
129
- Timecop.freeze(time) do
130
- plugin.invoke_with_instrumentation(job, invoked_block)
251
+ it "filters selected arguments" do
252
+ expect(Appsignal).to receive(:monitor_transaction).with(
253
+ "perform_job.delayed_job",
254
+ default_params.merge(
255
+ :params => {
256
+ :foo => "[FILTERED]",
257
+ :bar => "Bar"
258
+ }
259
+ )
260
+ )
261
+ end
131
262
  end
132
263
  end
133
264
  end
@@ -135,22 +266,36 @@ describe Appsignal::Hooks::DelayedJobHook do
135
266
  end
136
267
 
137
268
  context "with an erroring call" do
138
- it "should add the error to the transaction" do
139
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_error).with(error)
140
- expect(Appsignal::Transaction).to receive(:complete_current!)
269
+ let(:error) { VerySpecificError }
270
+ let(:transaction) do
271
+ Appsignal::Transaction.new(
272
+ SecureRandom.uuid,
273
+ Appsignal::Transaction::BACKGROUND_JOB,
274
+ Appsignal::Transaction::GenericRequest.new({})
275
+ )
276
+ end
277
+ before do
278
+ expect(invoked_block).to receive(:call).and_raise(error)
141
279
 
142
- allow(invoked_block).to receive(:call).and_raise(error)
280
+ allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
281
+ expect(Appsignal::Transaction).to receive(:create)
282
+ .with(
283
+ kind_of(String),
284
+ Appsignal::Transaction::BACKGROUND_JOB,
285
+ kind_of(Appsignal::Transaction::GenericRequest)
286
+ ).and_return(transaction)
287
+ end
288
+
289
+ it "adds the error to the transaction" do
290
+ expect(transaction).to receive(:set_error).with(error)
291
+ expect(transaction).to receive(:complete)
143
292
 
144
293
  expect do
145
294
  plugin.invoke_with_instrumentation(job, invoked_block)
146
- end.to raise_error(StandardError)
295
+ end.to raise_error(error)
147
296
  end
148
297
  end
149
298
  end
150
-
151
- it "should add the plugin" do
152
- expect(::Delayed::Worker.plugins).to include Appsignal::Hooks::DelayedJobPlugin
153
- end
154
299
  end
155
300
 
156
301
  context "without delayed job" do
@@ -1,7 +1,10 @@
1
1
  describe Appsignal::Hooks::ShoryukenMiddleware do
2
2
  let(:current_transaction) { background_job_transaction }
3
3
 
4
- let(:worker_instance) { double }
4
+ class DemoShoryukenWorker
5
+ end
6
+
7
+ let(:worker_instance) { DemoShoryukenWorker.new }
5
8
  let(:queue) { double }
6
9
  let(:sqs_msg) { double(:attributes => {}) }
7
10
  let(:body) { {} }
@@ -16,52 +19,144 @@ describe Appsignal::Hooks::ShoryukenMiddleware do
16
19
  let(:sqs_msg) do
17
20
  double(:attributes => { "SentTimestamp" => Time.parse("1976-11-18 0:00:00UTC").to_i * 1000 })
18
21
  end
19
- let(:body) do
20
- {
21
- "job_class" => "TestClass",
22
- "arguments" => ["Model", "1"]
23
- }
22
+
23
+ context "with complex argument" do
24
+ let(:body) do
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
37
+
38
+ it "wraps the job in a transaction with the correct params" do
39
+ expect(Appsignal).to receive(:monitor_transaction).with(
40
+ "perform_job.shoryuken",
41
+ :class => "DemoShoryukenWorker",
42
+ :method => "perform",
43
+ :metadata => {
44
+ :queue => "some-funky-queue-name",
45
+ "SentTimestamp" => 217_123_200_000
46
+ },
47
+ :params => {
48
+ :foo => "Foo",
49
+ :bar => "Bar"
50
+ },
51
+ :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
52
+ )
53
+ end
54
+
55
+ context "with parameter filtering" do
56
+ before do
57
+ Appsignal.config = project_fixture_config("production")
58
+ Appsignal.config[:filter_parameters] = ["foo"]
59
+ end
60
+
61
+ it "filters selected arguments" do
62
+ expect(Appsignal).to receive(:monitor_transaction).with(
63
+ "perform_job.shoryuken",
64
+ :class => "DemoShoryukenWorker",
65
+ :method => "perform",
66
+ :metadata => {
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
75
+ )
76
+ end
77
+ end
24
78
  end
25
79
 
26
- it "should wrap in a transaction with the correct params" do
27
- expect(Appsignal).to receive(:monitor_transaction).with(
28
- "perform_job.shoryuken",
29
- :class => "TestClass",
30
- :method => "perform",
31
- :metadata => {
32
- :queue => "some-funky-queue-name",
33
- "SentTimestamp" => 217_123_200_000
34
- },
35
- :params => ["Model", "1"],
36
- :queue_start => Time.parse("1976-11-18 0:00:00UTC").utc
37
- )
80
+ context "with a string as an argument" do
81
+ let(:body) { "foo bar" }
82
+
83
+ it "handles string arguments" do
84
+ expect(Appsignal).to receive(:monitor_transaction).with(
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
+ )
95
+
96
+ Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
97
+ Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
98
+ # nothing
99
+ end
100
+ end
101
+ end
38
102
  end
39
103
 
40
- after do
41
- Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
42
- Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
43
- # nothing
104
+ context "with primitive type as argument" do
105
+ let(:body) { 1 }
106
+
107
+ it "handles primitive types as arguments" do
108
+ expect(Appsignal).to receive(:monitor_transaction).with(
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
+ )
119
+
120
+ Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
121
+ Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
122
+ # nothing
123
+ end
44
124
  end
45
125
  end
46
126
  end
47
127
  end
48
128
 
49
- context "with an erroring call" do
50
- let(:error) { VerySpecificError.new }
129
+ context "with exception" do
130
+ let(:transaction) do
131
+ Appsignal::Transaction.new(
132
+ SecureRandom.uuid,
133
+ Appsignal::Transaction::BACKGROUND_JOB,
134
+ Appsignal::Transaction::GenericRequest.new({})
135
+ )
136
+ end
137
+
138
+ before do
139
+ allow(Appsignal::Transaction).to receive(:current).and_return(transaction)
140
+ expect(Appsignal::Transaction).to receive(:create)
141
+ .with(
142
+ kind_of(String),
143
+ Appsignal::Transaction::BACKGROUND_JOB,
144
+ kind_of(Appsignal::Transaction::GenericRequest)
145
+ ).and_return(transaction)
146
+ end
51
147
 
52
- it "should add the exception to appsignal" do
53
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_error).with(error)
148
+ it "sets the exception on the transaction" do
149
+ expect(transaction).to receive(:set_error).with(VerySpecificError)
54
150
  end
55
151
 
56
152
  after do
57
- begin
153
+ expect do
58
154
  Timecop.freeze(Time.parse("01-01-2001 10:01:00UTC")) do
59
155
  Appsignal::Hooks::ShoryukenMiddleware.new.call(worker_instance, queue, sqs_msg, body) do
60
- raise error
156
+ raise VerySpecificError
61
157
  end
62
158
  end
63
- rescue VerySpecificError
64
- end
159
+ end.to raise_error(VerySpecificError)
65
160
  end
66
161
  end
67
162
  end