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