appsignal 3.9.3-java → 3.10.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +22 -19
  3. data/CHANGELOG.md +92 -0
  4. data/README.md +0 -1
  5. data/Rakefile +1 -1
  6. data/build_matrix.yml +10 -12
  7. data/gemfiles/webmachine1.gemfile +5 -4
  8. data/lib/appsignal/config.rb +4 -0
  9. data/lib/appsignal/environment.rb +6 -1
  10. data/lib/appsignal/helpers/instrumentation.rb +163 -1
  11. data/lib/appsignal/hooks/active_job.rb +1 -6
  12. data/lib/appsignal/integrations/padrino.rb +21 -25
  13. data/lib/appsignal/integrations/rake.rb +46 -12
  14. data/lib/appsignal/integrations/sidekiq.rb +1 -11
  15. data/lib/appsignal/integrations/webmachine.rb +15 -9
  16. data/lib/appsignal/rack/abstract_middleware.rb +49 -12
  17. data/lib/appsignal/rack/body_wrapper.rb +143 -0
  18. data/lib/appsignal/rack/generic_instrumentation.rb +5 -4
  19. data/lib/appsignal/rack/grape_middleware.rb +1 -1
  20. data/lib/appsignal/rack/hanami_middleware.rb +1 -1
  21. data/lib/appsignal/rack/instrumentation_middleware.rb +62 -0
  22. data/lib/appsignal/rack/rails_instrumentation.rb +1 -3
  23. data/lib/appsignal/rack/sinatra_instrumentation.rb +1 -3
  24. data/lib/appsignal/rack/streaming_listener.rb +13 -59
  25. data/lib/appsignal/rack.rb +31 -0
  26. data/lib/appsignal/transaction.rb +50 -8
  27. data/lib/appsignal/version.rb +1 -1
  28. data/lib/appsignal.rb +3 -1
  29. data/spec/lib/appsignal/config_spec.rb +1 -0
  30. data/spec/lib/appsignal/hooks/rake_spec.rb +100 -17
  31. data/spec/lib/appsignal/integrations/padrino_spec.rb +181 -131
  32. data/spec/lib/appsignal/integrations/sinatra_spec.rb +10 -2
  33. data/spec/lib/appsignal/integrations/webmachine_spec.rb +65 -17
  34. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +96 -8
  35. data/spec/lib/appsignal/rack/body_wrapper_spec.rb +263 -0
  36. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +70 -17
  37. data/spec/lib/appsignal/rack/grape_middleware_spec.rb +1 -1
  38. data/spec/lib/appsignal/rack/instrumentation_middleware_spec.rb +38 -0
  39. data/spec/lib/appsignal/rack/streaming_listener_spec.rb +43 -120
  40. data/spec/lib/appsignal/transaction_spec.rb +163 -4
  41. data/spec/lib/appsignal_spec.rb +197 -6
  42. data/spec/support/mocks/dummy_app.rb +1 -1
  43. metadata +8 -4
  44. 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
@@ -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
@@ -15,31 +15,60 @@ if DependencyHelper.webmachine_present?
15
15
 
16
16
  describe Appsignal::Integrations::WebmachineIntegration do
17
17
  let(:request) do
18
- Webmachine::Request.new("GET", "http://google.com:80/foo", {}, nil)
18
+ Webmachine::Request.new(
19
+ "GET",
20
+ "http://google.com:80/foo?param1=value1&param2=value2",
21
+ {},
22
+ nil
23
+ )
19
24
  end
20
- let(:resource) { double(:trace? => false, :handle_exception => true, :"code=" => nil) }
21
- let(:response) { Response.new }
22
- let(:fsm) { Webmachine::Decision::FSM.new(resource, request, response) }
23
- before(:context) { start_agent }
24
- around { |example| keep_transactions { example.run } }
25
+ let(:app) do
26
+ proc do
27
+ def to_html
28
+ "Some HTML"
29
+ end
30
+ end
31
+ end
32
+ let(:resource) do
33
+ app_block = app
34
+ Class.new(Webmachine::Resource) do
35
+ class_eval(&app_block) if app_block
25
36
 
26
- # Make sure the request responds to the method we need to get query params.
27
- describe "request" do
28
- it "responds to #query" do
29
- expect(request).to respond_to(:query)
37
+ def self.name
38
+ "MyResource"
39
+ end
30
40
  end
31
41
  end
42
+ let(:resource_instance) { resource.new(request, response) }
43
+ let(:response) { Webmachine::Response.new }
44
+ let(:fsm) { Webmachine::Decision::FSM.new(resource_instance, request, response) }
45
+ before { start_agent }
46
+ around { |example| keep_transactions { example.run } }
32
47
 
33
48
  describe "#run" do
34
- before { allow(fsm).to receive(:call).and_call_original }
35
-
36
49
  it "creates a transaction" do
37
50
  expect { fsm.run }.to(change { created_transactions.count }.by(1))
38
51
  end
39
52
 
40
53
  it "sets the action" do
41
54
  fsm.run
42
- expect(last_transaction).to have_action("RSpec::Mocks::Double#GET")
55
+ expect(last_transaction).to have_action("MyResource#GET")
56
+ end
57
+
58
+ context "with action already set" do
59
+ let(:app) do
60
+ proc do
61
+ def to_html
62
+ Appsignal.set_action("Custom Action")
63
+ "Some HTML"
64
+ end
65
+ end
66
+ end
67
+
68
+ it "doesn't overwrite the action" do
69
+ fsm.run
70
+ expect(last_transaction).to have_action("Custom Action")
71
+ end
43
72
  end
44
73
 
45
74
  it "records an instrumentation event" do
@@ -47,16 +76,35 @@ if DependencyHelper.webmachine_present?
47
76
  expect(last_transaction).to include_event("name" => "process_action.webmachine")
48
77
  end
49
78
 
79
+ it "sets the params" do
80
+ fsm.run
81
+ expect(last_transaction).to include_params("param1" => "value1", "param2" => "value2")
82
+ end
83
+
50
84
  it "closes the transaction" do
51
85
  fsm.run
52
86
  expect(last_transaction).to be_completed
53
87
  expect(current_transaction?).to be_falsy
54
88
  end
55
89
 
56
- it "sets a response code" do
57
- expect(fsm.response.code).to be_nil
58
- fsm.run
59
- expect(fsm.response.code).not_to be_nil
90
+ context "with parent transaction" do
91
+ let(:transaction) { http_request_transaction }
92
+ before { set_current_transaction(transaction) }
93
+
94
+ it "sets the action" do
95
+ fsm.run
96
+ expect(last_transaction).to have_action("MyResource#GET")
97
+ end
98
+
99
+ it "sets the params" do
100
+ fsm.run
101
+ last_transaction._sample
102
+ expect(last_transaction).to include_params("param1" => "value1", "param2" => "value2")
103
+ end
104
+
105
+ it "does not close the transaction" do
106
+ expect(last_transaction).to_not be_completed
107
+ end
60
108
  end
61
109
  end
62
110