appsignal 2.2.1 → 2.3.0.beta.1

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.
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