appsignal 3.10.0-java → 3.12.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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +197 -0
  4. data/Gemfile +1 -0
  5. data/Rakefile +1 -1
  6. data/benchmark.rake +99 -42
  7. data/lib/appsignal/cli/demo.rb +0 -1
  8. data/lib/appsignal/cli/diagnose.rb +1 -1
  9. data/lib/appsignal/config.rb +204 -130
  10. data/lib/appsignal/demo.rb +16 -26
  11. data/lib/appsignal/event_formatter/rom/sql_formatter.rb +1 -0
  12. data/lib/appsignal/event_formatter.rb +3 -2
  13. data/lib/appsignal/helpers/instrumentation.rb +331 -19
  14. data/lib/appsignal/hooks/action_cable.rb +21 -16
  15. data/lib/appsignal/hooks/active_job.rb +14 -8
  16. data/lib/appsignal/hooks/delayed_job.rb +1 -1
  17. data/lib/appsignal/hooks/shoryuken.rb +3 -63
  18. data/lib/appsignal/integrations/action_cable.rb +5 -7
  19. data/lib/appsignal/integrations/active_support_notifications.rb +1 -0
  20. data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +36 -35
  21. data/lib/appsignal/integrations/data_mapper.rb +1 -0
  22. data/lib/appsignal/integrations/delayed_job_plugin.rb +27 -33
  23. data/lib/appsignal/integrations/dry_monitor.rb +1 -0
  24. data/lib/appsignal/integrations/excon.rb +1 -0
  25. data/lib/appsignal/integrations/grape.rb +7 -0
  26. data/lib/appsignal/integrations/hanami.rb +8 -43
  27. data/lib/appsignal/integrations/http.rb +1 -0
  28. data/lib/appsignal/integrations/net_http.rb +1 -0
  29. data/lib/appsignal/integrations/object.rb +6 -0
  30. data/lib/appsignal/integrations/padrino.rb +8 -73
  31. data/lib/appsignal/integrations/que.rb +13 -20
  32. data/lib/appsignal/integrations/railtie.rb +36 -14
  33. data/lib/appsignal/integrations/rake.rb +1 -5
  34. data/lib/appsignal/integrations/redis.rb +1 -0
  35. data/lib/appsignal/integrations/redis_client.rb +1 -0
  36. data/lib/appsignal/integrations/resque.rb +2 -5
  37. data/lib/appsignal/integrations/shoryuken.rb +75 -0
  38. data/lib/appsignal/integrations/sidekiq.rb +7 -15
  39. data/lib/appsignal/integrations/sinatra.rb +8 -19
  40. data/lib/appsignal/integrations/unicorn.rb +1 -0
  41. data/lib/appsignal/integrations/webmachine.rb +2 -5
  42. data/lib/appsignal/loaders/grape.rb +13 -0
  43. data/lib/appsignal/loaders/hanami.rb +40 -0
  44. data/lib/appsignal/loaders/padrino.rb +68 -0
  45. data/lib/appsignal/loaders/sinatra.rb +24 -0
  46. data/lib/appsignal/loaders.rb +92 -0
  47. data/lib/appsignal/logger.rb +7 -3
  48. data/lib/appsignal/probes/helpers.rb +1 -0
  49. data/lib/appsignal/probes/mri.rb +1 -0
  50. data/lib/appsignal/probes/sidekiq.rb +1 -0
  51. data/lib/appsignal/probes.rb +3 -0
  52. data/lib/appsignal/rack/abstract_middleware.rb +20 -13
  53. data/lib/appsignal/rack/event_handler.rb +44 -13
  54. data/lib/appsignal/rack/generic_instrumentation.rb +1 -0
  55. data/lib/appsignal/rack/grape_middleware.rb +2 -1
  56. data/lib/appsignal/rack/streaming_listener.rb +1 -0
  57. data/lib/appsignal/rack.rb +35 -0
  58. data/lib/appsignal/span.rb +1 -0
  59. data/lib/appsignal/transaction.rb +308 -101
  60. data/lib/appsignal/utils/data.rb +0 -1
  61. data/lib/appsignal/utils/hash_sanitizer.rb +0 -1
  62. data/lib/appsignal/utils/integration_logger.rb +0 -13
  63. data/lib/appsignal/utils/integration_memory_logger.rb +0 -13
  64. data/lib/appsignal/utils/json.rb +0 -1
  65. data/lib/appsignal/utils/query_params_sanitizer.rb +0 -1
  66. data/lib/appsignal/utils/stdout_and_logger_message.rb +0 -1
  67. data/lib/appsignal/utils.rb +6 -0
  68. data/lib/appsignal/version.rb +1 -1
  69. data/lib/appsignal.rb +169 -14
  70. data/spec/lib/appsignal/capistrano2_spec.rb +1 -1
  71. data/spec/lib/appsignal/cli/demo_spec.rb +0 -1
  72. data/spec/lib/appsignal/cli/diagnose/paths_spec.rb +1 -1
  73. data/spec/lib/appsignal/cli/diagnose_spec.rb +0 -1
  74. data/spec/lib/appsignal/config_spec.rb +291 -44
  75. data/spec/lib/appsignal/demo_spec.rb +1 -2
  76. data/spec/lib/appsignal/environment_spec.rb +4 -2
  77. data/spec/lib/appsignal/hooks/action_cable_spec.rb +43 -74
  78. data/spec/lib/appsignal/hooks/active_support_notifications_spec.rb +3 -6
  79. data/spec/lib/appsignal/hooks/activejob_spec.rb +12 -3
  80. data/spec/lib/appsignal/hooks/delayed_job_spec.rb +2 -443
  81. data/spec/lib/appsignal/hooks/dry_monitor_spec.rb +4 -7
  82. data/spec/lib/appsignal/hooks/excon_spec.rb +3 -6
  83. data/spec/lib/appsignal/hooks/gvl_spec.rb +2 -2
  84. data/spec/lib/appsignal/hooks/http_spec.rb +1 -3
  85. data/spec/lib/appsignal/hooks/net_http_spec.rb +1 -1
  86. data/spec/lib/appsignal/hooks/redis_client_spec.rb +5 -8
  87. data/spec/lib/appsignal/hooks/redis_spec.rb +3 -6
  88. data/spec/lib/appsignal/hooks/resque_spec.rb +1 -1
  89. data/spec/lib/appsignal/hooks/sequel_spec.rb +3 -5
  90. data/spec/lib/appsignal/hooks/shoryuken_spec.rb +0 -171
  91. data/spec/lib/appsignal/hooks/sidekiq_spec.rb +1 -1
  92. data/spec/lib/appsignal/hooks/webmachine_spec.rb +1 -1
  93. data/spec/lib/appsignal/integrations/delayed_job_plugin_spec.rb +459 -0
  94. data/spec/lib/appsignal/integrations/grape_spec.rb +36 -0
  95. data/spec/lib/appsignal/integrations/hanami_spec.rb +9 -178
  96. data/spec/lib/appsignal/integrations/http_spec.rb +1 -5
  97. data/spec/lib/appsignal/integrations/mongo_ruby_driver_spec.rb +4 -2
  98. data/spec/lib/appsignal/integrations/net_http_spec.rb +1 -1
  99. data/spec/lib/appsignal/integrations/object_spec.rb +1 -3
  100. data/spec/lib/appsignal/integrations/padrino_spec.rb +8 -330
  101. data/spec/lib/appsignal/integrations/que_spec.rb +3 -4
  102. data/spec/lib/appsignal/integrations/railtie_spec.rb +275 -191
  103. data/spec/lib/appsignal/integrations/shoryuken_spec.rb +167 -0
  104. data/spec/lib/appsignal/integrations/sidekiq_spec.rb +15 -13
  105. data/spec/lib/appsignal/integrations/sinatra_spec.rb +9 -104
  106. data/spec/lib/appsignal/integrations/webmachine_spec.rb +13 -1
  107. data/spec/lib/appsignal/loaders/grape_spec.rb +12 -0
  108. data/spec/lib/appsignal/loaders/hanami_spec.rb +95 -0
  109. data/spec/lib/appsignal/loaders/padrino_spec.rb +277 -0
  110. data/spec/lib/appsignal/loaders/sinatra_spec.rb +47 -0
  111. data/spec/lib/appsignal/loaders_spec.rb +137 -0
  112. data/spec/lib/appsignal/probes/sidekiq_spec.rb +1 -1
  113. data/spec/lib/appsignal/probes_spec.rb +6 -5
  114. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +51 -5
  115. data/spec/lib/appsignal/rack/event_handler_spec.rb +114 -10
  116. data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +1 -1
  117. data/spec/lib/appsignal/rack/grape_middleware_spec.rb +2 -35
  118. data/spec/lib/appsignal/rack/hanami_middleware_spec.rb +1 -1
  119. data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +4 -2
  120. data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +3 -3
  121. data/spec/lib/appsignal/rack_spec.rb +63 -0
  122. data/spec/lib/appsignal/span_spec.rb +1 -3
  123. data/spec/lib/appsignal/transaction_spec.rb +1640 -1075
  124. data/spec/lib/appsignal/utils/integration_logger_spec.rb +12 -16
  125. data/spec/lib/appsignal/utils/integration_memory_logger_spec.rb +0 -10
  126. data/spec/lib/appsignal_spec.rb +601 -36
  127. data/spec/lib/puma/appsignal_spec.rb +0 -3
  128. data/spec/spec_helper.rb +5 -4
  129. data/spec/support/helpers/config_helpers.rb +2 -1
  130. data/spec/support/helpers/loader_helper.rb +21 -0
  131. data/spec/support/helpers/transaction_helpers.rb +44 -20
  132. data/spec/support/matchers/transaction.rb +15 -1
  133. data/spec/support/stubs/appsignal/loaders/loader_stub.rb +7 -0
  134. data/spec/support/testing.rb +47 -1
  135. metadata +19 -2
@@ -0,0 +1,277 @@
1
+ if DependencyHelper.padrino_present?
2
+ describe "Appsignal::Loaders::PadrinoLoader" do
3
+ before { Appsignal.config = nil }
4
+
5
+ describe "#on_load" do
6
+ it "registers Padrino default config" do
7
+ load_loader(:padrino)
8
+
9
+ expect(Appsignal::Config.loader_defaults).to include([
10
+ :padrino,
11
+ {
12
+ :env => :test,
13
+ :root_path => Padrino.mounted_root
14
+ }
15
+ ])
16
+ end
17
+ end
18
+
19
+ describe "#on_start" do
20
+ let(:callbacks) { { :before_load => nil } }
21
+ before do
22
+ allow(Padrino).to receive(:before_load)
23
+ .and_wrap_original do |original_method, *args, &block|
24
+ callbacks[:before_load] = block
25
+ original_method.call(*args, &block)
26
+ end
27
+ end
28
+ after { uninstall_padrino_integration }
29
+
30
+ def uninstall_padrino_integration
31
+ expected_middleware = [
32
+ Rack::Events,
33
+ Appsignal::Rack::SinatraBaseInstrumentation
34
+ ]
35
+ Padrino.middleware.delete_if do |middleware|
36
+ expected_middleware.include?(middleware.first)
37
+ end
38
+ end
39
+
40
+ it "adds the instrumentation middleware to Padrino" do
41
+ load_loader(:padrino)
42
+ start_loader(:padrino)
43
+
44
+ callbacks[:before_load].call
45
+
46
+ middlewares = Padrino.middleware
47
+ expect(middlewares).to include(
48
+ [Rack::Events, [[instance_of(Appsignal::Rack::EventHandler)]], nil]
49
+ )
50
+ expect(middlewares).to include(
51
+ [
52
+ Appsignal::Rack::SinatraBaseInstrumentation,
53
+ [
54
+ :instrument_event_name => "process_action.padrino"
55
+ ],
56
+ nil
57
+ ]
58
+ )
59
+ end
60
+ end
61
+
62
+ describe "Padrino integration" do
63
+ class PadrinoClassWithRouter
64
+ include Padrino::Routing
65
+ end
66
+
67
+ let(:base) { double }
68
+ let(:router) { PadrinoClassWithRouter.new }
69
+ let(:env) { {} }
70
+ # TODO: use an instance double
71
+ let(:settings) { double(:name => "TestApp") }
72
+ around { |example| keep_transactions { example.run } }
73
+ before { Appsignal.config = nil }
74
+
75
+ describe "routes" do
76
+ let(:env) do
77
+ {
78
+ "REQUEST_METHOD" => "GET",
79
+ "PATH_INFO" => path,
80
+ "REQUEST_PATH" => path,
81
+ "rack.input" => StringIO.new
82
+ }
83
+ end
84
+ let(:app) do
85
+ Class.new(Padrino::Application) do
86
+ def self.name
87
+ "PadrinoTestApp"
88
+ end
89
+ end
90
+ end
91
+ let(:response) { app.call(env) }
92
+
93
+ def fetch_body(body)
94
+ if body.respond_to?(:response)
95
+ _, _, nested_body = body.response.to_a
96
+ fetch_body(nested_body)
97
+ elsif body.respond_to?(:to_ary)
98
+ body.to_ary
99
+ else
100
+ body
101
+ end
102
+ end
103
+
104
+ RSpec::Matchers.define :match_response do |expected_status, expected_body|
105
+ match do |response|
106
+ status, _headers, potential_body = response
107
+ body = fetch_body(potential_body)
108
+
109
+ matches_body =
110
+ if expected_body.is_a?(Regexp)
111
+ body.join =~ expected_body
112
+ else
113
+ body == [expected_body].compact
114
+ end
115
+ status == expected_status && matches_body
116
+ end
117
+ end
118
+
119
+ context "when AppSignal is not active" do
120
+ let(:path) { "/foo" }
121
+ before { app.controllers { get(:foo) { "content" } } }
122
+
123
+ it "does not instrument the request" do
124
+ expect do
125
+ expect(response).to match_response(200, "content")
126
+ end.to_not(change { created_transactions.count })
127
+ end
128
+ end
129
+
130
+ context "when AppSignal is active" do
131
+ let(:transaction) { http_request_transaction }
132
+ before do
133
+ start_agent
134
+ set_current_transaction(transaction)
135
+ end
136
+
137
+ context "with not existing route" do
138
+ let(:path) { "/404" }
139
+
140
+ it "instruments the request" do
141
+ expect(response).to match_response(404, /^GET /404/)
142
+ expect(last_transaction).to have_action("PadrinoTestApp#unknown")
143
+ end
144
+ end
145
+
146
+ context "when Sinatra tells us it's a static file" do
147
+ let(:path) { "/static" }
148
+ before do
149
+ env["sinatra.static_file"] = true
150
+ app.controllers { get(:static) { "Static!" } }
151
+ end
152
+
153
+ it "does not instrument the request" do
154
+ expect(response).to match_response(200, "Static!")
155
+ expect(last_transaction).to_not have_action
156
+ end
157
+ end
158
+
159
+ # Older Padrino versions don't support `action` (v11.0+)
160
+ context "without #action on Sinatra::Request" do
161
+ let(:path) { "/my_original_path/10" }
162
+ before do
163
+ allow_any_instance_of(Sinatra::Request).to receive(:action).and_return(nil)
164
+ app.controllers { get(:my_original_path, :with => :id) { "content" } }
165
+ end
166
+
167
+ it "falls back on Sinatra::Request#route_obj.original_path" do
168
+ expect(response).to match_response(200, "content")
169
+ expect(last_transaction).to have_action("PadrinoTestApp:/my_original_path/:id")
170
+ end
171
+ end
172
+
173
+ context "without Sinatra::Request#route_obj.original_path" do
174
+ let(:path) { "/my_original_path" }
175
+ before do
176
+ allow_any_instance_of(Sinatra::Request).to receive(:action).and_return(nil)
177
+ allow_any_instance_of(Sinatra::Request).to receive(:route_obj).and_return(nil)
178
+ app.controllers { get(:my_original_path) { "content" } }
179
+ end
180
+
181
+ it "falls back on app name" do
182
+ expect(response).to match_response(200, "content")
183
+ expect(last_transaction).to have_action("PadrinoTestApp#unknown")
184
+ end
185
+ end
186
+
187
+ context "with existing route" do
188
+ let(:path) { "/" }
189
+ def make_request
190
+ expect(response).to match_response(200, "content")
191
+ end
192
+
193
+ context "with action name as symbol" do
194
+ context "with :index helper" do
195
+ before do
196
+ # :index == "/"
197
+ app.controllers { get(:index) { "content" } }
198
+ end
199
+
200
+ it "sets the action with the app name and action name" do
201
+ make_request
202
+ expect(last_transaction).to have_action("PadrinoTestApp:#index")
203
+ end
204
+ end
205
+
206
+ context "with custom action name" do
207
+ let(:path) { "/foo" }
208
+ before do
209
+ app.controllers { get(:foo) { "content" } }
210
+ end
211
+
212
+ it "sets the action with the app name and action name" do
213
+ make_request
214
+ expect(last_transaction).to have_action("PadrinoTestApp:#foo")
215
+ end
216
+ end
217
+ end
218
+
219
+ context "with an action defined with a path" do
220
+ context "with root path" do
221
+ before do
222
+ # :index == "/"
223
+ app.controllers { get("/") { "content" } }
224
+ end
225
+
226
+ it "sets the action with the app name and action path" do
227
+ make_request
228
+ expect(last_transaction).to have_action("PadrinoTestApp:#/")
229
+ end
230
+ end
231
+
232
+ context "with custom path" do
233
+ let(:path) { "/foo" }
234
+ before do
235
+ app.controllers { get("/foo") { "content" } }
236
+ end
237
+
238
+ it "sets the action with the app name and action path" do
239
+ make_request
240
+ expect(last_transaction).to have_action("PadrinoTestApp:#/foo")
241
+ end
242
+ end
243
+ end
244
+
245
+ context "with controller" do
246
+ let(:path) { "/my_controller" }
247
+
248
+ context "with controller as name" do
249
+ before do
250
+ # :index == "/"
251
+ app.controllers(:my_controller) { get(:index) { "content" } }
252
+ end
253
+
254
+ it "sets the action with the app name, controller name and action name" do
255
+ make_request
256
+ expect(last_transaction).to have_action("PadrinoTestApp:my_controller#index")
257
+ end
258
+ end
259
+
260
+ context "with controller as path" do
261
+ before do
262
+ # :index == "/"
263
+ app.controllers("/my_controller") { get(:index) { "content" } }
264
+ end
265
+
266
+ it "sets the action with the app name, controller name and action path" do
267
+ make_request
268
+ expect(last_transaction).to have_action("PadrinoTestApp:/my_controller#index")
269
+ end
270
+ end
271
+ end
272
+ end
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,47 @@
1
+ if DependencyHelper.sinatra_present?
2
+ describe "Appsignal::Loaders::SinatraLoader" do
3
+ before { Appsignal.config = nil }
4
+
5
+ describe "#on_load" do
6
+ it "registers Sinatra default config" do
7
+ ::Sinatra::Application.settings.root = "/some/path"
8
+ load_loader(:sinatra)
9
+
10
+ expect(Appsignal::Config.loader_defaults).to include([
11
+ :sinatra,
12
+ {
13
+ :env => :test,
14
+ :root_path => "/some/path"
15
+ }
16
+ ])
17
+ end
18
+ end
19
+
20
+ describe "#on_start" do
21
+ after { uninstall_sinatra_integration }
22
+
23
+ def uninstall_sinatra_integration
24
+ expected_middleware = [
25
+ Rack::Events,
26
+ Appsignal::Rack::SinatraBaseInstrumentation
27
+ ]
28
+ Sinatra::Base.instance_variable_get(:@middleware).delete_if do |middleware|
29
+ expected_middleware.include?(middleware.first)
30
+ end
31
+ end
32
+
33
+ it "adds the instrumentation middleware to Sinatra::Base" do
34
+ load_loader(:sinatra)
35
+ start_loader(:sinatra)
36
+
37
+ middlewares = Sinatra::Base.middleware.to_a
38
+ expect(middlewares).to include(
39
+ [Rack::Events, [[instance_of(Appsignal::Rack::EventHandler)]], nil]
40
+ )
41
+ expect(middlewares).to include(
42
+ [Appsignal::Rack::SinatraBaseInstrumentation, [], nil]
43
+ )
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,137 @@
1
+ describe Appsignal::Loaders do
2
+ describe ".register" do
3
+ before do
4
+ define_loader(:test_loader) do
5
+ def on_load
6
+ puts "do something on_load"
7
+ register_config_defaults(
8
+ :root_path => "/some/path",
9
+ :env => "test env",
10
+ :active => false
11
+ )
12
+ end
13
+ end
14
+ end
15
+
16
+ it "registers a loader" do
17
+ define_loader(:test_loader)
18
+ expect(Appsignal::Loaders.loaders).to have_key(:test_loader)
19
+ end
20
+ end
21
+
22
+ describe ".unregister" do
23
+ it "unregisters a loader" do
24
+ define_loader(:test_loader)
25
+ expect(Appsignal::Loaders.loaders).to have_key(:test_loader)
26
+
27
+ Appsignal::Loaders.unregister(:test_loader)
28
+ expect(Appsignal::Loaders.loaders).to_not have_key(:test_loader)
29
+ end
30
+ end
31
+
32
+ describe ".load" do
33
+ it "calls the Loader's on_loader method" do
34
+ Appsignal::Testing.store[:loader_loaded] = 0
35
+ define_loader(:test_loader) do
36
+ def on_load
37
+ Appsignal::Testing.store[:loader_loaded] += 1
38
+ end
39
+ end
40
+ Appsignal::Loaders.load(:test_loader)
41
+
42
+ expect(Appsignal::Testing.store[:loader_loaded]).to eq(1)
43
+ end
44
+
45
+ it "registers config defaults" do
46
+ define_loader(:test_loader) do
47
+ def on_load
48
+ register_config_defaults(:my_option => true)
49
+ end
50
+ end
51
+ Appsignal::Loaders.load(:test_loader)
52
+
53
+ expect(Appsignal::Config.loader_defaults).to eq([[:test_loader, { :my_option => true }]])
54
+ end
55
+
56
+ it "does not load errors that aren't registered" do
57
+ logs =
58
+ capture_logs do
59
+ Appsignal::Loaders.load(:unknown_loader)
60
+ end
61
+
62
+ expect(logs).to contains_log(:warn, "No loader found with the name 'unknown_loader'.")
63
+ end
64
+
65
+ it "loads the loader file on load" do
66
+ expect(Appsignal::Loaders.registered?(:loader_stub)).to be_falsy
67
+ Appsignal::Loaders.load(:loader_stub)
68
+
69
+ expect(Appsignal::Loaders.registered?(:loader_stub)).to be_truthy
70
+ end
71
+
72
+ it "does not error when a loader has no on_load method" do
73
+ define_loader(:test_loader) do
74
+ # Do nothing
75
+ end
76
+ Appsignal::Loaders.load(:test_loader)
77
+ end
78
+
79
+ it "logs an error when an error occurs on load" do
80
+ define_loader(:test_loader) do
81
+ def on_load
82
+ raise ExampleStandardError, "uh oh"
83
+ end
84
+ end
85
+ logs =
86
+ capture_logs do
87
+ Appsignal::Loaders.load(:test_loader)
88
+ end
89
+
90
+ expect(logs).to contains_log(
91
+ :error,
92
+ "An error occurred while loading the 'test_loader' loader: ExampleStandardError: uh oh"
93
+ )
94
+ end
95
+ end
96
+
97
+ describe ".start" do
98
+ it "starts all loaded loaders" do
99
+ Appsignal::Testing.store[:loader_started] = 0
100
+ define_loader(:test_loader) do
101
+ def on_start
102
+ Appsignal::Testing.store[:loader_started] += 1
103
+ end
104
+ end
105
+ Appsignal::Loaders.load(:test_loader)
106
+ Appsignal::Loaders.start
107
+
108
+ expect(Appsignal::Testing.store[:loader_started]).to eq(1)
109
+ end
110
+
111
+ it "does not error when a loader has no on_start method" do
112
+ define_loader(:test_loader) do
113
+ # Do nothing
114
+ end
115
+ Appsignal::Loaders.load(:test_loader)
116
+ Appsignal::Loaders.start
117
+ end
118
+
119
+ it "logs an error when an error occurs on start" do
120
+ define_loader(:test_loader) do
121
+ def on_start
122
+ raise ExampleStandardError, "uh oh"
123
+ end
124
+ end
125
+ logs =
126
+ capture_logs do
127
+ Appsignal::Loaders.load(:test_loader)
128
+ Appsignal::Loaders.start
129
+ end
130
+
131
+ expect(logs).to contains_log(
132
+ :error,
133
+ "An error occurred while starting the 'test_loader' loader: ExampleStandardError: uh oh"
134
+ )
135
+ end
136
+ end
137
+ end
@@ -6,7 +6,7 @@ describe Appsignal::Probes::SidekiqProbe do
6
6
  let(:redis_hostname) { "localhost" }
7
7
  let(:expected_default_tags) { { :hostname => "localhost" } }
8
8
  before do
9
- Appsignal.config = project_fixture_config
9
+ start_agent
10
10
 
11
11
  class SidekiqStats
12
12
  class << self
@@ -408,13 +408,14 @@ describe Appsignal::Probes do
408
408
  end
409
409
 
410
410
  context "logger" do
411
- let(:log_stream) { std_stream }
412
- let(:log) { log_contents(log_stream) }
411
+ before { start_agent }
413
412
 
414
- around { |example| use_logger_with(log_stream) { example.run } }
415
413
  it "logs a deprecation message" do
416
- silence { collection.register :my_probe, lambda {} }
417
- expect(log).to contains_log :warn,
414
+ logs =
415
+ capture_logs do
416
+ silence { collection.register :my_probe, lambda {} }
417
+ end
418
+ expect(logs).to contains_log :warn,
418
419
  "The method 'Appsignal::Probes.probes.register' is deprecated. " \
419
420
  "Use 'Appsignal::Probes.register' instead."
420
421
  end
@@ -5,13 +5,14 @@ describe Appsignal::Rack::AbstractMiddleware do
5
5
  Rack::MockRequest.env_for(
6
6
  request_path,
7
7
  "REQUEST_METHOD" => "GET",
8
- :params => { "page" => 2, "query" => "lorem" }
8
+ :params => { "page" => 2, "query" => "lorem" },
9
+ "rack.session" => { "session" => "data", "user_id" => 123 }
9
10
  )
10
11
  end
11
12
  let(:options) { {} }
12
13
  let(:middleware) { described_class.new(app, options) }
13
14
 
14
- before(:context) { start_agent }
15
+ before { start_agent }
15
16
  around { |example| keep_transactions { example.run } }
16
17
 
17
18
  def make_request
@@ -247,7 +248,7 @@ describe Appsignal::Rack::AbstractMiddleware do
247
248
  end
248
249
  end
249
250
 
250
- context "with fetching the request method raises an error" do
251
+ context "when fetching the request method raises an error" do
251
252
  class BrokenRequestMethodRequest < Rack::Request
252
253
  def request_method
253
254
  raise "uh oh!"
@@ -255,11 +256,16 @@ describe Appsignal::Rack::AbstractMiddleware do
255
256
  end
256
257
 
257
258
  let(:options) { { :request_class => BrokenRequestMethodRequest } }
259
+
258
260
  it "does not store the invalid HTTP request method" do
259
261
  env["REQUEST_METHOD"] = "FOO"
260
- make_request
262
+ logs = capture_logs { make_request }
261
263
 
262
264
  expect(last_transaction).to_not include_metadata("method" => anything)
265
+ expect(logs).to contains_log(
266
+ :error,
267
+ "Exception while fetching the HTTP request method: RuntimeError: uh oh"
268
+ )
263
269
  end
264
270
  end
265
271
 
@@ -285,6 +291,35 @@ describe Appsignal::Rack::AbstractMiddleware do
285
291
  expect(last_transaction).to include_params("custom" => "param")
286
292
  end
287
293
  end
294
+
295
+ context "when fetching the request method raises an error" do
296
+ class BrokenRequestParamsRequest < Rack::Request
297
+ def params
298
+ raise "uh oh!"
299
+ end
300
+ end
301
+
302
+ let(:options) do
303
+ { :request_class => BrokenRequestParamsRequest, :params_method => :params }
304
+ end
305
+
306
+ it "does not store the invalid HTTP request method" do
307
+ logs = capture_logs { make_request }
308
+
309
+ expect(last_transaction).to_not include_params
310
+ expect(logs).to contains_log(
311
+ :error,
312
+ "Exception while fetching params " \
313
+ "from 'BrokenRequestParamsRequest#params': RuntimeError uh oh!"
314
+ )
315
+ end
316
+ end
317
+
318
+ it "sets session data" do
319
+ make_request
320
+
321
+ expect(last_transaction).to include_session_data("session" => "data", "user_id" => 123)
322
+ end
288
323
  end
289
324
 
290
325
  context "with queue start header" do
@@ -316,6 +351,10 @@ describe Appsignal::Rack::AbstractMiddleware do
316
351
  def filtered_params
317
352
  { "abc" => "123" }
318
353
  end
354
+
355
+ def session
356
+ { "data" => "value" }
357
+ end
319
358
  end
320
359
 
321
360
  context "with overridden request class and params method" do
@@ -328,6 +367,12 @@ describe Appsignal::Rack::AbstractMiddleware do
328
367
 
329
368
  expect(last_transaction).to include_params("abc" => "123")
330
369
  end
370
+
371
+ it "uses the overridden request class to fetch session data" do
372
+ make_request
373
+
374
+ expect(last_transaction).to include_session_data("data" => "value")
375
+ end
331
376
  end
332
377
 
333
378
  context "with parent instrumentation" do
@@ -355,11 +400,12 @@ describe Appsignal::Rack::AbstractMiddleware do
355
400
  expect(response_events).to eq(1)
356
401
  end
357
402
 
358
- context "when response body is already a BodyWrapper subclass" do
403
+ context "when the response body is already instrumented" do
359
404
  let(:body) { Appsignal::Rack::BodyWrapper.wrap(["hello!"], transaction) }
360
405
  let(:app) { DummyApp.new { [200, {}, body] } }
361
406
 
362
407
  it "doesn't wrap the body again" do
408
+ env[Appsignal::Rack::APPSIGNAL_RESPONSE_INSTRUMENTED] = true
363
409
  _status, _headers, body = make_request
364
410
  expect(body).to eq(body)
365
411