appsignal 2.11.0.alpha.2-java → 2.11.0.beta.5-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 +3 -0
- data/.semaphore/semaphore.yml +94 -10
- data/CHANGELOG.md +31 -1
- data/README.md +4 -4
- data/Rakefile +16 -4
- data/appsignal.gemspec +1 -1
- data/build_matrix.yml +7 -3
- data/ext/Rakefile +2 -0
- data/ext/agent.yml +19 -19
- data/ext/base.rb +7 -0
- data/ext/extconf.rb +2 -0
- data/gemfiles/rails-4.2.gemfile +9 -2
- data/gemfiles/rails-5.0.gemfile +1 -0
- data/gemfiles/rails-5.1.gemfile +1 -0
- data/gemfiles/rails-5.2.gemfile +1 -0
- data/gemfiles/rails-6.0.gemfile +1 -0
- data/gemfiles/resque-1.gemfile +7 -0
- data/gemfiles/{resque.gemfile → resque-2.gemfile} +1 -1
- data/lib/appsignal.rb +1 -0
- data/lib/appsignal/auth_check.rb +4 -2
- data/lib/appsignal/cli/diagnose.rb +1 -1
- data/lib/appsignal/config.rb +35 -2
- data/lib/appsignal/extension.rb +6 -5
- data/lib/appsignal/extension/jruby.rb +6 -5
- data/lib/appsignal/hooks.rb +25 -0
- data/lib/appsignal/hooks/active_job.rb +137 -0
- data/lib/appsignal/hooks/puma.rb +0 -1
- data/lib/appsignal/hooks/resque.rb +60 -0
- data/lib/appsignal/hooks/sidekiq.rb +17 -94
- data/lib/appsignal/integrations/delayed_job_plugin.rb +1 -1
- data/lib/appsignal/integrations/que.rb +1 -1
- data/lib/appsignal/integrations/resque.rb +9 -12
- data/lib/appsignal/integrations/resque_active_job.rb +9 -32
- data/lib/appsignal/probes.rb +7 -0
- data/lib/appsignal/probes/puma.rb +1 -1
- data/lib/appsignal/probes/sidekiq.rb +3 -1
- data/lib/appsignal/transaction.rb +10 -0
- data/lib/appsignal/utils/deprecation_message.rb +6 -2
- data/lib/appsignal/version.rb +1 -1
- data/spec/lib/appsignal/auth_check_spec.rb +23 -0
- data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
- data/spec/lib/appsignal/capistrano3_spec.rb +1 -1
- data/spec/lib/appsignal/cli/diagnose_spec.rb +42 -0
- data/spec/lib/appsignal/config_spec.rb +21 -0
- data/spec/lib/appsignal/extension/jruby_spec.rb +31 -28
- data/spec/lib/appsignal/extension_install_failure_spec.rb +23 -0
- data/spec/lib/appsignal/hooks/activejob_spec.rb +591 -0
- data/spec/lib/appsignal/hooks/delayed_job_spec.rb +3 -14
- data/spec/lib/appsignal/hooks/resque_spec.rb +185 -0
- data/spec/lib/appsignal/hooks/sidekiq_spec.rb +222 -268
- data/spec/lib/appsignal/hooks_spec.rb +57 -0
- data/spec/lib/appsignal/integrations/que_spec.rb +25 -6
- data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +20 -179
- data/spec/lib/appsignal/integrations/resque_spec.rb +20 -85
- data/spec/lib/appsignal/marker_spec.rb +1 -1
- data/spec/lib/appsignal/probes/sidekiq_spec.rb +10 -7
- data/spec/lib/appsignal/transaction_spec.rb +5 -7
- data/spec/spec_helper.rb +5 -0
- data/spec/support/helpers/action_mailer_helpers.rb +25 -0
- data/spec/support/helpers/config_helpers.rb +3 -2
- data/spec/support/helpers/dependency_helper.rb +9 -2
- data/spec/support/helpers/transaction_helpers.rb +6 -0
- data/spec/support/stubs/sidekiq/api.rb +1 -1
- data/spec/support/testing.rb +19 -19
- metadata +16 -4
@@ -227,25 +227,14 @@ describe Appsignal::Hooks::DelayedJobHook do
|
|
227
227
|
end
|
228
228
|
end
|
229
229
|
|
230
|
-
context "
|
231
|
-
let(:job_data) do
|
232
|
-
{ :name => "", :payload_object => payload_object }
|
233
|
-
end
|
234
|
-
|
235
|
-
it "wraps it in a transaction using the class method job name" do
|
236
|
-
perform
|
237
|
-
expect(last_transaction.to_h["action"]).to eql("unknown")
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
context "with invalid job name" do
|
230
|
+
context "with only job class name" do
|
242
231
|
let(:job_data) do
|
243
232
|
{ :name => "Banana", :payload_object => payload_object }
|
244
233
|
end
|
245
234
|
|
246
|
-
it "
|
235
|
+
it "appends #perform to the class name" do
|
247
236
|
perform
|
248
|
-
expect(last_transaction.to_h["action"]).to eql("
|
237
|
+
expect(last_transaction.to_h["action"]).to eql("Banana#perform")
|
249
238
|
end
|
250
239
|
end
|
251
240
|
|
@@ -0,0 +1,185 @@
|
|
1
|
+
describe Appsignal::Hooks::ResqueHook do
|
2
|
+
describe "#dependency_present?" do
|
3
|
+
subject { described_class.new.dependencies_present? }
|
4
|
+
|
5
|
+
context "when Resque is loaded" do
|
6
|
+
before { stub_const "Resque", 1 }
|
7
|
+
|
8
|
+
it { is_expected.to be_truthy }
|
9
|
+
end
|
10
|
+
|
11
|
+
context "when Resque is not loaded" do
|
12
|
+
before { hide_const "Resque" }
|
13
|
+
|
14
|
+
it { is_expected.to be_falsy }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
if DependencyHelper.resque_present?
|
19
|
+
describe "#install" do
|
20
|
+
def perform_job(klass, options = {})
|
21
|
+
payload = { "class" => klass.to_s }.merge(options)
|
22
|
+
job = ::Resque::Job.new(queue, payload)
|
23
|
+
keep_transactions { job.perform }
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:queue) { "default" }
|
27
|
+
let(:namespace) { Appsignal::Transaction::BACKGROUND_JOB }
|
28
|
+
before do
|
29
|
+
start_agent
|
30
|
+
|
31
|
+
class ResqueTestJob
|
32
|
+
def self.perform(*_args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class ResqueErrorTestJob
|
37
|
+
def self.perform
|
38
|
+
raise "resque job error"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
expect(Appsignal).to receive(:stop) # Resque calls stop after every job
|
43
|
+
end
|
44
|
+
around do |example|
|
45
|
+
keep_transactions { example.run }
|
46
|
+
end
|
47
|
+
after do
|
48
|
+
Object.send(:remove_const, :ResqueTestJob)
|
49
|
+
Object.send(:remove_const, :ResqueErrorTestJob)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "tracks a transaction on perform" do
|
53
|
+
perform_job(ResqueTestJob)
|
54
|
+
|
55
|
+
transaction = last_transaction
|
56
|
+
transaction_hash = transaction.to_h
|
57
|
+
expect(transaction_hash).to include(
|
58
|
+
"id" => kind_of(String),
|
59
|
+
"action" => "ResqueTestJob#perform",
|
60
|
+
"error" => nil,
|
61
|
+
"namespace" => namespace,
|
62
|
+
"metadata" => {},
|
63
|
+
"sample_data" => { "tags" => { "queue" => queue } }
|
64
|
+
)
|
65
|
+
expect(transaction_hash["events"].map { |e| e["name"] })
|
66
|
+
.to eql(["perform.resque"])
|
67
|
+
end
|
68
|
+
|
69
|
+
context "with error" do
|
70
|
+
it "tracks the error on the transaction" do
|
71
|
+
expect do
|
72
|
+
perform_job(ResqueErrorTestJob)
|
73
|
+
end.to raise_error(RuntimeError, "resque job error")
|
74
|
+
|
75
|
+
transaction = last_transaction
|
76
|
+
transaction_hash = transaction.to_h
|
77
|
+
expect(transaction_hash).to include(
|
78
|
+
"id" => kind_of(String),
|
79
|
+
"action" => "ResqueErrorTestJob#perform",
|
80
|
+
"error" => {
|
81
|
+
"name" => "RuntimeError",
|
82
|
+
"message" => "resque job error",
|
83
|
+
"backtrace" => kind_of(String)
|
84
|
+
},
|
85
|
+
"namespace" => namespace,
|
86
|
+
"metadata" => {},
|
87
|
+
"sample_data" => { "tags" => { "queue" => queue } }
|
88
|
+
)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "with arguments" do
|
93
|
+
before do
|
94
|
+
Appsignal.config = project_fixture_config("production")
|
95
|
+
Appsignal.config[:filter_parameters] = ["foo"]
|
96
|
+
end
|
97
|
+
|
98
|
+
it "filters out configured arguments" do
|
99
|
+
perform_job(
|
100
|
+
ResqueTestJob,
|
101
|
+
"args" => [
|
102
|
+
"foo",
|
103
|
+
{
|
104
|
+
"foo" => "secret",
|
105
|
+
"bar" => "Bar",
|
106
|
+
"baz" => { "1" => "foo" }
|
107
|
+
}
|
108
|
+
]
|
109
|
+
)
|
110
|
+
|
111
|
+
transaction = last_transaction
|
112
|
+
transaction_hash = transaction.to_h
|
113
|
+
expect(transaction_hash).to include(
|
114
|
+
"id" => kind_of(String),
|
115
|
+
"action" => "ResqueTestJob#perform",
|
116
|
+
"error" => nil,
|
117
|
+
"namespace" => namespace,
|
118
|
+
"metadata" => {},
|
119
|
+
"sample_data" => {
|
120
|
+
"tags" => { "queue" => queue },
|
121
|
+
"params" => [
|
122
|
+
"foo",
|
123
|
+
{
|
124
|
+
"foo" => "[FILTERED]",
|
125
|
+
"bar" => "Bar",
|
126
|
+
"baz" => { "1" => "foo" }
|
127
|
+
}
|
128
|
+
]
|
129
|
+
}
|
130
|
+
)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context "with active job" do
|
135
|
+
before do
|
136
|
+
module ActiveJobMock
|
137
|
+
module QueueAdapters
|
138
|
+
module ResqueAdapter
|
139
|
+
module JobWrapper
|
140
|
+
class << self
|
141
|
+
def perform(job_data)
|
142
|
+
# Basic ActiveJob stub for this test.
|
143
|
+
# I haven't found a way to run Resque in a testing mode.
|
144
|
+
Appsignal.set_action(job_data["job_class"])
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
stub_const "ActiveJob", ActiveJobMock
|
153
|
+
end
|
154
|
+
after { Object.send(:remove_const, :ActiveJobMock) }
|
155
|
+
|
156
|
+
it "does not set arguments but lets the ActiveJob intergration handle it" do
|
157
|
+
perform_job(
|
158
|
+
ResqueTestJob,
|
159
|
+
"class" => "ActiveJob::QueueAdapters::ResqueAdapter::JobWrapper",
|
160
|
+
"args" => [
|
161
|
+
{
|
162
|
+
"job_class" => "ResqueTestJobByActiveJob#perform",
|
163
|
+
"arguments" => ["an argument", "second argument"]
|
164
|
+
}
|
165
|
+
]
|
166
|
+
)
|
167
|
+
|
168
|
+
transaction = last_transaction
|
169
|
+
transaction_hash = transaction.to_h
|
170
|
+
expect(transaction_hash).to include(
|
171
|
+
"id" => kind_of(String),
|
172
|
+
"action" => "ResqueTestJobByActiveJob#perform",
|
173
|
+
"error" => nil,
|
174
|
+
"namespace" => namespace,
|
175
|
+
"metadata" => {},
|
176
|
+
"sample_data" => {
|
177
|
+
"tags" => { "queue" => queue }
|
178
|
+
# Params will be set by the ActiveJob integration
|
179
|
+
}
|
180
|
+
)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -1,3 +1,54 @@
|
|
1
|
+
describe Appsignal::Hooks::SidekiqHook do
|
2
|
+
describe "#dependencies_present?" do
|
3
|
+
subject { described_class.new.dependencies_present? }
|
4
|
+
|
5
|
+
context "when Sidekiq constant is found" do
|
6
|
+
before { stub_const "Sidekiq", Class.new }
|
7
|
+
|
8
|
+
it { is_expected.to be_truthy }
|
9
|
+
end
|
10
|
+
|
11
|
+
context "when Sidekiq constant is not found" do
|
12
|
+
before { hide_const "Sidekiq" }
|
13
|
+
|
14
|
+
it { is_expected.to be_falsy }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#install" do
|
19
|
+
class SidekiqMiddlewareMock < Set
|
20
|
+
def exists?(middleware)
|
21
|
+
include?(middleware)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
module SidekiqMock
|
25
|
+
def self.middlewares
|
26
|
+
@middlewares ||= SidekiqMiddlewareMock.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.configure_server
|
30
|
+
yield self
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.server_middleware
|
34
|
+
yield middlewares if block_given?
|
35
|
+
middlewares
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
before do
|
40
|
+
Appsignal.config = project_fixture_config
|
41
|
+
stub_const "Sidekiq", SidekiqMock
|
42
|
+
end
|
43
|
+
|
44
|
+
it "adds the AppSignal SidekiqPlugin to the Sidekiq middleware chain" do
|
45
|
+
described_class.new.install
|
46
|
+
|
47
|
+
expect(Sidekiq.server_middleware.exists?(Appsignal::Hooks::SidekiqPlugin)).to be(true)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
1
52
|
describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
|
2
53
|
class DelayedTestClass; end
|
3
54
|
|
@@ -25,9 +76,10 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
|
|
25
76
|
]
|
26
77
|
end
|
27
78
|
let(:job_class) { "TestClass" }
|
79
|
+
let(:jid) { "b4a577edbccf1d805744efa9" }
|
28
80
|
let(:item) do
|
29
81
|
{
|
30
|
-
"jid" =>
|
82
|
+
"jid" => jid,
|
31
83
|
"class" => job_class,
|
32
84
|
"retry_count" => 0,
|
33
85
|
"queue" => "default",
|
@@ -48,37 +100,13 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
|
|
48
100
|
expect(log_contents(log)).to_not contains_log(:warn, "Unable to load YAML")
|
49
101
|
end
|
50
102
|
|
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
|
56
|
-
|
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
|
-
)
|
66
|
-
end
|
67
|
-
|
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
|
74
|
-
|
75
103
|
describe "internal Sidekiq job values" do
|
76
104
|
it "does not save internal Sidekiq values as metadata on transaction" do
|
77
105
|
perform_job
|
78
106
|
|
79
107
|
transaction_hash = transaction.to_h
|
80
108
|
expect(transaction_hash["metadata"].keys)
|
81
|
-
.to_not include(*Appsignal::Hooks::SidekiqPlugin::
|
109
|
+
.to_not include(*Appsignal::Hooks::SidekiqPlugin::EXCLUDED_JOB_KEYS)
|
82
110
|
end
|
83
111
|
end
|
84
112
|
|
@@ -124,7 +152,7 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
|
|
124
152
|
context "when using the Sidekiq delayed extension" do
|
125
153
|
let(:item) do
|
126
154
|
{
|
127
|
-
"jid" =>
|
155
|
+
"jid" => jid,
|
128
156
|
"class" => "Sidekiq::Extensions::DelayedClass",
|
129
157
|
"queue" => "default",
|
130
158
|
"args" => [
|
@@ -164,7 +192,7 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
|
|
164
192
|
context "when using the Sidekiq ActiveRecord instance delayed extension" do
|
165
193
|
let(:item) do
|
166
194
|
{
|
167
|
-
"jid" =>
|
195
|
+
"jid" => jid,
|
168
196
|
"class" => "Sidekiq::Extensions::DelayedModel",
|
169
197
|
"queue" => "default",
|
170
198
|
"args" => [
|
@@ -201,217 +229,6 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
|
|
201
229
|
end
|
202
230
|
end
|
203
231
|
|
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
|
224
|
-
|
225
|
-
it "creates a transaction with events" do
|
226
|
-
perform_job
|
227
|
-
|
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)
|
256
|
-
end
|
257
|
-
|
258
|
-
context "with ActionMailer job" do
|
259
|
-
let(:item) do
|
260
|
-
{
|
261
|
-
"class" => "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper",
|
262
|
-
"wrapped" => "ActionMailer::DeliveryJob",
|
263
|
-
"queue" => "default",
|
264
|
-
"args" => [{
|
265
|
-
"job_class" => "ActiveMailerTestJob",
|
266
|
-
"job_id" => "23e79d48-6966-40d0-b2d4-f7938463a263",
|
267
|
-
"queue_name" => "default",
|
268
|
-
"arguments" => [
|
269
|
-
"MailerClass", "mailer_method", "deliver_now",
|
270
|
-
"foo", { "foo" => "Foo", "bar" => "Bar", "baz" => { 1 => :bar } }
|
271
|
-
]
|
272
|
-
}],
|
273
|
-
"retry" => true,
|
274
|
-
"jid" => "efb140489485999d32b5504c",
|
275
|
-
"created_at" => Time.parse("2001-01-01 10:00:00UTC").to_f,
|
276
|
-
"enqueued_at" => Time.parse("2001-01-01 10:00:00UTC").to_f
|
277
|
-
}
|
278
|
-
end
|
279
|
-
|
280
|
-
it "creates a transaction for the ActionMailer class" do
|
281
|
-
perform_job
|
282
|
-
|
283
|
-
transaction_hash = transaction.to_h
|
284
|
-
expect(transaction_hash).to include(
|
285
|
-
"id" => kind_of(String),
|
286
|
-
"action" => "MailerClass#mailer_method",
|
287
|
-
"error" => nil,
|
288
|
-
"namespace" => namespace,
|
289
|
-
"metadata" => {
|
290
|
-
"queue" => "default"
|
291
|
-
},
|
292
|
-
"sample_data" => {
|
293
|
-
"environment" => {},
|
294
|
-
"params" => [
|
295
|
-
"foo",
|
296
|
-
{
|
297
|
-
"foo" => "Foo",
|
298
|
-
"bar" => "Bar",
|
299
|
-
"baz" => { "1" => "bar" }
|
300
|
-
}
|
301
|
-
],
|
302
|
-
"tags" => {}
|
303
|
-
}
|
304
|
-
)
|
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
|
-
]
|
327
|
-
)
|
328
|
-
end
|
329
|
-
end
|
330
|
-
|
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
|
365
|
-
{
|
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
|
-
]
|
372
|
-
}
|
373
|
-
end
|
374
|
-
|
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
|
381
|
-
|
382
|
-
it "stores the job metadata on the transaction" do
|
383
|
-
transaction_hash = transaction.to_h
|
384
|
-
expect(transaction_hash).to include(
|
385
|
-
"id" => kind_of(String),
|
386
|
-
"error" => nil,
|
387
|
-
"namespace" => namespace,
|
388
|
-
"metadata" => {
|
389
|
-
"queue" => "default"
|
390
|
-
},
|
391
|
-
"sample_data" => {
|
392
|
-
"environment" => {},
|
393
|
-
"params" => [
|
394
|
-
"foo",
|
395
|
-
{
|
396
|
-
"foo" => "Foo",
|
397
|
-
"bar" => "Bar",
|
398
|
-
"baz" => { "1" => "bar" }
|
399
|
-
}
|
400
|
-
],
|
401
|
-
"tags" => {}
|
402
|
-
}
|
403
|
-
)
|
404
|
-
end
|
405
|
-
|
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"
|
409
|
-
)
|
410
|
-
end
|
411
|
-
end
|
412
|
-
end
|
413
|
-
end
|
414
|
-
|
415
232
|
context "with an error" do
|
416
233
|
let(:error) { ExampleException }
|
417
234
|
|
@@ -427,7 +244,7 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
|
|
427
244
|
|
428
245
|
transaction_hash = transaction.to_h
|
429
246
|
expect(transaction_hash).to include(
|
430
|
-
"id" =>
|
247
|
+
"id" => jid,
|
431
248
|
"action" => "TestClass#perform",
|
432
249
|
"error" => {
|
433
250
|
"name" => "ExampleException",
|
@@ -461,7 +278,7 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
|
|
461
278
|
|
462
279
|
transaction_hash = transaction.to_h
|
463
280
|
expect(transaction_hash).to include(
|
464
|
-
"id" =>
|
281
|
+
"id" => jid,
|
465
282
|
"action" => "TestClass#perform",
|
466
283
|
"error" => nil,
|
467
284
|
"metadata" => {
|
@@ -510,47 +327,184 @@ describe Appsignal::Hooks::SidekiqPlugin, :with_yaml_parse_error => false do
|
|
510
327
|
end
|
511
328
|
end
|
512
329
|
|
513
|
-
|
514
|
-
|
515
|
-
|
330
|
+
if DependencyHelper.active_job_present?
|
331
|
+
require "active_job"
|
332
|
+
require "action_mailer"
|
333
|
+
require "sidekiq/testing"
|
334
|
+
|
335
|
+
describe "Sidekiq ActiveJob integration" do
|
336
|
+
let(:namespace) { Appsignal::Transaction::BACKGROUND_JOB }
|
337
|
+
let(:time) { Time.parse("2001-01-01 10:00:00UTC") }
|
338
|
+
let(:log) { StringIO.new }
|
339
|
+
let(:given_args) do
|
340
|
+
[
|
341
|
+
"foo",
|
342
|
+
{
|
343
|
+
:foo => "Foo",
|
344
|
+
"bar" => "Bar",
|
345
|
+
"baz" => { "1" => "foo" }
|
346
|
+
}
|
347
|
+
]
|
348
|
+
end
|
349
|
+
let(:expected_args) do
|
350
|
+
[
|
351
|
+
"foo",
|
352
|
+
{
|
353
|
+
"_aj_symbol_keys" => ["foo"],
|
354
|
+
"foo" => "Foo",
|
355
|
+
"bar" => "Bar",
|
356
|
+
"baz" => {
|
357
|
+
"_aj_symbol_keys" => [],
|
358
|
+
"1" => "foo"
|
359
|
+
}
|
360
|
+
}
|
361
|
+
]
|
362
|
+
end
|
363
|
+
let(:expected_tags) do
|
364
|
+
{}.tap do |hash|
|
365
|
+
hash["active_job_id"] = kind_of(String)
|
366
|
+
if DependencyHelper.rails_version >= Gem::Version.new("5.0.0")
|
367
|
+
hash["provider_job_id"] = kind_of(String)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
before do
|
372
|
+
start_agent
|
373
|
+
Appsignal.logger = test_logger(log)
|
374
|
+
ActiveJob::Base.queue_adapter = :sidekiq
|
516
375
|
|
517
|
-
|
518
|
-
|
519
|
-
after { Object.send(:remove_const, "Sidekiq") }
|
376
|
+
class ActiveJobSidekiqTestJob < ActiveJob::Base
|
377
|
+
self.queue_adapter = :sidekiq
|
520
378
|
|
521
|
-
|
379
|
+
def perform(*_args)
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
class ActiveJobSidekiqErrorTestJob < ActiveJob::Base
|
384
|
+
self.queue_adapter = :sidekiq
|
385
|
+
|
386
|
+
def perform(*_args)
|
387
|
+
raise "uh oh"
|
388
|
+
end
|
389
|
+
end
|
390
|
+
# Manually add the AppSignal middleware for the Testing environment.
|
391
|
+
# It doesn't use configured middlewares by default looks like.
|
392
|
+
# We test somewhere else if the middleware is installed properly.
|
393
|
+
Sidekiq::Testing.server_middleware do |chain|
|
394
|
+
chain.add Appsignal::Hooks::SidekiqPlugin
|
395
|
+
end
|
396
|
+
end
|
397
|
+
around do |example|
|
398
|
+
keep_transactions do
|
399
|
+
Sidekiq::Testing.fake! do
|
400
|
+
example.run
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
after do
|
405
|
+
Object.send(:remove_const, :ActiveJobSidekiqTestJob)
|
406
|
+
Object.send(:remove_const, :ActiveJobSidekiqErrorTestJob)
|
522
407
|
end
|
523
408
|
|
524
|
-
|
525
|
-
|
409
|
+
it "reports the transaction from the ActiveJob integration" do
|
410
|
+
perform_job(ActiveJobSidekiqTestJob, given_args)
|
526
411
|
|
527
|
-
|
412
|
+
transaction = last_transaction
|
413
|
+
transaction_hash = transaction.to_h
|
414
|
+
expect(transaction_hash).to include(
|
415
|
+
"action" => "ActiveJobSidekiqTestJob#perform",
|
416
|
+
"error" => nil,
|
417
|
+
"namespace" => namespace,
|
418
|
+
"metadata" => hash_including(
|
419
|
+
"queue" => "default"
|
420
|
+
),
|
421
|
+
"sample_data" => hash_including(
|
422
|
+
"environment" => {},
|
423
|
+
"params" => [expected_args],
|
424
|
+
"tags" => expected_tags.merge("queue" => "default")
|
425
|
+
)
|
426
|
+
)
|
427
|
+
expect(transaction.request.env).to eq(:queue_start => time.to_f)
|
428
|
+
events = transaction_hash["events"]
|
429
|
+
.sort_by { |e| e["start"] }
|
430
|
+
.map { |event| event["name"] }
|
431
|
+
expect(events)
|
432
|
+
.to eq(["perform_job.sidekiq", "perform_start.active_job", "perform.active_job"])
|
528
433
|
end
|
529
|
-
end
|
530
434
|
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
@middlewares ||= Set.new
|
537
|
-
end
|
435
|
+
context "with error" do
|
436
|
+
it "reports the error on the transaction from the ActiveRecord integration" do
|
437
|
+
expect do
|
438
|
+
perform_job(ActiveJobSidekiqErrorTestJob, given_args)
|
439
|
+
end.to raise_error(RuntimeError, "uh oh")
|
538
440
|
|
539
|
-
|
540
|
-
|
541
|
-
|
441
|
+
transaction = last_transaction
|
442
|
+
transaction_hash = transaction.to_h
|
443
|
+
expect(transaction_hash).to include(
|
444
|
+
"action" => "ActiveJobSidekiqErrorTestJob#perform",
|
445
|
+
"error" => {
|
446
|
+
"name" => "RuntimeError",
|
447
|
+
"message" => "uh oh",
|
448
|
+
"backtrace" => kind_of(String)
|
449
|
+
},
|
450
|
+
"namespace" => namespace,
|
451
|
+
"metadata" => hash_including(
|
452
|
+
"queue" => "default"
|
453
|
+
),
|
454
|
+
"sample_data" => hash_including(
|
455
|
+
"environment" => {},
|
456
|
+
"params" => [expected_args],
|
457
|
+
"tags" => expected_tags.merge("queue" => "default")
|
458
|
+
)
|
459
|
+
)
|
460
|
+
expect(transaction.request.env).to eq(:queue_start => time.to_f)
|
461
|
+
events = transaction_hash["events"]
|
462
|
+
.sort_by { |e| e["start"] }
|
463
|
+
.map { |event| event["name"] }
|
464
|
+
expect(events)
|
465
|
+
.to eq(["perform_job.sidekiq", "perform_start.active_job", "perform.active_job"])
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
context "with ActionMailer" do
|
470
|
+
include ActionMailerHelpers
|
542
471
|
|
543
|
-
|
544
|
-
|
472
|
+
before do
|
473
|
+
class ActionMailerSidekiqTestJob < ActionMailer::Base
|
474
|
+
def welcome(*args)
|
475
|
+
end
|
545
476
|
end
|
546
477
|
end
|
478
|
+
|
479
|
+
it "reports ActionMailer data on the transaction" do
|
480
|
+
perform_mailer(ActionMailerSidekiqTestJob, :welcome, given_args)
|
481
|
+
|
482
|
+
transaction = last_transaction
|
483
|
+
transaction_hash = transaction.to_h
|
484
|
+
expect(transaction_hash).to include(
|
485
|
+
"action" => "ActionMailerSidekiqTestJob#welcome",
|
486
|
+
"sample_data" => hash_including(
|
487
|
+
"params" => ["ActionMailerSidekiqTestJob", "welcome", "deliver_now"] + expected_args
|
488
|
+
)
|
489
|
+
)
|
490
|
+
end
|
547
491
|
end
|
548
|
-
after { Object.send(:remove_const, "Sidekiq") }
|
549
492
|
|
550
|
-
|
551
|
-
|
493
|
+
def perform_sidekiq
|
494
|
+
Timecop.freeze(time) do
|
495
|
+
yield
|
496
|
+
# Combined with Sidekiq::Testing.fake! and drain_all we get a
|
497
|
+
# enqueue_at in the job data.
|
498
|
+
Sidekiq::Worker.drain_all
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
def perform_job(job_class, args)
|
503
|
+
perform_sidekiq { job_class.perform_later(args) }
|
504
|
+
end
|
552
505
|
|
553
|
-
|
506
|
+
def perform_mailer(mailer, method, args = nil)
|
507
|
+
perform_sidekiq { perform_action_mailer(mailer, method, args) }
|
554
508
|
end
|
555
509
|
end
|
556
510
|
end
|