appsignal 3.10.0-java → 3.11.0-java
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 +1 -1
- data/CHANGELOG.md +88 -0
- data/Gemfile +1 -0
- data/benchmark.rake +99 -42
- data/lib/appsignal/cli/demo.rb +0 -1
- data/lib/appsignal/config.rb +54 -98
- data/lib/appsignal/demo.rb +15 -20
- data/lib/appsignal/event_formatter/rom/sql_formatter.rb +1 -0
- data/lib/appsignal/event_formatter.rb +3 -2
- data/lib/appsignal/helpers/instrumentation.rb +331 -19
- data/lib/appsignal/hooks/action_cable.rb +21 -16
- data/lib/appsignal/hooks/active_job.rb +14 -8
- data/lib/appsignal/hooks/delayed_job.rb +1 -1
- data/lib/appsignal/hooks/shoryuken.rb +3 -63
- data/lib/appsignal/integrations/action_cable.rb +5 -7
- data/lib/appsignal/integrations/active_support_notifications.rb +1 -0
- data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +36 -35
- data/lib/appsignal/integrations/data_mapper.rb +1 -0
- data/lib/appsignal/integrations/delayed_job_plugin.rb +27 -33
- data/lib/appsignal/integrations/dry_monitor.rb +1 -0
- data/lib/appsignal/integrations/excon.rb +1 -0
- data/lib/appsignal/integrations/http.rb +1 -0
- data/lib/appsignal/integrations/net_http.rb +1 -0
- data/lib/appsignal/integrations/object.rb +6 -0
- data/lib/appsignal/integrations/que.rb +13 -20
- data/lib/appsignal/integrations/railtie.rb +1 -1
- data/lib/appsignal/integrations/rake.rb +1 -5
- data/lib/appsignal/integrations/redis.rb +1 -0
- data/lib/appsignal/integrations/redis_client.rb +1 -0
- data/lib/appsignal/integrations/resque.rb +2 -5
- data/lib/appsignal/integrations/shoryuken.rb +75 -0
- data/lib/appsignal/integrations/sidekiq.rb +7 -15
- data/lib/appsignal/integrations/unicorn.rb +1 -0
- data/lib/appsignal/integrations/webmachine.rb +2 -5
- data/lib/appsignal/logger.rb +7 -3
- data/lib/appsignal/probes/helpers.rb +1 -0
- data/lib/appsignal/probes/mri.rb +1 -0
- data/lib/appsignal/probes/sidekiq.rb +1 -0
- data/lib/appsignal/probes.rb +3 -0
- data/lib/appsignal/rack/abstract_middleware.rb +18 -12
- data/lib/appsignal/rack/event_handler.rb +39 -8
- data/lib/appsignal/rack/generic_instrumentation.rb +1 -0
- data/lib/appsignal/rack/grape_middleware.rb +2 -1
- data/lib/appsignal/rack/streaming_listener.rb +1 -0
- data/lib/appsignal/rack.rb +29 -0
- data/lib/appsignal/span.rb +1 -0
- data/lib/appsignal/transaction.rb +308 -101
- data/lib/appsignal/utils/data.rb +0 -1
- data/lib/appsignal/utils/hash_sanitizer.rb +0 -1
- data/lib/appsignal/utils/integration_logger.rb +0 -13
- data/lib/appsignal/utils/integration_memory_logger.rb +0 -13
- data/lib/appsignal/utils/json.rb +0 -1
- data/lib/appsignal/utils/query_params_sanitizer.rb +0 -1
- data/lib/appsignal/utils/stdout_and_logger_message.rb +0 -1
- data/lib/appsignal/utils.rb +6 -0
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +6 -5
- data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
- data/spec/lib/appsignal/config_spec.rb +138 -43
- data/spec/lib/appsignal/hooks/action_cable_spec.rb +43 -74
- data/spec/lib/appsignal/hooks/activejob_spec.rb +9 -0
- data/spec/lib/appsignal/hooks/delayed_job_spec.rb +2 -443
- data/spec/lib/appsignal/hooks/shoryuken_spec.rb +0 -171
- data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +459 -0
- data/spec/lib/appsignal/integrations/que_spec.rb +3 -4
- data/spec/lib/appsignal/integrations/shoryuken_spec.rb +167 -0
- data/spec/lib/appsignal/integrations/sidekiq_spec.rb +4 -4
- data/spec/lib/appsignal/integrations/webmachine_spec.rb +13 -1
- data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +48 -3
- data/spec/lib/appsignal/rack/event_handler_spec.rb +81 -10
- data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +4 -2
- data/spec/lib/appsignal/rack_spec.rb +63 -0
- data/spec/lib/appsignal/transaction_spec.rb +1634 -1071
- data/spec/lib/appsignal/utils/integration_logger_spec.rb +12 -16
- data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +0 -10
- data/spec/lib/appsignal_spec.rb +323 -10
- data/spec/support/helpers/transaction_helpers.rb +44 -20
- data/spec/support/matchers/transaction.rb +15 -1
- data/spec/support/testing.rb +1 -1
- metadata +6 -2
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
describe "Appsignal::Integrations::DelayedJobHook" do
|
|
2
|
+
before(:context) do
|
|
3
|
+
module Delayed
|
|
4
|
+
class Plugin
|
|
5
|
+
def self.callbacks
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class Worker
|
|
10
|
+
def self.plugins
|
|
11
|
+
@plugins ||= []
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
require "appsignal/integrations/delayed_job_plugin"
|
|
16
|
+
end
|
|
17
|
+
after(:context) { Object.send(:remove_const, :Delayed) }
|
|
18
|
+
before { start_agent }
|
|
19
|
+
|
|
20
|
+
# We haven't found a way to test the hooks, we'll have to do that manually
|
|
21
|
+
|
|
22
|
+
describe ".invoke_with_instrumentation" do
|
|
23
|
+
let(:plugin) { Appsignal::Integrations::DelayedJobPlugin }
|
|
24
|
+
let(:time) { Time.parse("01-01-2001 10:01:00UTC") }
|
|
25
|
+
let(:created_at) { time - 3600 }
|
|
26
|
+
let(:run_at) { time - 3600 }
|
|
27
|
+
let(:payload_object) { double(:args => args) }
|
|
28
|
+
let(:job_data) do
|
|
29
|
+
{
|
|
30
|
+
:id => 123,
|
|
31
|
+
:name => "TestClass#perform",
|
|
32
|
+
:priority => 1,
|
|
33
|
+
:attempts => 1,
|
|
34
|
+
:queue => "default",
|
|
35
|
+
:created_at => created_at,
|
|
36
|
+
:run_at => run_at,
|
|
37
|
+
:payload_object => payload_object
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
let(:args) { ["argument"] }
|
|
41
|
+
let(:job) { double(job_data) }
|
|
42
|
+
let(:invoked_block) { proc {} }
|
|
43
|
+
|
|
44
|
+
def perform
|
|
45
|
+
Timecop.freeze(time) do
|
|
46
|
+
keep_transactions do
|
|
47
|
+
plugin.invoke_with_instrumentation(job, invoked_block)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
context "with a normal call" do
|
|
53
|
+
it "wraps it in a transaction" do
|
|
54
|
+
perform
|
|
55
|
+
|
|
56
|
+
transaction = last_transaction
|
|
57
|
+
expect(transaction).to have_namespace("background_job")
|
|
58
|
+
expect(transaction).to have_action("TestClass#perform")
|
|
59
|
+
expect(transaction).to_not have_error
|
|
60
|
+
expect(transaction).to include_event(:name => "perform_job.delayed_job")
|
|
61
|
+
expect(transaction).to include_tags(
|
|
62
|
+
"priority" => 1,
|
|
63
|
+
"attempts" => 1,
|
|
64
|
+
"queue" => "default",
|
|
65
|
+
"id" => "123"
|
|
66
|
+
)
|
|
67
|
+
expect(transaction).to include_params(["argument"])
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
context "with more complex params" do
|
|
71
|
+
let(:args) do
|
|
72
|
+
{
|
|
73
|
+
:foo => "Foo",
|
|
74
|
+
:bar => "Bar"
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "adds the more complex arguments" do
|
|
79
|
+
perform
|
|
80
|
+
|
|
81
|
+
expect(last_transaction).to include_params("foo" => "Foo", "bar" => "Bar")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
context "with parameter filtering" do
|
|
85
|
+
before do
|
|
86
|
+
Appsignal.config = project_fixture_config("production")
|
|
87
|
+
Appsignal.config[:filter_parameters] = ["foo"]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it "filters selected arguments" do
|
|
91
|
+
perform
|
|
92
|
+
|
|
93
|
+
expect(last_transaction).to include_params("foo" => "[FILTERED]", "bar" => "Bar")
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
context "with run_at in the future" do
|
|
99
|
+
let(:run_at) { Time.parse("2017-01-01 10:01:00UTC") }
|
|
100
|
+
|
|
101
|
+
it "reports queue_start with run_at time" do
|
|
102
|
+
perform
|
|
103
|
+
|
|
104
|
+
expect(last_transaction).to have_queue_start(run_at.to_i * 1000)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
context "with class method job" do
|
|
109
|
+
let(:job_data) do
|
|
110
|
+
{ :name => "CustomClassMethod.perform", :payload_object => payload_object }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "wraps it in a transaction using the class method job name" do
|
|
114
|
+
perform
|
|
115
|
+
expect(last_transaction).to have_action("CustomClassMethod.perform")
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
context "with custom name call" do
|
|
120
|
+
before { perform }
|
|
121
|
+
|
|
122
|
+
context "with appsignal_name defined" do
|
|
123
|
+
context "with payload_object being an object" do
|
|
124
|
+
context "with value" do
|
|
125
|
+
let(:payload_object) { double(:appsignal_name => "CustomClass#perform") }
|
|
126
|
+
|
|
127
|
+
it "wraps it in a transaction using the custom name" do
|
|
128
|
+
expect(last_transaction).to have_action("CustomClass#perform")
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
context "with non-String value" do
|
|
133
|
+
let(:payload_object) { double(:appsignal_name => Object.new) }
|
|
134
|
+
|
|
135
|
+
it "wraps it in a transaction using the original job name" do
|
|
136
|
+
expect(last_transaction).to have_action("TestClass#perform")
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
context "with class method name as job" do
|
|
141
|
+
let(:payload_object) { double(:appsignal_name => "CustomClassMethod.perform") }
|
|
142
|
+
|
|
143
|
+
it "wraps it in a transaction using the custom name" do
|
|
144
|
+
perform
|
|
145
|
+
expect(last_transaction).to have_action("CustomClassMethod.perform")
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
context "with payload_object being a Hash" do
|
|
151
|
+
context "with value" do
|
|
152
|
+
let(:payload_object) { double(:appsignal_name => "CustomClassHash#perform") }
|
|
153
|
+
|
|
154
|
+
it "wraps it in a transaction using the custom name" do
|
|
155
|
+
expect(last_transaction).to have_action("CustomClassHash#perform")
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
context "with non-String value" do
|
|
160
|
+
let(:payload_object) { double(:appsignal_name => Object.new) }
|
|
161
|
+
|
|
162
|
+
it "wraps it in a transaction using the original job name" do
|
|
163
|
+
expect(last_transaction).to have_action("TestClass#perform")
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
context "with class method name as job" do
|
|
168
|
+
let(:payload_object) { { :appsignal_name => "CustomClassMethod.perform" } }
|
|
169
|
+
|
|
170
|
+
it "wraps it in a transaction using the custom name" do
|
|
171
|
+
perform
|
|
172
|
+
expect(last_transaction).to have_action("CustomClassMethod.perform")
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
context "with payload_object acting like a Hash and returning a non-String value" do
|
|
178
|
+
class ClassActingAsHash
|
|
179
|
+
def self.[](_key)
|
|
180
|
+
Object.new
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def self.appsignal_name
|
|
184
|
+
"ClassActingAsHash#perform"
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
let(:payload_object) { ClassActingAsHash }
|
|
188
|
+
|
|
189
|
+
# We check for hash values before object values
|
|
190
|
+
# this means ClassActingAsHash returns `Object.new` instead
|
|
191
|
+
# of `self.appsignal_name`. Since this isn't a valid `String`
|
|
192
|
+
# we return the default job name as action name.
|
|
193
|
+
it "wraps it in a transaction using the original job name" do
|
|
194
|
+
expect(last_transaction).to have_action("TestClass#perform")
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
context "with only job class name" do
|
|
201
|
+
let(:job_data) do
|
|
202
|
+
{ :name => "Banana", :payload_object => payload_object }
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
it "appends #perform to the class name" do
|
|
206
|
+
perform
|
|
207
|
+
expect(last_transaction).to have_action("Banana#perform")
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
if active_job_present?
|
|
212
|
+
require "active_job"
|
|
213
|
+
|
|
214
|
+
context "when wrapped by ActiveJob" do
|
|
215
|
+
let(:payload_object) do
|
|
216
|
+
ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper.new(
|
|
217
|
+
"arguments" => args,
|
|
218
|
+
"job_class" => "TestClass",
|
|
219
|
+
"job_id" => 123,
|
|
220
|
+
"locale" => :en,
|
|
221
|
+
"queue_name" => "default"
|
|
222
|
+
)
|
|
223
|
+
end
|
|
224
|
+
let(:job) do
|
|
225
|
+
double(
|
|
226
|
+
:id => 123,
|
|
227
|
+
:name => "ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper",
|
|
228
|
+
:priority => 1,
|
|
229
|
+
:attempts => 1,
|
|
230
|
+
:queue => "default",
|
|
231
|
+
:created_at => created_at,
|
|
232
|
+
:run_at => run_at,
|
|
233
|
+
:payload_object => payload_object
|
|
234
|
+
)
|
|
235
|
+
end
|
|
236
|
+
let(:args) { ["activejob_argument"] }
|
|
237
|
+
|
|
238
|
+
it "wraps it in a transaction with the correct params" do
|
|
239
|
+
perform
|
|
240
|
+
|
|
241
|
+
transaction = last_transaction
|
|
242
|
+
expect(transaction).to have_namespace("background_job")
|
|
243
|
+
expect(transaction).to have_action("TestClass#perform")
|
|
244
|
+
expect(transaction).to_not have_error
|
|
245
|
+
expect(transaction).to include_event("name" => "perform_job.delayed_job")
|
|
246
|
+
expect(transaction).to include_tags(
|
|
247
|
+
"priority" => 1,
|
|
248
|
+
"attempts" => 1,
|
|
249
|
+
"queue" => "default",
|
|
250
|
+
"id" => "123"
|
|
251
|
+
)
|
|
252
|
+
expect(transaction).to include_params(["activejob_argument"])
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
context "with more complex params" do
|
|
256
|
+
let(:args) do
|
|
257
|
+
{
|
|
258
|
+
:foo => "Foo",
|
|
259
|
+
:bar => "Bar"
|
|
260
|
+
}
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
it "adds the more complex arguments" do
|
|
264
|
+
perform
|
|
265
|
+
transaction = last_transaction
|
|
266
|
+
expect(transaction).to have_action("TestClass#perform")
|
|
267
|
+
expect(transaction).to include_params(
|
|
268
|
+
"foo" => "Foo",
|
|
269
|
+
"bar" => "Bar"
|
|
270
|
+
)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
context "with parameter filtering" do
|
|
274
|
+
before do
|
|
275
|
+
Appsignal.config = project_fixture_config("production")
|
|
276
|
+
Appsignal.config[:filter_parameters] = ["foo"]
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
it "filters selected arguments" do
|
|
280
|
+
perform
|
|
281
|
+
transaction = last_transaction
|
|
282
|
+
expect(transaction).to have_action("TestClass#perform")
|
|
283
|
+
expect(transaction).to include_params(
|
|
284
|
+
"foo" => "[FILTERED]",
|
|
285
|
+
"bar" => "Bar"
|
|
286
|
+
)
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
context "with run_at in the future" do
|
|
292
|
+
let(:run_at) { Time.parse("2017-01-01 10:01:00UTC") }
|
|
293
|
+
|
|
294
|
+
it "reports queue_start with run_at time" do
|
|
295
|
+
perform
|
|
296
|
+
|
|
297
|
+
expect(last_transaction).to have_queue_start(run_at.to_i * 1000)
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
context "with an erroring call" do
|
|
305
|
+
let(:error) { ExampleException.new("uh oh") }
|
|
306
|
+
before do
|
|
307
|
+
expect(invoked_block).to receive(:call).and_raise(error)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
it "adds the error to the transaction" do
|
|
311
|
+
expect do
|
|
312
|
+
perform
|
|
313
|
+
end.to raise_error(error)
|
|
314
|
+
|
|
315
|
+
transaction = last_transaction
|
|
316
|
+
expect(transaction).to have_namespace("background_job")
|
|
317
|
+
expect(transaction).to have_action("TestClass#perform")
|
|
318
|
+
expect(transaction).to have_error("ExampleException", "uh oh")
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
describe ".extract_value" do
|
|
324
|
+
let(:plugin) { Appsignal::Integrations::DelayedJobPlugin }
|
|
325
|
+
|
|
326
|
+
context "for a hash" do
|
|
327
|
+
let(:hash) { { :key => "value", :bool_false => false } }
|
|
328
|
+
|
|
329
|
+
context "when the key exists" do
|
|
330
|
+
subject { plugin.extract_value(hash, :key) }
|
|
331
|
+
|
|
332
|
+
it { is_expected.to eq "value" }
|
|
333
|
+
|
|
334
|
+
context "when the value is false" do
|
|
335
|
+
subject { plugin.extract_value(hash, :bool_false) }
|
|
336
|
+
|
|
337
|
+
it { is_expected.to be false }
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
context "when the key does not exist" do
|
|
342
|
+
subject { plugin.extract_value(hash, :nonexistent_key) }
|
|
343
|
+
|
|
344
|
+
it { is_expected.to be_nil }
|
|
345
|
+
|
|
346
|
+
context "with a default value" do
|
|
347
|
+
subject { plugin.extract_value(hash, :nonexistent_key, 1) }
|
|
348
|
+
|
|
349
|
+
it { is_expected.to eq 1 }
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
context "for a struct" do
|
|
355
|
+
before :context do
|
|
356
|
+
TestStruct = Struct.new(:key)
|
|
357
|
+
end
|
|
358
|
+
let(:struct) { TestStruct.new("value") }
|
|
359
|
+
|
|
360
|
+
context "when the key exists" do
|
|
361
|
+
subject { plugin.extract_value(struct, :key) }
|
|
362
|
+
|
|
363
|
+
it { is_expected.to eq "value" }
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
context "when the key does not exist" do
|
|
367
|
+
subject { plugin.extract_value(struct, :nonexistent_key) }
|
|
368
|
+
|
|
369
|
+
it { is_expected.to be_nil }
|
|
370
|
+
|
|
371
|
+
context "with a default value" do
|
|
372
|
+
subject { plugin.extract_value(struct, :nonexistent_key, 1) }
|
|
373
|
+
|
|
374
|
+
it { is_expected.to eq 1 }
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
context "for a struct with a method" do
|
|
380
|
+
before :context do
|
|
381
|
+
class TestStructClass < Struct.new(:id) # rubocop:disable Style/StructInheritance
|
|
382
|
+
def appsignal_name
|
|
383
|
+
"TestStruct#perform"
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
def bool_false
|
|
387
|
+
false
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
let(:struct) { TestStructClass.new("id") }
|
|
392
|
+
|
|
393
|
+
context "when the Struct responds to a method" do
|
|
394
|
+
subject { plugin.extract_value(struct, :appsignal_name) }
|
|
395
|
+
|
|
396
|
+
it "returns the method value" do
|
|
397
|
+
is_expected.to eq "TestStruct#perform"
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
context "when the value is false" do
|
|
401
|
+
subject { plugin.extract_value(struct, :bool_false) }
|
|
402
|
+
|
|
403
|
+
it "returns the method value" do
|
|
404
|
+
is_expected.to be false
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
context "when the key does not exist" do
|
|
410
|
+
subject { plugin.extract_value(struct, :nonexistent_key) }
|
|
411
|
+
|
|
412
|
+
context "without a method with the same name" do
|
|
413
|
+
it "returns nil" do
|
|
414
|
+
is_expected.to be_nil
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
context "with a default value" do
|
|
419
|
+
let(:default_value) { :my_default_value }
|
|
420
|
+
subject { plugin.extract_value(struct, :nonexistent_key, default_value) }
|
|
421
|
+
|
|
422
|
+
it "returns the default value" do
|
|
423
|
+
is_expected.to eq default_value
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
context "for an object" do
|
|
430
|
+
let(:object) { double(:existing_method => "value") }
|
|
431
|
+
|
|
432
|
+
context "when the method exists" do
|
|
433
|
+
subject { plugin.extract_value(object, :existing_method) }
|
|
434
|
+
|
|
435
|
+
it { is_expected.to eq "value" }
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
context "when the method does not exist" do
|
|
439
|
+
subject { plugin.extract_value(object, :nonexistent_method) }
|
|
440
|
+
|
|
441
|
+
it { is_expected.to be_nil }
|
|
442
|
+
|
|
443
|
+
context "and there is a default value" do
|
|
444
|
+
subject { plugin.extract_value(object, :nonexistent_method, 1) }
|
|
445
|
+
|
|
446
|
+
it { is_expected.to eq 1 }
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
context "when we need to call to_s on the value" do
|
|
452
|
+
let(:object) { double(:existing_method => 1) }
|
|
453
|
+
|
|
454
|
+
subject { plugin.extract_value(object, :existing_method, nil, true) }
|
|
455
|
+
|
|
456
|
+
it { is_expected.to eq "1" }
|
|
457
|
+
end
|
|
458
|
+
end
|
|
459
|
+
end
|
|
@@ -39,7 +39,6 @@ if DependencyHelper.que_present?
|
|
|
39
39
|
allow(Que).to receive(:execute)
|
|
40
40
|
|
|
41
41
|
start_agent
|
|
42
|
-
expect(Appsignal.active?).to be_truthy
|
|
43
42
|
end
|
|
44
43
|
around { |example| keep_transactions { example.run } }
|
|
45
44
|
|
|
@@ -66,7 +65,7 @@ if DependencyHelper.que_present?
|
|
|
66
65
|
"title" => ""
|
|
67
66
|
)
|
|
68
67
|
expect(transaction).to include_params(%w[1 birds])
|
|
69
|
-
expect(transaction).to
|
|
68
|
+
expect(transaction).to include_tags(
|
|
70
69
|
"attempts" => 0,
|
|
71
70
|
"id" => 123,
|
|
72
71
|
"priority" => 100,
|
|
@@ -95,7 +94,7 @@ if DependencyHelper.que_present?
|
|
|
95
94
|
expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
|
|
96
95
|
expect(transaction).to have_error(error.class.name, error.message)
|
|
97
96
|
expect(transaction).to include_params(%w[1 birds])
|
|
98
|
-
expect(transaction).to
|
|
97
|
+
expect(transaction).to include_tags(
|
|
99
98
|
"attempts" => 0,
|
|
100
99
|
"id" => 123,
|
|
101
100
|
"priority" => 100,
|
|
@@ -120,7 +119,7 @@ if DependencyHelper.que_present?
|
|
|
120
119
|
expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
|
|
121
120
|
expect(transaction).to have_error(error.class.name, error.message)
|
|
122
121
|
expect(transaction).to include_params(%w[1 birds])
|
|
123
|
-
expect(transaction).to
|
|
122
|
+
expect(transaction).to include_tags(
|
|
124
123
|
"attempts" => 0,
|
|
125
124
|
"id" => 123,
|
|
126
125
|
"priority" => 100,
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
require "appsignal/integrations/shoryuken"
|
|
2
|
+
|
|
3
|
+
describe Appsignal::Integrations::ShoryukenMiddleware do
|
|
4
|
+
class DemoShoryukenWorker
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
let(:time) { "2010-01-01 10:01:00UTC" }
|
|
8
|
+
let(:worker_instance) { DemoShoryukenWorker.new }
|
|
9
|
+
let(:queue) { "some-funky-queue-name" }
|
|
10
|
+
let(:sqs_msg) { double(:message_id => "msg1", :attributes => {}) }
|
|
11
|
+
let(:body) { {} }
|
|
12
|
+
before { start_agent }
|
|
13
|
+
around { |example| keep_transactions { example.run } }
|
|
14
|
+
|
|
15
|
+
def perform_shoryuken_job(&block)
|
|
16
|
+
block ||= lambda {}
|
|
17
|
+
Timecop.freeze(Time.parse(time)) do
|
|
18
|
+
described_class.new.call(
|
|
19
|
+
worker_instance,
|
|
20
|
+
queue,
|
|
21
|
+
sqs_msg,
|
|
22
|
+
body,
|
|
23
|
+
&block
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
context "with a performance call" do
|
|
29
|
+
let(:sent_timestamp) { Time.parse("1976-11-18 0:00:00UTC").to_i * 1000 }
|
|
30
|
+
let(:sqs_msg) do
|
|
31
|
+
double(:message_id => "msg1", :attributes => { "SentTimestamp" => sent_timestamp })
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context "with complex argument" do
|
|
35
|
+
let(:body) { { :foo => "Foo", :bar => "Bar" } }
|
|
36
|
+
|
|
37
|
+
it "wraps the job in a transaction with the correct params" do
|
|
38
|
+
expect { perform_shoryuken_job }.to change { created_transactions.length }.by(1)
|
|
39
|
+
|
|
40
|
+
transaction = last_transaction
|
|
41
|
+
expect(transaction).to have_id
|
|
42
|
+
expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
|
|
43
|
+
expect(transaction).to have_action("DemoShoryukenWorker#perform")
|
|
44
|
+
expect(transaction).to_not have_error
|
|
45
|
+
expect(transaction).to include_event(
|
|
46
|
+
"body" => "",
|
|
47
|
+
"body_format" => Appsignal::EventFormatter::DEFAULT,
|
|
48
|
+
"count" => 1,
|
|
49
|
+
"name" => "perform_job.shoryuken",
|
|
50
|
+
"title" => ""
|
|
51
|
+
)
|
|
52
|
+
expect(transaction).to include_params("foo" => "Foo", "bar" => "Bar")
|
|
53
|
+
expect(transaction).to include_tags(
|
|
54
|
+
"message_id" => "msg1",
|
|
55
|
+
"queue" => queue,
|
|
56
|
+
"SentTimestamp" => sent_timestamp
|
|
57
|
+
)
|
|
58
|
+
expect(transaction).to have_queue_start(sent_timestamp)
|
|
59
|
+
expect(transaction).to be_completed
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context "with parameter filtering" do
|
|
63
|
+
before do
|
|
64
|
+
Appsignal.config = project_fixture_config("production")
|
|
65
|
+
Appsignal.config[:filter_parameters] = ["foo"]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "filters selected arguments" do
|
|
69
|
+
perform_shoryuken_job
|
|
70
|
+
|
|
71
|
+
expect(last_transaction).to include_params("foo" => "[FILTERED]", "bar" => "Bar")
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
context "with a string as an argument" do
|
|
77
|
+
let(:body) { "foo bar" }
|
|
78
|
+
|
|
79
|
+
it "handles string arguments" do
|
|
80
|
+
perform_shoryuken_job
|
|
81
|
+
|
|
82
|
+
expect(last_transaction).to include_params("params" => body)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
context "with primitive type as argument" do
|
|
87
|
+
let(:body) { 1 }
|
|
88
|
+
|
|
89
|
+
it "handles primitive types as arguments" do
|
|
90
|
+
perform_shoryuken_job
|
|
91
|
+
|
|
92
|
+
expect(last_transaction).to include_params("params" => body)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
context "with exception" do
|
|
98
|
+
it "sets the exception on the transaction" do
|
|
99
|
+
expect do
|
|
100
|
+
expect do
|
|
101
|
+
perform_shoryuken_job { raise ExampleException, "error message" }
|
|
102
|
+
end.to raise_error(ExampleException)
|
|
103
|
+
end.to change { created_transactions.length }.by(1)
|
|
104
|
+
|
|
105
|
+
transaction = last_transaction
|
|
106
|
+
expect(transaction).to have_id
|
|
107
|
+
expect(transaction).to have_action("DemoShoryukenWorker#perform")
|
|
108
|
+
expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
|
|
109
|
+
expect(transaction).to have_error("ExampleException", "error message")
|
|
110
|
+
expect(transaction).to be_completed
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
context "with batched jobs" do
|
|
115
|
+
let(:sqs_msg) do
|
|
116
|
+
[
|
|
117
|
+
double(
|
|
118
|
+
:message_id => "msg2",
|
|
119
|
+
:attributes => {
|
|
120
|
+
"SentTimestamp" => (Time.parse("1976-11-18 01:00:00UTC").to_i * 1000).to_s
|
|
121
|
+
}
|
|
122
|
+
),
|
|
123
|
+
double(
|
|
124
|
+
:message_id => "msg1",
|
|
125
|
+
:attributes => { "SentTimestamp" => sent_timestamp.to_s }
|
|
126
|
+
)
|
|
127
|
+
]
|
|
128
|
+
end
|
|
129
|
+
let(:body) do
|
|
130
|
+
[
|
|
131
|
+
"foo bar",
|
|
132
|
+
{ :id => "123", :foo => "Foo", :bar => "Bar" }
|
|
133
|
+
]
|
|
134
|
+
end
|
|
135
|
+
let(:sent_timestamp) { Time.parse("1976-11-18 01:00:00UTC").to_i * 1000 }
|
|
136
|
+
|
|
137
|
+
it "creates a transaction for the batch" do
|
|
138
|
+
expect do
|
|
139
|
+
perform_shoryuken_job {} # rubocop:disable Lint/EmptyBlock
|
|
140
|
+
end.to change { created_transactions.length }.by(1)
|
|
141
|
+
|
|
142
|
+
transaction = last_transaction
|
|
143
|
+
expect(transaction).to have_id
|
|
144
|
+
expect(transaction).to have_action("DemoShoryukenWorker#perform")
|
|
145
|
+
expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
|
|
146
|
+
expect(transaction).to_not have_error
|
|
147
|
+
expect(transaction).to include_event(
|
|
148
|
+
"body" => "",
|
|
149
|
+
"body_format" => Appsignal::EventFormatter::DEFAULT,
|
|
150
|
+
"count" => 1,
|
|
151
|
+
"name" => "perform_job.shoryuken",
|
|
152
|
+
"title" => ""
|
|
153
|
+
)
|
|
154
|
+
expect(transaction).to include_params(
|
|
155
|
+
"msg2" => "foo bar",
|
|
156
|
+
"msg1" => { "id" => "123", "foo" => "Foo", "bar" => "Bar" }
|
|
157
|
+
)
|
|
158
|
+
expect(transaction).to include_tags(
|
|
159
|
+
"batch" => true,
|
|
160
|
+
"queue" => "some-funky-queue-name",
|
|
161
|
+
"SentTimestamp" => sent_timestamp.to_s # Earliest/oldest timestamp from messages
|
|
162
|
+
)
|
|
163
|
+
# Queue time based on earliest/oldest timestamp from messages
|
|
164
|
+
expect(transaction).to have_queue_start(sent_timestamp)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -362,7 +362,7 @@ describe Appsignal::Integrations::SidekiqMiddleware, :with_yaml_parse_error => f
|
|
|
362
362
|
perform_sidekiq_job { raise error, "uh oh" }
|
|
363
363
|
end.to raise_error(error)
|
|
364
364
|
|
|
365
|
-
expect(transaction).to have_id
|
|
365
|
+
expect(transaction).to have_id
|
|
366
366
|
expect(transaction).to have_namespace(namespace)
|
|
367
367
|
expect(transaction).to have_action("TestClass#perform")
|
|
368
368
|
expect(transaction).to have_error("ExampleException", "uh oh")
|
|
@@ -373,7 +373,7 @@ describe Appsignal::Integrations::SidekiqMiddleware, :with_yaml_parse_error => f
|
|
|
373
373
|
)
|
|
374
374
|
expect(transaction).to_not include_environment
|
|
375
375
|
expect(transaction).to include_params(expected_args)
|
|
376
|
-
expect(transaction).
|
|
376
|
+
expect(transaction).to include_tags("request_id" => jid)
|
|
377
377
|
expect(transaction).to_not include_breadcrumbs
|
|
378
378
|
expect_transaction_to_have_sidekiq_event(transaction)
|
|
379
379
|
end
|
|
@@ -418,11 +418,11 @@ describe Appsignal::Integrations::SidekiqMiddleware, :with_yaml_parse_error => f
|
|
|
418
418
|
.with("sidekiq_queue_job_count", 1, { :queue => "default", :status => :processed })
|
|
419
419
|
perform_sidekiq_job
|
|
420
420
|
|
|
421
|
-
expect(transaction).to have_id
|
|
421
|
+
expect(transaction).to have_id
|
|
422
422
|
expect(transaction).to have_namespace(namespace)
|
|
423
423
|
expect(transaction).to have_action("TestClass#perform")
|
|
424
424
|
expect(transaction).to_not have_error
|
|
425
|
-
expect(transaction).
|
|
425
|
+
expect(transaction).to include_tags("request_id" => jid)
|
|
426
426
|
expect(transaction).to_not include_environment
|
|
427
427
|
expect(transaction).to_not include_breadcrumbs
|
|
428
428
|
expect(transaction).to_not include_params(expected_args)
|