appsignal 3.9.3 → 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +22 -19
  3. data/.rubocop.yml +1 -1
  4. data/CHANGELOG.md +180 -0
  5. data/Gemfile +1 -0
  6. data/README.md +0 -1
  7. data/Rakefile +1 -1
  8. data/benchmark.rake +99 -42
  9. data/build_matrix.yml +10 -12
  10. data/gemfiles/webmachine1.gemfile +5 -4
  11. data/lib/appsignal/cli/demo.rb +0 -1
  12. data/lib/appsignal/config.rb +57 -97
  13. data/lib/appsignal/demo.rb +15 -20
  14. data/lib/appsignal/environment.rb +6 -1
  15. data/lib/appsignal/event_formatter/rom/sql_formatter.rb +1 -0
  16. data/lib/appsignal/event_formatter.rb +3 -2
  17. data/lib/appsignal/helpers/instrumentation.rb +490 -16
  18. data/lib/appsignal/hooks/action_cable.rb +21 -16
  19. data/lib/appsignal/hooks/active_job.rb +15 -14
  20. data/lib/appsignal/hooks/delayed_job.rb +1 -1
  21. data/lib/appsignal/hooks/shoryuken.rb +3 -63
  22. data/lib/appsignal/integrations/action_cable.rb +5 -7
  23. data/lib/appsignal/integrations/active_support_notifications.rb +1 -0
  24. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +36 -35
  25. data/lib/appsignal/integrations/data_mapper.rb +1 -0
  26. data/lib/appsignal/integrations/delayed_job_plugin.rb +27 -33
  27. data/lib/appsignal/integrations/dry_monitor.rb +1 -0
  28. data/lib/appsignal/integrations/excon.rb +1 -0
  29. data/lib/appsignal/integrations/http.rb +1 -0
  30. data/lib/appsignal/integrations/net_http.rb +1 -0
  31. data/lib/appsignal/integrations/object.rb +6 -0
  32. data/lib/appsignal/integrations/padrino.rb +21 -25
  33. data/lib/appsignal/integrations/que.rb +13 -20
  34. data/lib/appsignal/integrations/railtie.rb +1 -1
  35. data/lib/appsignal/integrations/rake.rb +45 -15
  36. data/lib/appsignal/integrations/redis.rb +1 -0
  37. data/lib/appsignal/integrations/redis_client.rb +1 -0
  38. data/lib/appsignal/integrations/resque.rb +2 -5
  39. data/lib/appsignal/integrations/shoryuken.rb +75 -0
  40. data/lib/appsignal/integrations/sidekiq.rb +7 -25
  41. data/lib/appsignal/integrations/unicorn.rb +1 -0
  42. data/lib/appsignal/integrations/webmachine.rb +12 -9
  43. data/lib/appsignal/logger.rb +7 -3
  44. data/lib/appsignal/probes/helpers.rb +1 -0
  45. data/lib/appsignal/probes/mri.rb +1 -0
  46. data/lib/appsignal/probes/sidekiq.rb +1 -0
  47. data/lib/appsignal/probes.rb +3 -0
  48. data/lib/appsignal/rack/abstract_middleware.rb +67 -24
  49. data/lib/appsignal/rack/body_wrapper.rb +143 -0
  50. data/lib/appsignal/rack/event_handler.rb +39 -8
  51. data/lib/appsignal/rack/generic_instrumentation.rb +6 -4
  52. data/lib/appsignal/rack/grape_middleware.rb +3 -2
  53. data/lib/appsignal/rack/hanami_middleware.rb +1 -1
  54. data/lib/appsignal/rack/instrumentation_middleware.rb +62 -0
  55. data/lib/appsignal/rack/rails_instrumentation.rb +1 -3
  56. data/lib/appsignal/rack/sinatra_instrumentation.rb +1 -3
  57. data/lib/appsignal/rack/streaming_listener.rb +14 -59
  58. data/lib/appsignal/rack.rb +60 -0
  59. data/lib/appsignal/span.rb +1 -0
  60. data/lib/appsignal/transaction.rb +353 -104
  61. data/lib/appsignal/utils/data.rb +0 -1
  62. data/lib/appsignal/utils/hash_sanitizer.rb +0 -1
  63. data/lib/appsignal/utils/integration_logger.rb +0 -13
  64. data/lib/appsignal/utils/integration_memory_logger.rb +0 -13
  65. data/lib/appsignal/utils/json.rb +0 -1
  66. data/lib/appsignal/utils/query_params_sanitizer.rb +0 -1
  67. data/lib/appsignal/utils/stdout_and_logger_message.rb +0 -1
  68. data/lib/appsignal/utils.rb +6 -0
  69. data/lib/appsignal/version.rb +1 -1
  70. data/lib/appsignal.rb +9 -6
  71. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  72. data/spec/lib/appsignal/config_spec.rb +139 -43
  73. data/spec/lib/appsignal/hooks/action_cable_spec.rb +43 -74
  74. data/spec/lib/appsignal/hooks/activejob_spec.rb +9 -0
  75. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +2 -443
  76. data/spec/lib/appsignal/hooks/rake_spec.rb +100 -17
  77. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +0 -171
  78. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +459 -0
  79. data/spec/lib/appsignal/integrations/padrino_spec.rb +181 -131
  80. data/spec/lib/appsignal/integrations/que_spec.rb +3 -4
  81. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +167 -0
  82. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +4 -4
  83. data/spec/lib/appsignal/integrations/sinatra_spec.rb +10 -2
  84. data/spec/lib/appsignal/integrations/webmachine_spec.rb +77 -17
  85. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +144 -11
  86. data/spec/lib/appsignal/rack/body_wrapper_spec.rb +263 -0
  87. data/spec/lib/appsignal/rack/event_handler_spec.rb +81 -10
  88. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +70 -17
  89. data/spec/lib/appsignal/rack/grape_middleware_spec.rb +1 -1
  90. data/spec/lib/appsignal/rack/instrumentation_middleware_spec.rb +38 -0
  91. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +4 -2
  92. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +43 -120
  93. data/spec/lib/appsignal/rack_spec.rb +63 -0
  94. data/spec/lib/appsignal/transaction_spec.rb +1675 -953
  95. data/spec/lib/appsignal/utils/integration_logger_spec.rb +12 -16
  96. data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +0 -10
  97. data/spec/lib/appsignal_spec.rb +517 -13
  98. data/spec/support/helpers/transaction_helpers.rb +44 -20
  99. data/spec/support/matchers/transaction.rb +15 -1
  100. data/spec/support/mocks/dummy_app.rb +1 -1
  101. data/spec/support/testing.rb +1 -1
  102. metadata +12 -4
  103. data/support/check_versions +0 -22
@@ -3,40 +3,133 @@ if DependencyHelper.padrino_present?
3
3
  require "appsignal/integrations/padrino"
4
4
 
5
5
  describe Appsignal::Integrations::PadrinoPlugin do
6
- it "starts AppSignal on init" do
7
- expect(Appsignal).to receive(:start)
6
+ let(:callbacks) { { :before_load => nil } }
7
+ before do
8
+ Appsignal.config = nil
9
+ allow(Padrino).to receive(:before_load)
10
+ .and_wrap_original do |original_method, *args, &block|
11
+ callbacks[:before_load] = block
12
+ original_method.call(*args, &block)
13
+ end
14
+ end
15
+ after { uninstall_padrino_integration }
16
+
17
+ def uninstall_padrino_integration
18
+ expected_middleware = [
19
+ Rack::Events,
20
+ Appsignal::Rack::SinatraBaseInstrumentation
21
+ ]
22
+ Padrino.middleware.delete_if do |middleware|
23
+ expected_middleware.include?(middleware.first)
24
+ end
8
25
  end
9
26
 
10
- context "when not active" do
11
- before { allow(Appsignal).to receive(:active?).and_return(false) }
27
+ context "when already active" do
28
+ before { allow(Appsignal).to receive(:active?).and_return(true) }
12
29
 
13
- it "does not add the listener middleware to the stack" do
14
- expect(Padrino).to_not receive(:use)
30
+ it "does not start AppSignal again" do
31
+ expect(Appsignal::Config).to_not receive(:new)
32
+ expect(Appsignal).to_not receive(:start)
33
+
34
+ Appsignal::Integrations::PadrinoPlugin.init
35
+ callbacks[:before_load].call
36
+ end
37
+
38
+ it "adds the instrumentation middleware to Sinatra::Base" do
39
+ Appsignal::Integrations::PadrinoPlugin.init
40
+ callbacks[:before_load].call
41
+
42
+ middlewares = Padrino.middleware
43
+ expect(middlewares).to include(
44
+ [Rack::Events, [[instance_of(Appsignal::Rack::EventHandler)]], nil]
45
+ )
46
+ expect(middlewares).to include(
47
+ [
48
+ Appsignal::Rack::SinatraBaseInstrumentation,
49
+ [
50
+ :instrument_event_name => "process_action.padrino"
51
+ ],
52
+ nil
53
+ ]
54
+ )
15
55
  end
16
56
  end
17
57
 
18
- context "when APPSIGNAL_APP_ENV ENV var is provided" do
19
- it "uses this as the environment" do
20
- ENV["APPSIGNAL_APP_ENV"] = "custom"
58
+ context "with active config" do
59
+ before do
60
+ ENV["APPSIGNAL_APP_NAME"] = "My Padrino app name"
61
+ ENV["APPSIGNAL_APP_ENV"] = "test"
62
+ ENV["APPSIGNAL_PUSH_API_KEY"] = "my-key"
63
+ end
64
+
65
+ it "starts AppSignal on init" do
66
+ expect(Appsignal).to_not be_active
21
67
 
22
- # Reset the plugin to pull down the latest data
23
68
  Appsignal::Integrations::PadrinoPlugin.init
69
+ callbacks[:before_load].call
70
+
71
+ expect(Appsignal).to be_active
72
+ middlewares = Padrino.middleware
73
+ expect(middlewares).to include(
74
+ [Rack::Events, [[instance_of(Appsignal::Rack::EventHandler)]], nil]
75
+ )
76
+ expect(middlewares).to include(
77
+ [
78
+ Appsignal::Rack::SinatraBaseInstrumentation,
79
+ [
80
+ :instrument_event_name => "process_action.padrino"
81
+ ],
82
+ nil
83
+ ]
84
+ )
85
+ end
86
+
87
+ context "when APPSIGNAL_APP_ENV ENV var is provided" do
88
+ it "uses this as the environment" do
89
+ ENV["APPSIGNAL_APP_ENV"] = "custom"
90
+
91
+ Appsignal::Integrations::PadrinoPlugin.init
92
+ callbacks[:before_load].call
24
93
 
25
- expect(Appsignal.config.env).to eq("custom")
94
+ expect(Appsignal.config.env).to eq("custom")
95
+ end
96
+ end
97
+
98
+ context "when APPSIGNAL_APP_ENV ENV var is not provided" do
99
+ it "uses the Padrino environment" do
100
+ Appsignal::Integrations::PadrinoPlugin.init
101
+ callbacks[:before_load].call
102
+
103
+ expect(Padrino.env.to_s).to eq("test")
104
+ expect(Appsignal.config.env).to eq(Padrino.env.to_s)
105
+ end
26
106
  end
27
107
  end
28
108
 
29
- context "when APPSIGNAL_APP_ENV ENV var is not provided" do
30
- it "uses the Padrino environment" do
31
- # Reset the plugin to pull down the latest data
109
+ context "when not active" do
110
+ it "does not add the listener middleware to the stack" do
111
+ expect(Appsignal).to_not be_active
112
+
32
113
  Appsignal::Integrations::PadrinoPlugin.init
114
+ callbacks[:before_load].call
33
115
 
34
- expect(Padrino.env.to_s).to eq("test")
35
- expect(Appsignal.config.env).to eq(Padrino.env.to_s)
116
+ expect(Appsignal).to_not be_active
117
+ middlewares = Padrino.middleware
118
+ expect(middlewares).to_not include(
119
+ [Rack::Events, [[instance_of(Appsignal::Rack::EventHandler)]], nil]
120
+ )
121
+ expect(middlewares).to_not include(
122
+ [
123
+ Appsignal::Rack::SinatraBaseInstrumentation,
124
+ [
125
+ :request_class => ::Sinatra::Request,
126
+ :instrument_event_name => "process_action.padrino"
127
+ ],
128
+ nil
129
+ ]
130
+ )
36
131
  end
37
132
  end
38
-
39
- after { Appsignal::Integrations::PadrinoPlugin.init }
40
133
  end
41
134
 
42
135
  describe Padrino::Routing::InstanceMethods do
@@ -50,9 +143,9 @@ if DependencyHelper.padrino_present?
50
143
  # TODO: use an instance double
51
144
  let(:settings) { double(:name => "TestApp") }
52
145
  around { |example| keep_transactions { example.run } }
146
+ before { Appsignal.config = nil }
53
147
 
54
148
  describe "routes" do
55
- let(:request_kind) { kind_of(Sinatra::Request) }
56
149
  let(:env) do
57
150
  {
58
151
  "REQUEST_METHOD" => "GET",
@@ -83,20 +176,7 @@ if DependencyHelper.padrino_present?
83
176
  end
84
177
  end
85
178
 
86
- def expect_a_transaction_to_be_created
87
- transaction = last_transaction
88
- expect(transaction).to have_id
89
- expect(transaction).to have_namespace(Appsignal::Transaction::HTTP_REQUEST)
90
- expect(transaction).to include_metadata(
91
- "path" => path,
92
- "method" => "GET"
93
- )
94
- expect(transaction).to include_event("name" => "process_action.padrino")
95
- expect(transaction).to be_completed
96
- end
97
-
98
179
  context "when AppSignal is not active" do
99
- before { allow(Appsignal).to receive(:active?).and_return(false) }
100
180
  let(:path) { "/foo" }
101
181
  before { app.controllers { get(:foo) { "content" } } }
102
182
 
@@ -108,16 +188,17 @@ if DependencyHelper.padrino_present?
108
188
  end
109
189
 
110
190
  context "when AppSignal is active" do
111
- before { start_agent }
191
+ let(:transaction) { http_request_transaction }
192
+ before do
193
+ start_agent
194
+ set_current_transaction(transaction)
195
+ end
112
196
 
113
197
  context "with not existing route" do
114
198
  let(:path) { "/404" }
115
199
 
116
200
  it "instruments the request" do
117
201
  expect(response).to match_response(404, /^GET /404/)
118
-
119
- expect_a_transaction_to_be_created
120
- # Uses path for action name
121
202
  expect(last_transaction).to have_action("PadrinoTestApp#unknown")
122
203
  end
123
204
  end
@@ -130,9 +211,8 @@ if DependencyHelper.padrino_present?
130
211
  end
131
212
 
132
213
  it "does not instrument the request" do
133
- expect do
134
- expect(response).to match_response(200, "Static!")
135
- end.to_not(change { created_transactions.count })
214
+ expect(response).to match_response(200, "Static!")
215
+ expect(last_transaction).to_not have_action
136
216
  end
137
217
  end
138
218
 
@@ -145,11 +225,7 @@ if DependencyHelper.padrino_present?
145
225
  end
146
226
 
147
227
  it "falls back on Sinatra::Request#route_obj.original_path" do
148
- expect do
149
- expect(response).to match_response(200, "content")
150
- end.to(change { created_transactions.count }.by(1))
151
-
152
- expect_a_transaction_to_be_created
228
+ expect(response).to match_response(200, "content")
153
229
  expect(last_transaction).to have_action("PadrinoTestApp:/my_original_path/:id")
154
230
  end
155
231
  end
@@ -164,118 +240,92 @@ if DependencyHelper.padrino_present?
164
240
 
165
241
  it "falls back on app name" do
166
242
  expect(response).to match_response(200, "content")
167
- expect_a_transaction_to_be_created
168
243
  expect(last_transaction).to have_action("PadrinoTestApp#unknown")
169
244
  end
170
245
  end
171
246
 
172
247
  context "with existing route" do
173
- context "with an exception in the controller" do
174
- let(:path) { "/exception" }
175
- before do
176
- app.controllers { get(:exception) { raise ExampleException, "error message" } }
177
- expect { response }.to raise_error(ExampleException, "error message")
178
- expect_a_transaction_to_be_created
179
- end
248
+ let(:path) { "/" }
249
+ def make_request
250
+ expect(response).to match_response(200, "content")
251
+ end
180
252
 
181
- it "sets the action name based on the app name and action name" do
182
- expect(last_transaction).to have_action("PadrinoTestApp:#exception")
183
- end
253
+ context "with action name as symbol" do
254
+ context "with :index helper" do
255
+ before do
256
+ # :index == "/"
257
+ app.controllers { get(:index) { "content" } }
258
+ end
184
259
 
185
- it "sets the error on the transaction" do
186
- expect(last_transaction).to have_error("ExampleException", "error message")
260
+ it "sets the action with the app name and action name" do
261
+ make_request
262
+ expect(last_transaction).to have_action("PadrinoTestApp:#index")
263
+ end
187
264
  end
188
- end
189
265
 
190
- context "without an exception in the controller" do
191
- let(:path) { "/" }
192
- def make_request
193
- expect(response).to match_response(200, "content")
266
+ context "with custom action name" do
267
+ let(:path) { "/foo" }
268
+ before do
269
+ app.controllers { get(:foo) { "content" } }
270
+ end
271
+
272
+ it "sets the action with the app name and action name" do
273
+ make_request
274
+ expect(last_transaction).to have_action("PadrinoTestApp:#foo")
275
+ end
194
276
  end
277
+ end
195
278
 
196
- context "with action name as symbol" do
197
- context "with :index helper" do
198
- before do
199
- # :index == "/"
200
- app.controllers { get(:index) { "content" } }
201
- end
202
-
203
- it "sets the action with the app name and action name" do
204
- make_request
205
- expect_a_transaction_to_be_created
206
- expect(last_transaction).to have_action("PadrinoTestApp:#index")
207
- end
279
+ context "with an action defined with a path" do
280
+ context "with root path" do
281
+ before do
282
+ # :index == "/"
283
+ app.controllers { get("/") { "content" } }
208
284
  end
209
285
 
210
- context "with custom action name" do
211
- let(:path) { "/foo" }
212
- before do
213
- app.controllers { get(:foo) { "content" } }
214
- end
215
-
216
- it "sets the action with the app name and action name" do
217
- make_request
218
- expect_a_transaction_to_be_created
219
- expect(last_transaction).to have_action("PadrinoTestApp:#foo")
220
- end
286
+ it "sets the action with the app name and action path" do
287
+ make_request
288
+ expect(last_transaction).to have_action("PadrinoTestApp:#/")
221
289
  end
222
290
  end
223
291
 
224
- context "with an action defined with a path" do
225
- context "with root path" do
226
- before do
227
- # :index == "/"
228
- app.controllers { get("/") { "content" } }
229
- end
230
-
231
- it "sets the action with the app name and action path" do
232
- make_request
233
- expect_a_transaction_to_be_created
234
- expect(last_transaction).to have_action("PadrinoTestApp:#/")
235
- end
292
+ context "with custom path" do
293
+ let(:path) { "/foo" }
294
+ before do
295
+ app.controllers { get("/foo") { "content" } }
236
296
  end
237
297
 
238
- context "with custom path" do
239
- let(:path) { "/foo" }
240
- before do
241
- app.controllers { get("/foo") { "content" } }
242
- end
243
-
244
- it "sets the action with the app name and action path" do
245
- make_request
246
- expect_a_transaction_to_be_created
247
- expect(last_transaction).to have_action("PadrinoTestApp:#/foo")
248
- end
298
+ it "sets the action with the app name and action path" do
299
+ make_request
300
+ expect(last_transaction).to have_action("PadrinoTestApp:#/foo")
249
301
  end
250
302
  end
303
+ end
304
+
305
+ context "with controller" do
306
+ let(:path) { "/my_controller" }
251
307
 
252
- context "with controller" do
253
- let(:path) { "/my_controller" }
308
+ context "with controller as name" do
309
+ before do
310
+ # :index == "/"
311
+ app.controllers(:my_controller) { get(:index) { "content" } }
312
+ end
254
313
 
255
- context "with controller as name" do
256
- before do
257
- # :index == "/"
258
- app.controllers(:my_controller) { get(:index) { "content" } }
259
- end
314
+ it "sets the action with the app name, controller name and action name" do
315
+ make_request
316
+ expect(last_transaction).to have_action("PadrinoTestApp:my_controller#index")
317
+ end
318
+ end
260
319
 
261
- it "sets the action with the app name, controller name and action name" do
262
- make_request
263
- expect_a_transaction_to_be_created
264
- expect(last_transaction).to have_action("PadrinoTestApp:my_controller#index")
265
- end
320
+ context "with controller as path" do
321
+ before do
322
+ # :index == "/"
323
+ app.controllers("/my_controller") { get(:index) { "content" } }
266
324
  end
267
325
 
268
- context "with controller as path" do
269
- before do
270
- # :index == "/"
271
- app.controllers("/my_controller") { get(:index) { "content" } }
272
- end
273
-
274
- it "sets the action with the app name, controller name and action path" do
275
- make_request
276
- expect_a_transaction_to_be_created
277
- expect(last_transaction).to have_action("PadrinoTestApp:/my_controller#index")
278
- end
326
+ it "sets the action with the app name, controller name and action path" do
327
+ make_request
328
+ expect(last_transaction).to have_action("PadrinoTestApp:/my_controller#index")
279
329
  end
280
330
  end
281
331
  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 include_sample_metadata(
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 include_sample_metadata(
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 include_sample_metadata(
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(jid)
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).to_not include_tags
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(jid)
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).to_not include_tags
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)
@@ -7,8 +7,12 @@ if DependencyHelper.sinatra_present?
7
7
 
8
8
  # "Uninstall" the AppSignal integration
9
9
  def uninstall_sinatra_integration
10
+ expected_middleware = [
11
+ Rack::Events,
12
+ Appsignal::Rack::SinatraBaseInstrumentation
13
+ ]
10
14
  Sinatra::Base.instance_variable_get(:@middleware).delete_if do |middleware|
11
- middleware.first == Appsignal::Rack::SinatraBaseInstrumentation
15
+ expected_middleware.include?(middleware.first)
12
16
  end
13
17
  end
14
18
 
@@ -29,7 +33,11 @@ if DependencyHelper.sinatra_present?
29
33
 
30
34
  it "adds the instrumentation middleware to Sinatra::Base" do
31
35
  install_sinatra_integration
32
- expect(Sinatra::Base.middleware.to_a).to include(
36
+ middlewares = Sinatra::Base.middleware.to_a
37
+ expect(middlewares).to include(
38
+ [Rack::Events, [[instance_of(Appsignal::Rack::EventHandler)]], nil]
39
+ )
40
+ expect(middlewares).to include(
33
41
  [Appsignal::Rack::SinatraBaseInstrumentation, [], nil]
34
42
  )
35
43
  end