appsignal 3.8.0-java → 3.9.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.
@@ -1,19 +1,83 @@
1
1
  require "appsignal/integrations/sidekiq"
2
2
 
3
- describe Appsignal::Integrations::SidekiqErrorHandler do
4
- let(:log) { StringIO.new }
5
- before do
6
- start_agent
7
- Appsignal.internal_logger = test_logger(log)
8
- end
3
+ describe Appsignal::Integrations::SidekiqDeathHandler do
4
+ before { start_agent }
9
5
  around { |example| keep_transactions { example.run } }
10
6
 
11
- context "without a current transaction" do
12
- let(:exception) do
13
- raise ExampleStandardError, "uh oh"
14
- rescue => error
15
- error
7
+ let(:exception) do
8
+ raise ExampleStandardError, "uh oh"
9
+ rescue => error
10
+ error
11
+ end
12
+ let(:job_context) { {} }
13
+ let(:transaction) { http_request_transaction }
14
+ before { set_current_transaction(transaction) }
15
+
16
+ def call_handler
17
+ expect do
18
+ described_class.new.call(job_context, exception)
19
+ end.to_not(change { created_transactions.count })
20
+ end
21
+
22
+ def expect_error_on_transaction
23
+ expect(last_transaction.to_h).to include(
24
+ "error" => hash_including(
25
+ "name" => "ExampleStandardError",
26
+ "message" => "uh oh",
27
+ "backtrace" => kind_of(String)
28
+ )
29
+ )
30
+ end
31
+
32
+ def expect_no_error_on_transaction
33
+ expect(last_transaction.to_h).to include("error" => nil)
34
+ end
35
+
36
+ context "when sidekiq_report_errors = none" do
37
+ before do
38
+ Appsignal.config[:sidekiq_report_errors] = "none"
39
+ call_handler
40
+ end
41
+
42
+ it "doesn't track the error on the transaction" do
43
+ expect_no_error_on_transaction
16
44
  end
45
+ end
46
+
47
+ context "when sidekiq_report_errors = all" do
48
+ before do
49
+ Appsignal.config[:sidekiq_report_errors] = "all"
50
+ call_handler
51
+ end
52
+
53
+ it "doesn't track the error on the transaction" do
54
+ expect_no_error_on_transaction
55
+ end
56
+ end
57
+
58
+ context "when sidekiq_report_errors = discard" do
59
+ before do
60
+ Appsignal.config[:sidekiq_report_errors] = "discard"
61
+ call_handler
62
+ end
63
+
64
+ it "records each occurrence of the error on the transaction" do
65
+ expect_error_on_transaction
66
+ end
67
+ end
68
+ end
69
+
70
+ describe Appsignal::Integrations::SidekiqErrorHandler do
71
+ before { start_agent }
72
+ around { |example| keep_transactions { example.run } }
73
+
74
+ let(:exception) do
75
+ raise ExampleStandardError, "uh oh"
76
+ rescue => error
77
+ error
78
+ end
79
+
80
+ context "when error is an internal error" do
17
81
  let(:job_context) do
18
82
  {
19
83
  :context => "Sidekiq internal error!",
@@ -21,14 +85,19 @@ describe Appsignal::Integrations::SidekiqErrorHandler do
21
85
  }
22
86
  end
23
87
 
24
- it "tracks error on a new transaction" do
25
- described_class.new.call(exception, job_context)
88
+ def expect_report_internal_error
89
+ expect do
90
+ described_class.new.call(exception, job_context)
91
+ end.to(change { created_transactions.count }.by(1))
26
92
 
27
93
  transaction_hash = last_transaction.to_h
28
- expect(transaction_hash["error"]).to include(
29
- "name" => "ExampleStandardError",
30
- "message" => "uh oh",
31
- "backtrace" => kind_of(String)
94
+ expect(transaction_hash).to include(
95
+ "action" => "SidekiqInternal",
96
+ "error" => hash_including(
97
+ "name" => "ExampleStandardError",
98
+ "message" => "uh oh",
99
+ "backtrace" => kind_of(String)
100
+ )
32
101
  )
33
102
  expect(transaction_hash["sample_data"]).to include(
34
103
  "params" => {
@@ -39,6 +108,95 @@ describe Appsignal::Integrations::SidekiqErrorHandler do
39
108
  "sidekiq_error" => "Sidekiq internal error!"
40
109
  )
41
110
  end
111
+
112
+ context "when sidekiq_report_errors = none" do
113
+ before { Appsignal.config[:sidekiq_report_errors] = "none" }
114
+
115
+ it "tracks the error on a new transaction" do
116
+ expect_report_internal_error
117
+ end
118
+ end
119
+
120
+ context "when sidekiq_report_errors = all" do
121
+ before { Appsignal.config[:sidekiq_report_errors] = "all" }
122
+
123
+ it "tracks the error on a new transaction" do
124
+ expect_report_internal_error
125
+ end
126
+ end
127
+
128
+ context "when sidekiq_report_errors = discard" do
129
+ before { Appsignal.config[:sidekiq_report_errors] = "discard" }
130
+
131
+ it "tracks the error on a new transaction" do
132
+ expect_report_internal_error
133
+ end
134
+ end
135
+ end
136
+
137
+ context "when error is a job error" do
138
+ let(:sidekiq_context) { { :job => {} } }
139
+ let(:transaction) { http_request_transaction }
140
+ before do
141
+ transaction.set_action("existing transaction action")
142
+ set_current_transaction(transaction)
143
+ end
144
+
145
+ def call_handler
146
+ expect do
147
+ described_class.new.call(exception, sidekiq_context)
148
+ end.to_not(change { created_transactions.count })
149
+ end
150
+
151
+ def expect_error_on_transaction
152
+ expect(last_transaction.to_h).to include(
153
+ "error" => hash_including(
154
+ "name" => "ExampleStandardError",
155
+ "message" => "uh oh",
156
+ "backtrace" => kind_of(String)
157
+ )
158
+ )
159
+ end
160
+
161
+ def expect_no_error_on_transaction
162
+ expect(last_transaction.to_h).to include("error" => nil)
163
+ end
164
+
165
+ context "when sidekiq_report_errors = none" do
166
+ before do
167
+ Appsignal.config[:sidekiq_report_errors] = "none"
168
+ call_handler
169
+ end
170
+
171
+ it "doesn't track the error on the transaction" do
172
+ expect_no_error_on_transaction
173
+ expect(last_transaction).to be_completed
174
+ end
175
+ end
176
+
177
+ context "when sidekiq_report_errors = all" do
178
+ before do
179
+ Appsignal.config[:sidekiq_report_errors] = "all"
180
+ call_handler
181
+ end
182
+
183
+ it "records each occurrence of the error on the transaction" do
184
+ expect_error_on_transaction
185
+ expect(last_transaction).to be_completed
186
+ end
187
+ end
188
+
189
+ context "when sidekiq_report_errors = discard" do
190
+ before do
191
+ Appsignal.config[:sidekiq_report_errors] = "discard"
192
+ call_handler
193
+ end
194
+
195
+ it "doesn't track the error on the transaction" do
196
+ expect_no_error_on_transaction
197
+ expect(last_transaction).to be_completed
198
+ end
199
+ end
42
200
  end
43
201
  end
44
202
 
@@ -13,59 +13,83 @@ if DependencyHelper.sinatra_present?
13
13
  end
14
14
 
15
15
  describe "Sinatra integration" do
16
- before { allow(Appsignal).to receive(:active?).and_return(true) }
16
+ before do
17
+ Appsignal.config = nil
18
+ end
17
19
  after { uninstall_sinatra_integration }
18
20
 
19
- context "Appsignal.internal_logger" do
20
- subject { Appsignal.internal_logger }
21
+ context "when active" do
22
+ before { allow(Appsignal).to receive(:active?).and_return(true) }
23
+
24
+ it "does not start AppSignal again" do
25
+ expect(Appsignal::Config).to_not receive(:new)
26
+ expect(Appsignal).to_not receive(:start)
27
+ expect(Appsignal).to_not receive(:start_logger)
28
+ install_sinatra_integration
29
+ end
21
30
 
22
- it "sets a logger" do
31
+ it "adds the instrumentation middleware to Sinatra::Base" do
23
32
  install_sinatra_integration
24
- is_expected.to be_a Logger
33
+ expect(Sinatra::Base.middleware.to_a).to include(
34
+ [Appsignal::Rack::SinatraBaseInstrumentation, [], nil]
35
+ )
25
36
  end
26
37
  end
27
38
 
28
- describe "middleware" do
29
- context "when AppSignal is not active" do
30
- before { allow(Appsignal).to receive(:active?).and_return(false) }
39
+ context "when not active" do
40
+ context "Appsignal.internal_logger" do
41
+ subject { Appsignal.internal_logger }
31
42
 
32
- it "does not add the instrumentation middleware to Sinatra::Base" do
43
+ it "sets a logger" do
33
44
  install_sinatra_integration
34
- expect(Sinatra::Base.middleware.to_a).to_not include(
35
- [Appsignal::Rack::SinatraBaseInstrumentation, [], nil]
36
- )
45
+ is_expected.to be_a Logger
37
46
  end
38
47
  end
39
48
 
40
- context "when AppSignal is active" do
41
- it "adds the instrumentation middleware to Sinatra::Base" do
42
- install_sinatra_integration
43
- expect(Sinatra::Base.middleware.to_a).to include(
44
- [Appsignal::Rack::SinatraBaseInstrumentation, [], nil]
45
- )
49
+ describe "middleware" do
50
+ context "when AppSignal is not active" do
51
+ it "does not add the instrumentation middleware to Sinatra::Base" do
52
+ install_sinatra_integration
53
+ expect(Sinatra::Base.middleware.to_a).to_not include(
54
+ [Appsignal::Rack::SinatraBaseInstrumentation, [], nil]
55
+ )
56
+ end
46
57
  end
47
- end
48
- end
49
-
50
- describe "environment" do
51
- subject { Appsignal.config.env }
52
58
 
53
- context "without APPSIGNAL_APP_ENV" do
54
- before { install_sinatra_integration }
59
+ context "when the new AppSignal config is active" do
60
+ it "adds the instrumentation middleware to Sinatra::Base" do
61
+ ENV["APPSIGNAL_APP_NAME"] = "My Sinatra app name"
62
+ ENV["APPSIGNAL_APP_ENV"] = "test"
63
+ ENV["APPSIGNAL_PUSH_API_KEY"] = "my-key"
55
64
 
56
- it "uses the app environment" do
57
- expect(subject).to eq("test")
65
+ install_sinatra_integration
66
+ expect(Sinatra::Base.middleware.to_a).to include(
67
+ [Appsignal::Rack::SinatraBaseInstrumentation, [], nil]
68
+ )
69
+ end
58
70
  end
59
71
  end
60
72
 
61
- context "with APPSIGNAL_APP_ENV" do
62
- before do
63
- ENV["APPSIGNAL_APP_ENV"] = "env-staging"
64
- install_sinatra_integration
73
+ describe "environment" do
74
+ subject { Appsignal.config.env }
75
+
76
+ context "without APPSIGNAL_APP_ENV" do
77
+ before { install_sinatra_integration }
78
+
79
+ it "uses the app environment" do
80
+ expect(subject).to eq("test")
81
+ end
65
82
  end
66
83
 
67
- it "uses the environment variable" do
68
- expect(subject).to eq("env-staging")
84
+ context "with APPSIGNAL_APP_ENV" do
85
+ before do
86
+ ENV["APPSIGNAL_APP_ENV"] = "env-staging"
87
+ install_sinatra_integration
88
+ end
89
+
90
+ it "uses the environment variable" do
91
+ expect(subject).to eq("env-staging")
92
+ end
69
93
  end
70
94
  end
71
95
  end
@@ -0,0 +1,250 @@
1
+ describe Appsignal::Rack::AbstractMiddleware do
2
+ let(:app) { double(:call => true) }
3
+ let(:request_path) { "/some/path" }
4
+ let(:env) do
5
+ Rack::MockRequest.env_for(
6
+ request_path,
7
+ "REQUEST_METHOD" => "GET",
8
+ :params => { "page" => 2, "query" => "lorem" }
9
+ )
10
+ end
11
+ let(:options) { {} }
12
+ let(:middleware) { Appsignal::Rack::AbstractMiddleware.new(app, options) }
13
+
14
+ before(:context) { start_agent }
15
+ around { |example| keep_transactions { example.run } }
16
+
17
+ def make_request(env)
18
+ middleware.call(env)
19
+ end
20
+
21
+ def make_request_with_error(env, error)
22
+ expect { make_request(env) }.to raise_error(error)
23
+ end
24
+
25
+ describe "#call" do
26
+ context "when appsignal is not active" do
27
+ before { allow(Appsignal).to receive(:active?).and_return(false) }
28
+
29
+ it "does not instrument requests" do
30
+ expect { make_request(env) }.to_not(change { created_transactions.count })
31
+ end
32
+
33
+ it "calls the next middleware in the stack" do
34
+ expect(app).to receive(:call).with(env)
35
+ make_request(env)
36
+ end
37
+ end
38
+
39
+ context "when appsignal is active" do
40
+ before { allow(Appsignal).to receive(:active?).and_return(true) }
41
+
42
+ it "calls the next middleware in the stack" do
43
+ make_request(env)
44
+
45
+ expect(app).to have_received(:call).with(env)
46
+ end
47
+
48
+ context "without an exception" do
49
+ it "create a transaction for the request" do
50
+ expect { make_request(env) }.to(change { created_transactions.count }.by(1))
51
+
52
+ expect(last_transaction.to_h).to include(
53
+ "namespace" => Appsignal::Transaction::HTTP_REQUEST,
54
+ "action" => nil,
55
+ "error" => nil
56
+ )
57
+ end
58
+
59
+ it "reports a process.abstract event" do
60
+ make_request(env)
61
+
62
+ expect(last_transaction.to_h).to include(
63
+ "events" => [
64
+ hash_including(
65
+ "body" => "",
66
+ "body_format" => Appsignal::EventFormatter::DEFAULT,
67
+ "count" => 1,
68
+ "name" => "process.abstract",
69
+ "title" => ""
70
+ )
71
+ ]
72
+ )
73
+ end
74
+
75
+ it "completes the transaction" do
76
+ make_request(env)
77
+ expect(last_transaction).to be_completed
78
+ end
79
+ end
80
+
81
+ context "with an exception" do
82
+ let(:error) { ExampleException.new("error message") }
83
+ before do
84
+ allow(app).to receive(:call).and_raise(error)
85
+ expect { make_request_with_error(env, error) }
86
+ .to(change { created_transactions.count }.by(1))
87
+ end
88
+
89
+ it "creates a transaction for the request and records the exception" do
90
+ expect(last_transaction.to_h).to include(
91
+ "error" => hash_including(
92
+ "name" => "ExampleException",
93
+ "message" => "error message"
94
+ )
95
+ )
96
+ end
97
+
98
+ it "completes the transaction" do
99
+ expect(last_transaction).to be_completed
100
+ end
101
+ end
102
+
103
+ context "without action name metadata" do
104
+ it "reports no action name" do
105
+ make_request(env)
106
+
107
+ expect(last_transaction.to_h).to include("action" => nil)
108
+ end
109
+ end
110
+
111
+ context "with appsignal.route env" do
112
+ before do
113
+ env["appsignal.route"] = "POST /my-route"
114
+ end
115
+
116
+ it "reports the appsignal.route value as the action name" do
117
+ make_request(env)
118
+
119
+ expect(last_transaction.to_h).to include("action" => "POST /my-route")
120
+ end
121
+ end
122
+
123
+ context "with appsignal.action env" do
124
+ before do
125
+ env["appsignal.action"] = "POST /my-action"
126
+ end
127
+
128
+ it "reports the appsignal.route value as the action name" do
129
+ make_request(env)
130
+
131
+ expect(last_transaction.to_h).to include("action" => "POST /my-action")
132
+ end
133
+ end
134
+
135
+ describe "request metadata" do
136
+ before do
137
+ env.merge("PATH_INFO" => "/some/path", "REQUEST_METHOD" => "GET")
138
+ end
139
+
140
+ it "sets request metadata" do
141
+ make_request(env)
142
+
143
+ expect(last_transaction.to_h).to include(
144
+ "metadata" => {
145
+ "method" => "GET",
146
+ "path" => "/some/path"
147
+ },
148
+ "sample_data" => hash_including(
149
+ "environment" => hash_including(
150
+ "REQUEST_METHOD" => "GET",
151
+ "PATH_INFO" => "/some/path"
152
+ # and more, but we don't need to test Rack mock defaults
153
+ )
154
+ )
155
+ )
156
+ end
157
+
158
+ it "sets request parameters" do
159
+ make_request(env)
160
+
161
+ expect(last_transaction.to_h).to include(
162
+ "sample_data" => hash_including(
163
+ "params" => hash_including(
164
+ "page" => "2",
165
+ "query" => "lorem"
166
+ )
167
+ )
168
+ )
169
+ end
170
+ end
171
+
172
+ context "with queue start header" do
173
+ let(:queue_start_time) { fixed_time * 1_000 }
174
+ before do
175
+ env["HTTP_X_REQUEST_START"] = "t=#{queue_start_time.to_i}" # in milliseconds
176
+ end
177
+
178
+ it "sets the queue start" do
179
+ make_request(env)
180
+
181
+ expect(last_transaction.ext.queue_start).to eq(queue_start_time)
182
+ end
183
+ end
184
+
185
+ class FilteredRequest
186
+ attr_reader :env
187
+
188
+ def initialize(env)
189
+ @env = env
190
+ end
191
+
192
+ def path
193
+ "/static/path"
194
+ end
195
+
196
+ def request_method
197
+ "GET"
198
+ end
199
+
200
+ def filtered_params
201
+ { "abc" => "123" }
202
+ end
203
+ end
204
+
205
+ context "with overridden request class and params method" do
206
+ let(:options) do
207
+ { :request_class => FilteredRequest, :params_method => :filtered_params }
208
+ end
209
+
210
+ it "uses the overridden request class and params method to fetch params" do
211
+ make_request(env)
212
+
213
+ expect(last_transaction.to_h).to include(
214
+ "sample_data" => hash_including(
215
+ "params" => { "abc" => "123" }
216
+ )
217
+ )
218
+ end
219
+ end
220
+
221
+ context "with parent instrumentation" do
222
+ before do
223
+ env[Appsignal::Rack::APPSIGNAL_TRANSACTION] = http_request_transaction
224
+ end
225
+
226
+ it "uses the existing transaction" do
227
+ make_request(env)
228
+
229
+ expect { make_request(env) }.to_not(change { created_transactions.count })
230
+ end
231
+
232
+ it "doesn't complete the existing transaction" do
233
+ make_request(env)
234
+
235
+ expect(env[Appsignal::Rack::APPSIGNAL_TRANSACTION]).to_not be_completed
236
+ end
237
+
238
+ context "with custom set action name" do
239
+ it "does not overwrite the action name" do
240
+ env[Appsignal::Rack::APPSIGNAL_TRANSACTION].set_action("My custom action")
241
+ env["appsignal.action"] = "POST /my-action"
242
+ make_request(env)
243
+
244
+ expect(last_transaction.to_h).to include("action" => "My custom action")
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
@@ -11,6 +11,7 @@ describe Appsignal::Rack::EventHandler do
11
11
  let(:response) { nil }
12
12
  let(:log_stream) { StringIO.new }
13
13
  let(:log) { log_contents(log_stream) }
14
+ let(:event_handler_instance) { described_class.new }
14
15
  before do
15
16
  start_agent
16
17
  Appsignal.internal_logger = test_logger(log_stream)
@@ -18,7 +19,7 @@ describe Appsignal::Rack::EventHandler do
18
19
  around { |example| keep_transactions { example.run } }
19
20
 
20
21
  def on_start
21
- described_class.new.on_start(request, response)
22
+ event_handler_instance.on_start(request, response)
22
23
  end
23
24
 
24
25
  describe "#on_start" do
@@ -34,6 +35,14 @@ describe Appsignal::Rack::EventHandler do
34
35
  expect(Appsignal::Transaction.current).to eq(last_transaction)
35
36
  end
36
37
 
38
+ context "when the handler is nested in another EventHandler" do
39
+ it "does not create a new transaction in the nested EventHandler" do
40
+ on_start
41
+ expect { described_class.new.on_start(request, response) }
42
+ .to_not(change { created_transactions.length })
43
+ end
44
+ end
45
+
37
46
  it "registers transaction on the request environment" do
38
47
  on_start
39
48
 
@@ -87,7 +96,7 @@ describe Appsignal::Rack::EventHandler do
87
96
 
88
97
  describe "#on_error" do
89
98
  def on_error(error)
90
- described_class.new.on_error(request, response, error)
99
+ event_handler_instance.on_error(request, response, error)
91
100
  end
92
101
 
93
102
  it "reports the error" do
@@ -103,6 +112,15 @@ describe Appsignal::Rack::EventHandler do
103
112
  )
104
113
  end
105
114
 
115
+ context "when the handler is nested in another EventHandler" do
116
+ it "does not report the error on the transaction" do
117
+ on_start
118
+ described_class.new.on_error(request, response, ExampleStandardError.new("the error"))
119
+
120
+ expect(last_transaction.to_h).to include("error" => nil)
121
+ end
122
+ end
123
+
106
124
  it "logs an error in case of an internal error" do
107
125
  on_start
108
126
 
@@ -122,7 +140,7 @@ describe Appsignal::Rack::EventHandler do
122
140
  let(:response) { Rack::Events::BufferedResponse.new(200, {}, ["body"]) }
123
141
 
124
142
  def on_finish
125
- described_class.new.on_finish(request, response)
143
+ event_handler_instance.on_finish(request, response)
126
144
  end
127
145
 
128
146
  it "doesn't do anything without a transaction" do
@@ -155,6 +173,22 @@ describe Appsignal::Rack::EventHandler do
155
173
  )
156
174
  )
157
175
  expect(last_transaction.ext.queue_start).to eq(queue_start_time)
176
+ expect(last_transaction).to be_completed
177
+ end
178
+
179
+ context "when the handler is nested in another EventHandler" do
180
+ it "does not complete the transaction" do
181
+ on_start
182
+ described_class.new.on_finish(request, response)
183
+
184
+ expect(last_transaction.to_h).to include(
185
+ "action" => nil,
186
+ "metadata" => {},
187
+ "sample_data" => {},
188
+ "events" => []
189
+ )
190
+ expect(last_transaction).to_not be_completed
191
+ end
158
192
  end
159
193
 
160
194
  it "doesn't set the action name if already set" do
@@ -183,6 +217,25 @@ describe Appsignal::Rack::EventHandler do
183
217
  )
184
218
  end
185
219
 
220
+ it "sets the response status as a tag" do
221
+ on_start
222
+ on_finish
223
+
224
+ expect(last_transaction.to_h).to include(
225
+ "sample_data" => hash_including(
226
+ "tags" => { "response_status" => 200 }
227
+ )
228
+ )
229
+ end
230
+
231
+ it "increments the response status counter for response status" do
232
+ expect(Appsignal).to receive(:increment_counter)
233
+ .with(:response_status, 1, :status => 200, :namespace => :web)
234
+
235
+ on_start
236
+ on_finish
237
+ end
238
+
186
239
  it "logs an error in case of an error" do
187
240
  expect(Appsignal::Transaction)
188
241
  .to receive(:complete_current!).and_raise(ExampleStandardError, "oh no")