appsignal 2.11.0.alpha.1-java → 2.11.0.alpha.2-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/.semaphore/semaphore.yml +39 -53
- data/CHANGELOG.md +3 -1
- data/build_matrix.yml +8 -6
- data/ext/agent.yml +19 -19
- data/gemfiles/padrino.gemfile +2 -2
- data/lib/appsignal/hooks/puma.rb +2 -58
- data/lib/appsignal/hooks/sidekiq.rb +2 -99
- data/lib/appsignal/probes/puma.rb +61 -0
- data/lib/appsignal/probes/sidekiq.rb +102 -0
- data/lib/appsignal/rack/js_exception_catcher.rb +5 -2
- data/lib/appsignal/version.rb +1 -1
- data/lib/puma/plugin/appsignal.rb +2 -1
- data/spec/lib/appsignal/hooks/puma_spec.rb +2 -181
- data/spec/lib/appsignal/hooks/sidekiq_spec.rb +256 -462
- data/spec/lib/appsignal/integrations/padrino_spec.rb +1 -1
- data/spec/lib/appsignal/probes/puma_spec.rb +180 -0
- data/spec/lib/appsignal/probes/sidekiq_spec.rb +201 -0
- data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +9 -4
- data/spec/lib/puma/appsignal_spec.rb +1 -1
- data/spec/support/stubs/sidekiq/api.rb +1 -1
- metadata +8 -2
@@ -48,147 +48,225 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
|
|
48
48
|
expect(log_contents(log)).to_not contains_log(:warn, "Unable to load YAML")
|
49
49
|
end
|
50
50
|
|
51
|
-
shared_examples "
|
52
|
-
|
53
|
-
|
54
|
-
|
51
|
+
shared_examples "unknown job action name" do
|
52
|
+
it "sets the action name to unknown" do
|
53
|
+
transaction_hash = transaction.to_h
|
54
|
+
expect(transaction_hash).to include("action" => "unknown")
|
55
|
+
end
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
it "stores no sample data" do
|
58
|
+
transaction_hash = transaction.to_h
|
59
|
+
expect(transaction_hash).to include(
|
60
|
+
"sample_data" => {
|
61
|
+
"environment" => {},
|
62
|
+
"params" => [],
|
63
|
+
"tags" => {}
|
64
|
+
}
|
65
|
+
)
|
60
66
|
end
|
61
67
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
68
|
+
it "logs a debug message" do
|
69
|
+
expect(log_contents(log)).to contains_log(
|
70
|
+
:debug, "Unable to determine an action name from Sidekiq payload: #{item}"
|
71
|
+
)
|
72
|
+
end
|
73
|
+
end
|
67
74
|
|
68
|
-
|
69
|
-
|
75
|
+
describe "internal Sidekiq job values" do
|
76
|
+
it "does not save internal Sidekiq values as metadata on transaction" do
|
77
|
+
perform_job
|
70
78
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
"foo",
|
75
|
-
{
|
76
|
-
"foo" => "[FILTERED]",
|
77
|
-
"bar" => "Bar",
|
78
|
-
"baz" => { "1" => "foo" }
|
79
|
-
}
|
80
|
-
]
|
81
|
-
)
|
82
|
-
end
|
79
|
+
transaction_hash = transaction.to_h
|
80
|
+
expect(transaction_hash["metadata"].keys)
|
81
|
+
.to_not include(*Appsignal::Hooks::SidekiqPlugin::JOB_KEYS)
|
83
82
|
end
|
83
|
+
end
|
84
84
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
85
|
+
context "with parameter filtering" do
|
86
|
+
before do
|
87
|
+
Appsignal.config = project_fixture_config("production")
|
88
|
+
Appsignal.config[:filter_parameters] = ["foo"]
|
89
|
+
end
|
90
90
|
|
91
|
-
|
92
|
-
|
91
|
+
it "filters selected arguments" do
|
92
|
+
perform_job
|
93
93
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
94
|
+
transaction_hash = transaction.to_h
|
95
|
+
expect(transaction_hash["sample_data"]).to include(
|
96
|
+
"params" => [
|
97
|
+
"foo",
|
98
|
+
{
|
99
|
+
"foo" => "[FILTERED]",
|
100
|
+
"bar" => "Bar",
|
101
|
+
"baz" => { "1" => "foo" }
|
102
|
+
}
|
103
|
+
]
|
104
|
+
)
|
99
105
|
end
|
106
|
+
end
|
100
107
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
"queue" => "default",
|
107
|
-
"args" => [
|
108
|
-
"---\n- !ruby/class 'DelayedTestClass'\n- :foo_method\n- - :bar: baz\n"
|
109
|
-
],
|
110
|
-
"retry" => true,
|
111
|
-
"created_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
|
112
|
-
"enqueued_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
|
113
|
-
"extra" => "data"
|
114
|
-
}
|
115
|
-
end
|
108
|
+
context "with encrypted arguments" do
|
109
|
+
before do
|
110
|
+
item["encrypt"] = true
|
111
|
+
item["args"] << "super secret value" # Last argument will be replaced
|
112
|
+
end
|
116
113
|
|
117
|
-
|
114
|
+
it "replaces the last argument (the secret bag) with an [encrypted data] string" do
|
115
|
+
perform_job
|
116
|
+
|
117
|
+
transaction_hash = transaction.to_h
|
118
|
+
expect(transaction_hash["sample_data"]).to include(
|
119
|
+
"params" => expected_args << "[encrypted data]"
|
120
|
+
)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context "when using the Sidekiq delayed extension" do
|
125
|
+
let(:item) do
|
126
|
+
{
|
127
|
+
"jid" => "efb140489485999d32b5504c",
|
128
|
+
"class" => "Sidekiq::Extensions::DelayedClass",
|
129
|
+
"queue" => "default",
|
130
|
+
"args" => [
|
131
|
+
"---\n- !ruby/class 'DelayedTestClass'\n- :foo_method\n- - :bar: baz\n"
|
132
|
+
],
|
133
|
+
"retry" => true,
|
134
|
+
"created_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
|
135
|
+
"enqueued_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
|
136
|
+
"extra" => "data"
|
137
|
+
}
|
138
|
+
end
|
139
|
+
|
140
|
+
it "uses the delayed class and method name for the action" do
|
141
|
+
perform_job
|
142
|
+
|
143
|
+
transaction_hash = transaction.to_h
|
144
|
+
expect(transaction_hash["action"]).to eq("DelayedTestClass.foo_method")
|
145
|
+
expect(transaction_hash["sample_data"]).to include(
|
146
|
+
"params" => ["bar" => "baz"]
|
147
|
+
)
|
148
|
+
end
|
149
|
+
|
150
|
+
context "when job arguments is a malformed YAML object", :with_yaml_parse_error => true do
|
151
|
+
before { item["args"] = [] }
|
152
|
+
|
153
|
+
it "logs a warning and uses the default argument" do
|
118
154
|
perform_job
|
119
155
|
|
120
156
|
transaction_hash = transaction.to_h
|
121
|
-
expect(transaction_hash["action"]).to eq("
|
122
|
-
expect(transaction_hash["sample_data"]).to include(
|
123
|
-
|
124
|
-
)
|
157
|
+
expect(transaction_hash["action"]).to eq("Sidekiq::Extensions::DelayedClass#perform")
|
158
|
+
expect(transaction_hash["sample_data"]).to include("params" => [])
|
159
|
+
expect(log_contents(log)).to contains_log(:warn, "Unable to load YAML")
|
125
160
|
end
|
161
|
+
end
|
162
|
+
end
|
126
163
|
|
127
|
-
|
128
|
-
|
164
|
+
context "when using the Sidekiq ActiveRecord instance delayed extension" do
|
165
|
+
let(:item) do
|
166
|
+
{
|
167
|
+
"jid" => "efb140489485999d32b5504c",
|
168
|
+
"class" => "Sidekiq::Extensions::DelayedModel",
|
169
|
+
"queue" => "default",
|
170
|
+
"args" => [
|
171
|
+
"---\n- !ruby/object:DelayedTestClass {}\n- :foo_method\n- - :bar: :baz\n"
|
172
|
+
],
|
173
|
+
"retry" => true,
|
174
|
+
"created_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
|
175
|
+
"enqueued_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
|
176
|
+
"extra" => "data"
|
177
|
+
}
|
178
|
+
end
|
129
179
|
|
130
|
-
|
131
|
-
|
180
|
+
it "uses the delayed class and method name for the action" do
|
181
|
+
perform_job
|
132
182
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
end
|
183
|
+
transaction_hash = transaction.to_h
|
184
|
+
expect(transaction_hash["action"]).to eq("DelayedTestClass#foo_method")
|
185
|
+
expect(transaction_hash["sample_data"]).to include(
|
186
|
+
"params" => ["bar" => "baz"]
|
187
|
+
)
|
139
188
|
end
|
140
189
|
|
141
|
-
context "when
|
142
|
-
|
143
|
-
{
|
144
|
-
"jid" => "efb140489485999d32b5504c",
|
145
|
-
"class" => "Sidekiq::Extensions::DelayedModel",
|
146
|
-
"queue" => "default",
|
147
|
-
"args" => [
|
148
|
-
"---\n- !ruby/object:DelayedTestClass {}\n- :foo_method\n- - :bar: :baz\n"
|
149
|
-
],
|
150
|
-
"retry" => true,
|
151
|
-
"created_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
|
152
|
-
"enqueued_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
|
153
|
-
"extra" => "data"
|
154
|
-
}
|
155
|
-
end
|
190
|
+
context "when job arguments is a malformed YAML object", :with_yaml_parse_error => true do
|
191
|
+
before { item["args"] = [] }
|
156
192
|
|
157
|
-
it "
|
193
|
+
it "logs a warning and uses the default argument" do
|
158
194
|
perform_job
|
159
195
|
|
160
196
|
transaction_hash = transaction.to_h
|
161
|
-
expect(transaction_hash["action"]).to eq("
|
162
|
-
expect(transaction_hash["sample_data"]).to include(
|
163
|
-
|
164
|
-
)
|
197
|
+
expect(transaction_hash["action"]).to eq("Sidekiq::Extensions::DelayedModel#perform")
|
198
|
+
expect(transaction_hash["sample_data"]).to include("params" => [])
|
199
|
+
expect(log_contents(log)).to contains_log(:warn, "Unable to load YAML")
|
165
200
|
end
|
201
|
+
end
|
202
|
+
end
|
166
203
|
|
167
|
-
|
168
|
-
|
204
|
+
context "when using ActiveJob" do
|
205
|
+
let(:item) do
|
206
|
+
{
|
207
|
+
"class" => "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
|
208
|
+
"wrapped" => "ActiveJobTestClass",
|
209
|
+
"queue" => "default",
|
210
|
+
"args" => [{
|
211
|
+
"job_class" => "ActiveJobTestJob",
|
212
|
+
"job_id" => "23e79d48-6966-40d0-b2d4-f7938463a263",
|
213
|
+
"queue_name" => "default",
|
214
|
+
"arguments" => [
|
215
|
+
"foo", { "foo" => "Foo", "bar" => "Bar", "baz" => { 1 => :bar } }
|
216
|
+
]
|
217
|
+
}],
|
218
|
+
"retry" => true,
|
219
|
+
"jid" => "efb140489485999d32b5504c",
|
220
|
+
"created_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
|
221
|
+
"enqueued_at" => Time.parse("2001-01-01 10:00:00UTC").to_f
|
222
|
+
}
|
223
|
+
end
|
169
224
|
|
170
|
-
|
171
|
-
|
225
|
+
it "creates a transaction with events" do
|
226
|
+
perform_job
|
172
227
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
228
|
+
transaction_hash = transaction.to_h
|
229
|
+
expect(transaction_hash).to include(
|
230
|
+
"id" => kind_of(String),
|
231
|
+
"action" => "ActiveJobTestClass#perform",
|
232
|
+
"error" => nil,
|
233
|
+
"namespace" => namespace,
|
234
|
+
"metadata" => {
|
235
|
+
"queue" => "default"
|
236
|
+
},
|
237
|
+
"sample_data" => {
|
238
|
+
"environment" => {},
|
239
|
+
"params" => [
|
240
|
+
"foo",
|
241
|
+
{
|
242
|
+
"foo" => "Foo",
|
243
|
+
"bar" => "Bar",
|
244
|
+
"baz" => { "1" => "bar" }
|
245
|
+
}
|
246
|
+
],
|
247
|
+
"tags" => {}
|
248
|
+
}
|
249
|
+
)
|
250
|
+
# TODO: Not available in transaction.to_h yet.
|
251
|
+
# https://github.com/appsignal/appsignal-agent/issues/293
|
252
|
+
expect(transaction.request.env).to eq(
|
253
|
+
:queue_start => Time.parse("2001-01-01 10:00:00UTC").to_f
|
254
|
+
)
|
255
|
+
expect_transaction_to_have_sidekiq_event(transaction_hash)
|
179
256
|
end
|
180
257
|
|
181
|
-
context "
|
258
|
+
context "with ActionMailer job" do
|
182
259
|
let(:item) do
|
183
260
|
{
|
184
261
|
"class" => "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
|
185
|
-
"wrapped" => "
|
262
|
+
"wrapped" => "ActionMailer::DeliveryJob",
|
186
263
|
"queue" => "default",
|
187
264
|
"args" => [{
|
188
|
-
"job_class" => "
|
265
|
+
"job_class" => "ActiveMailerTestJob",
|
189
266
|
"job_id" => "23e79d48-6966-40d0-b2d4-f7938463a263",
|
190
267
|
"queue_name" => "default",
|
191
268
|
"arguments" => [
|
269
|
+
"MailerClass", "mailer_method", "deliver_now",
|
192
270
|
"foo", { "foo" => "Foo", "bar" => "Bar", "baz" => { 1 => :bar } }
|
193
271
|
]
|
194
272
|
}],
|
@@ -199,13 +277,13 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
|
|
199
277
|
}
|
200
278
|
end
|
201
279
|
|
202
|
-
it "creates a transaction
|
280
|
+
it "creates a transaction for the ActionMailer class" do
|
203
281
|
perform_job
|
204
282
|
|
205
283
|
transaction_hash = transaction.to_h
|
206
284
|
expect(transaction_hash).to include(
|
207
285
|
"id" => kind_of(String),
|
208
|
-
"action" => "
|
286
|
+
"action" => "MailerClass#mailer_method",
|
209
287
|
"error" => nil,
|
210
288
|
"namespace" => namespace,
|
211
289
|
"metadata" => {
|
@@ -224,43 +302,87 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
|
|
224
302
|
"tags" => {}
|
225
303
|
}
|
226
304
|
)
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
context "with parameter filtering" do
|
309
|
+
before do
|
310
|
+
Appsignal.config = project_fixture_config("production")
|
311
|
+
Appsignal.config[:filter_parameters] = ["foo"]
|
312
|
+
end
|
313
|
+
|
314
|
+
it "filters selected arguments" do
|
315
|
+
perform_job
|
316
|
+
|
317
|
+
transaction_hash = transaction.to_h
|
318
|
+
expect(transaction_hash["sample_data"]).to include(
|
319
|
+
"params" => [
|
320
|
+
"foo",
|
321
|
+
{
|
322
|
+
"foo" => "[FILTERED]",
|
323
|
+
"bar" => "Bar",
|
324
|
+
"baz" => { "1" => "bar" }
|
325
|
+
}
|
326
|
+
]
|
231
327
|
)
|
232
|
-
expect_transaction_to_have_sidekiq_event(transaction_hash)
|
233
328
|
end
|
329
|
+
end
|
234
330
|
|
235
|
-
|
236
|
-
|
331
|
+
context "when Sidekiq job payload is missing the 'wrapped' value" do
|
332
|
+
let(:item) do
|
333
|
+
{
|
334
|
+
"class" => "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
|
335
|
+
"queue" => "default",
|
336
|
+
"args" => [first_argument],
|
337
|
+
"retry" => true,
|
338
|
+
"jid" => "efb140489485999d32b5504c",
|
339
|
+
"created_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
|
340
|
+
"enqueued_at" => Time.parse("2001-01-01 10:00:00UTC").to_f
|
341
|
+
}
|
342
|
+
end
|
343
|
+
before { perform_job }
|
344
|
+
|
345
|
+
context "when the first argument is not a Hash object" do
|
346
|
+
let(:first_argument) { "foo" }
|
347
|
+
|
348
|
+
include_examples "unknown job action name"
|
349
|
+
end
|
350
|
+
|
351
|
+
context "when the first argument is a Hash object not containing a job payload" do
|
352
|
+
let(:first_argument) { { "foo" => "bar" } }
|
353
|
+
|
354
|
+
include_examples "unknown job action name"
|
355
|
+
|
356
|
+
context "when the argument contains an invalid job_class value" do
|
357
|
+
let(:first_argument) { { "job_class" => :foo } }
|
358
|
+
|
359
|
+
include_examples "unknown job action name"
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
context "when the first argument is a Hash object containing a job payload" do
|
364
|
+
let(:first_argument) do
|
237
365
|
{
|
238
|
-
"
|
239
|
-
"
|
240
|
-
"
|
241
|
-
"
|
242
|
-
"
|
243
|
-
|
244
|
-
"queue_name" => "default",
|
245
|
-
"arguments" => [
|
246
|
-
"MailerClass", "mailer_method", "deliver_now",
|
247
|
-
"foo", { "foo" => "Foo", "bar" => "Bar", "baz" => { 1 => :bar } }
|
248
|
-
]
|
249
|
-
}],
|
250
|
-
"retry" => true,
|
251
|
-
"jid" => "efb140489485999d32b5504c",
|
252
|
-
"created_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
|
253
|
-
"enqueued_at" => Time.parse("2001-01-01 10:00:00UTC").to_f
|
366
|
+
"job_class" => "ActiveMailerTestJob",
|
367
|
+
"job_id" => "23e79d48-6966-40d0-b2d4-f7938463a263",
|
368
|
+
"queue_name" => "default",
|
369
|
+
"arguments" => [
|
370
|
+
"foo", { "foo" => "Foo", "bar" => "Bar", "baz" => { 1 => :bar } }
|
371
|
+
]
|
254
372
|
}
|
255
373
|
end
|
256
374
|
|
257
|
-
it "
|
258
|
-
|
375
|
+
it "sets the action name to the job class in the first argument" do
|
376
|
+
transaction_hash = transaction.to_h
|
377
|
+
expect(transaction_hash).to include(
|
378
|
+
"action" => "ActiveMailerTestJob#perform"
|
379
|
+
)
|
380
|
+
end
|
259
381
|
|
382
|
+
it "stores the job metadata on the transaction" do
|
260
383
|
transaction_hash = transaction.to_h
|
261
384
|
expect(transaction_hash).to include(
|
262
385
|
"id" => kind_of(String),
|
263
|
-
"action" => "MailerClass#mailer_method",
|
264
386
|
"error" => nil,
|
265
387
|
"namespace" => namespace,
|
266
388
|
"metadata" => {
|
@@ -280,137 +402,13 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
|
|
280
402
|
}
|
281
403
|
)
|
282
404
|
end
|
283
|
-
end
|
284
|
-
|
285
|
-
context "with parameter filtering" do
|
286
|
-
before do
|
287
|
-
Appsignal.config = project_fixture_config("production")
|
288
|
-
Appsignal.config[:filter_parameters] = ["foo"]
|
289
|
-
end
|
290
|
-
|
291
|
-
it "filters selected arguments" do
|
292
|
-
perform_job
|
293
405
|
|
294
|
-
|
295
|
-
expect(
|
296
|
-
"
|
297
|
-
"foo",
|
298
|
-
{
|
299
|
-
"foo" => "[FILTERED]",
|
300
|
-
"bar" => "Bar",
|
301
|
-
"baz" => { "1" => "bar" }
|
302
|
-
}
|
303
|
-
]
|
406
|
+
it "does not log a debug message" do
|
407
|
+
expect(log_contents(log)).to_not contains_log(
|
408
|
+
:debug, "Unable to determine an action name from Sidekiq payload"
|
304
409
|
)
|
305
410
|
end
|
306
411
|
end
|
307
|
-
|
308
|
-
context "when Sidekiq job payload is missing the 'wrapped' value" do
|
309
|
-
let(:item) do
|
310
|
-
{
|
311
|
-
"class" => "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
|
312
|
-
"queue" => "default",
|
313
|
-
"args" => [first_argument],
|
314
|
-
"retry" => true,
|
315
|
-
"jid" => "efb140489485999d32b5504c",
|
316
|
-
"created_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
|
317
|
-
"enqueued_at" => Time.parse("2001-01-01 10:00:00UTC").to_f
|
318
|
-
}
|
319
|
-
end
|
320
|
-
before { perform_job }
|
321
|
-
|
322
|
-
context "when the first argument is not a Hash object" do
|
323
|
-
let(:first_argument) { "foo" }
|
324
|
-
|
325
|
-
include_examples "unknown job action name"
|
326
|
-
end
|
327
|
-
|
328
|
-
context "when the first argument is a Hash object not containing a job payload" do
|
329
|
-
let(:first_argument) { { "foo" => "bar" } }
|
330
|
-
|
331
|
-
include_examples "unknown job action name"
|
332
|
-
|
333
|
-
context "when the argument contains an invalid job_class value" do
|
334
|
-
let(:first_argument) { { "job_class" => :foo } }
|
335
|
-
|
336
|
-
include_examples "unknown job action name"
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
context "when the first argument is a Hash object containing a job payload" do
|
341
|
-
let(:first_argument) do
|
342
|
-
{
|
343
|
-
"job_class" => "ActiveMailerTestJob",
|
344
|
-
"job_id" => "23e79d48-6966-40d0-b2d4-f7938463a263",
|
345
|
-
"queue_name" => "default",
|
346
|
-
"arguments" => [
|
347
|
-
"foo", { "foo" => "Foo", "bar" => "Bar", "baz" => { 1 => :bar } }
|
348
|
-
]
|
349
|
-
}
|
350
|
-
end
|
351
|
-
|
352
|
-
it "sets the action name to the job class in the first argument" do
|
353
|
-
transaction_hash = transaction.to_h
|
354
|
-
expect(transaction_hash).to include(
|
355
|
-
"action" => "ActiveMailerTestJob#perform"
|
356
|
-
)
|
357
|
-
end
|
358
|
-
|
359
|
-
it "stores the job metadata on the transaction" do
|
360
|
-
transaction_hash = transaction.to_h
|
361
|
-
expect(transaction_hash).to include(
|
362
|
-
"id" => kind_of(String),
|
363
|
-
"error" => nil,
|
364
|
-
"namespace" => namespace,
|
365
|
-
"metadata" => {
|
366
|
-
"queue" => "default"
|
367
|
-
},
|
368
|
-
"sample_data" => {
|
369
|
-
"environment" => {},
|
370
|
-
"params" => [
|
371
|
-
"foo",
|
372
|
-
{
|
373
|
-
"foo" => "Foo",
|
374
|
-
"bar" => "Bar",
|
375
|
-
"baz" => { "1" => "bar" }
|
376
|
-
}
|
377
|
-
],
|
378
|
-
"tags" => {}
|
379
|
-
}
|
380
|
-
)
|
381
|
-
end
|
382
|
-
|
383
|
-
it "does not log a debug message" do
|
384
|
-
expect(log_contents(log)).to_not contains_log(
|
385
|
-
:debug, "Unable to determine an action name from Sidekiq payload"
|
386
|
-
)
|
387
|
-
end
|
388
|
-
end
|
389
|
-
end
|
390
|
-
end
|
391
|
-
end
|
392
|
-
|
393
|
-
shared_examples "unknown job action name" do
|
394
|
-
it "sets the action name to unknown" do
|
395
|
-
transaction_hash = transaction.to_h
|
396
|
-
expect(transaction_hash).to include("action" => "unknown")
|
397
|
-
end
|
398
|
-
|
399
|
-
it "stores no sample data" do
|
400
|
-
transaction_hash = transaction.to_h
|
401
|
-
expect(transaction_hash).to include(
|
402
|
-
"sample_data" => {
|
403
|
-
"environment" => {},
|
404
|
-
"params" => [],
|
405
|
-
"tags" => {}
|
406
|
-
}
|
407
|
-
)
|
408
|
-
end
|
409
|
-
|
410
|
-
it "logs a debug message" do
|
411
|
-
expect(log_contents(log)).to contains_log(
|
412
|
-
:debug, "Unable to determine an action name from Sidekiq payload: #{item}"
|
413
|
-
)
|
414
412
|
end
|
415
413
|
end
|
416
414
|
|
@@ -452,8 +450,6 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
|
|
452
450
|
)
|
453
451
|
expect_transaction_to_have_sidekiq_event(transaction_hash)
|
454
452
|
end
|
455
|
-
|
456
|
-
include_examples "sidekiq metadata"
|
457
453
|
end
|
458
454
|
|
459
455
|
context "without an error" do
|
@@ -487,8 +483,6 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
|
|
487
483
|
)
|
488
484
|
expect_transaction_to_have_sidekiq_event(transaction_hash)
|
489
485
|
end
|
490
|
-
|
491
|
-
include_examples "sidekiq metadata"
|
492
486
|
end
|
493
487
|
|
494
488
|
def perform_job
|
@@ -537,7 +531,7 @@ describe Appsignal::Hooks::SidekiqHook do
|
|
537
531
|
describe "#install" do
|
538
532
|
before do
|
539
533
|
Appsignal.config = project_fixture_config
|
540
|
-
|
534
|
+
module Sidekiq
|
541
535
|
def self.middlewares
|
542
536
|
@middlewares ||= Set.new
|
543
537
|
end
|
@@ -560,203 +554,3 @@ describe Appsignal::Hooks::SidekiqHook do
|
|
560
554
|
end
|
561
555
|
end
|
562
556
|
end
|
563
|
-
|
564
|
-
describe Appsignal::Hooks::SidekiqProbe do
|
565
|
-
describe "#call" do
|
566
|
-
let(:probe) { described_class.new }
|
567
|
-
let(:redis_hostname) { "localhost" }
|
568
|
-
let(:expected_default_tags) { { :hostname => "localhost" } }
|
569
|
-
before do
|
570
|
-
Appsignal.config = project_fixture_config
|
571
|
-
class Sidekiq
|
572
|
-
def self.redis_info
|
573
|
-
{
|
574
|
-
"connected_clients" => 2,
|
575
|
-
"used_memory" => 1024,
|
576
|
-
"used_memory_rss" => 512
|
577
|
-
}
|
578
|
-
end
|
579
|
-
|
580
|
-
def self.redis
|
581
|
-
yield Client.new
|
582
|
-
end
|
583
|
-
|
584
|
-
class Client
|
585
|
-
def connection
|
586
|
-
{ :host => "localhost" }
|
587
|
-
end
|
588
|
-
end
|
589
|
-
|
590
|
-
class Stats
|
591
|
-
class << self
|
592
|
-
attr_reader :calls
|
593
|
-
|
594
|
-
def count_call
|
595
|
-
@calls ||= -1
|
596
|
-
@calls += 1
|
597
|
-
end
|
598
|
-
end
|
599
|
-
|
600
|
-
def workers_size
|
601
|
-
# First method called, so count it towards a call
|
602
|
-
self.class.count_call
|
603
|
-
24
|
604
|
-
end
|
605
|
-
|
606
|
-
def processes_size
|
607
|
-
25
|
608
|
-
end
|
609
|
-
|
610
|
-
# Return two different values for two separate calls.
|
611
|
-
# This allows us to test the delta of the value send as a gauge.
|
612
|
-
def processed
|
613
|
-
[10, 15][self.class.calls]
|
614
|
-
end
|
615
|
-
|
616
|
-
# Return two different values for two separate calls.
|
617
|
-
# This allows us to test the delta of the value send as a gauge.
|
618
|
-
def failed
|
619
|
-
[10, 13][self.class.calls]
|
620
|
-
end
|
621
|
-
|
622
|
-
def retry_size
|
623
|
-
12
|
624
|
-
end
|
625
|
-
|
626
|
-
# Return two different values for two separate calls.
|
627
|
-
# This allows us to test the delta of the value send as a gauge.
|
628
|
-
def dead_size
|
629
|
-
[10, 12][self.class.calls]
|
630
|
-
end
|
631
|
-
|
632
|
-
def scheduled_size
|
633
|
-
14
|
634
|
-
end
|
635
|
-
|
636
|
-
def enqueued
|
637
|
-
15
|
638
|
-
end
|
639
|
-
end
|
640
|
-
|
641
|
-
class Queue
|
642
|
-
Queue = Struct.new(:name, :size, :latency)
|
643
|
-
|
644
|
-
def self.all
|
645
|
-
[
|
646
|
-
Queue.new("default", 10, 12),
|
647
|
-
Queue.new("critical", 1, 2)
|
648
|
-
]
|
649
|
-
end
|
650
|
-
end
|
651
|
-
end
|
652
|
-
end
|
653
|
-
after { Object.send(:remove_const, "Sidekiq") }
|
654
|
-
|
655
|
-
describe ".dependencies_present?" do
|
656
|
-
before do
|
657
|
-
class Redis; end
|
658
|
-
Redis.const_set(:VERSION, version)
|
659
|
-
end
|
660
|
-
after { Object.send(:remove_const, "Redis") }
|
661
|
-
|
662
|
-
context "when Redis version is < 3.3.5" do
|
663
|
-
let(:version) { "3.3.4" }
|
664
|
-
|
665
|
-
it "does not start probe" do
|
666
|
-
expect(described_class.dependencies_present?).to be_falsy
|
667
|
-
end
|
668
|
-
end
|
669
|
-
|
670
|
-
context "when Redis version is >= 3.3.5" do
|
671
|
-
let(:version) { "3.3.5" }
|
672
|
-
|
673
|
-
it "does not start probe" do
|
674
|
-
expect(described_class.dependencies_present?).to be_truthy
|
675
|
-
end
|
676
|
-
end
|
677
|
-
end
|
678
|
-
|
679
|
-
it "loads Sidekiq::API" do
|
680
|
-
expect(defined?(Sidekiq::API)).to be_falsy
|
681
|
-
probe
|
682
|
-
expect(defined?(Sidekiq::API)).to be_truthy
|
683
|
-
end
|
684
|
-
|
685
|
-
it "logs config on initialize" do
|
686
|
-
log = capture_logs { probe }
|
687
|
-
expect(log).to contains_log(:debug, "Initializing Sidekiq probe\n")
|
688
|
-
end
|
689
|
-
|
690
|
-
it "logs used hostname on call once" do
|
691
|
-
log = capture_logs { probe.call }
|
692
|
-
expect(log).to contains_log(
|
693
|
-
:debug,
|
694
|
-
%(Sidekiq probe: Using Redis server hostname "localhost" as hostname)
|
695
|
-
)
|
696
|
-
log = capture_logs { probe.call }
|
697
|
-
# Match more logs with incompelete message
|
698
|
-
expect(log).to_not contains_log(:debug, %(Sidekiq probe: ))
|
699
|
-
end
|
700
|
-
|
701
|
-
it "collects custom metrics" do
|
702
|
-
expect_gauge("worker_count", 24).twice
|
703
|
-
expect_gauge("process_count", 25).twice
|
704
|
-
expect_gauge("connection_count", 2).twice
|
705
|
-
expect_gauge("memory_usage", 1024).twice
|
706
|
-
expect_gauge("memory_usage_rss", 512).twice
|
707
|
-
expect_gauge("job_count", 5, :status => :processed) # Gauge delta
|
708
|
-
expect_gauge("job_count", 3, :status => :failed) # Gauge delta
|
709
|
-
expect_gauge("job_count", 12, :status => :retry_queue).twice
|
710
|
-
expect_gauge("job_count", 2, :status => :died) # Gauge delta
|
711
|
-
expect_gauge("job_count", 14, :status => :scheduled).twice
|
712
|
-
expect_gauge("job_count", 15, :status => :enqueued).twice
|
713
|
-
expect_gauge("queue_length", 10, :queue => "default").twice
|
714
|
-
expect_gauge("queue_latency", 12_000, :queue => "default").twice
|
715
|
-
expect_gauge("queue_length", 1, :queue => "critical").twice
|
716
|
-
expect_gauge("queue_latency", 2_000, :queue => "critical").twice
|
717
|
-
# Call probe twice so we can calculate the delta for some gauge values
|
718
|
-
probe.call
|
719
|
-
probe.call
|
720
|
-
end
|
721
|
-
|
722
|
-
context "when `redis_info` is not defined" do
|
723
|
-
before do
|
724
|
-
allow(Sidekiq).to receive(:respond_to?).with(:redis_info).and_return(false)
|
725
|
-
end
|
726
|
-
|
727
|
-
it "does not collect redis metrics" do
|
728
|
-
expect_gauge("connection_count", 2).never
|
729
|
-
expect_gauge("memory_usage", 1024).never
|
730
|
-
expect_gauge("memory_usage_rss", 512).never
|
731
|
-
probe.call
|
732
|
-
end
|
733
|
-
end
|
734
|
-
|
735
|
-
context "when hostname is configured for probe" do
|
736
|
-
let(:redis_hostname) { "my_redis_server" }
|
737
|
-
let(:probe) { described_class.new(:hostname => redis_hostname) }
|
738
|
-
|
739
|
-
it "uses the redis hostname for the hostname tag" do
|
740
|
-
allow(Appsignal).to receive(:set_gauge).and_call_original
|
741
|
-
log = capture_logs { probe }
|
742
|
-
expect(log).to contains_log(
|
743
|
-
:debug,
|
744
|
-
%(Initializing Sidekiq probe with config: {:hostname=>"#{redis_hostname}"})
|
745
|
-
)
|
746
|
-
log = capture_logs { probe.call }
|
747
|
-
expect(log).to contains_log(
|
748
|
-
:debug,
|
749
|
-
"Sidekiq probe: Using hostname config option #{redis_hostname.inspect} as hostname"
|
750
|
-
)
|
751
|
-
expect(Appsignal).to have_received(:set_gauge)
|
752
|
-
.with(anything, anything, :hostname => redis_hostname).at_least(:once)
|
753
|
-
end
|
754
|
-
end
|
755
|
-
|
756
|
-
def expect_gauge(key, value, tags = {})
|
757
|
-
expect(Appsignal).to receive(:set_gauge)
|
758
|
-
.with("sidekiq_#{key}", value, expected_default_tags.merge(tags))
|
759
|
-
.and_call_original
|
760
|
-
end
|
761
|
-
end
|
762
|
-
end
|