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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -0
- data/lib/appsignal/config.rb +10 -2
- data/lib/appsignal/hooks/sidekiq.rb +18 -1
- data/lib/appsignal/integrations/sidekiq.rb +37 -18
- data/lib/appsignal/integrations/sinatra.rb +9 -7
- data/lib/appsignal/rack/abstract_middleware.rb +127 -0
- data/lib/appsignal/rack/event_handler.rb +29 -1
- data/lib/appsignal/rack/generic_instrumentation.rb +6 -33
- data/lib/appsignal/rack/sinatra_instrumentation.rb +16 -42
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +1 -0
- data/spec/lib/appsignal/config_spec.rb +28 -0
- data/spec/lib/appsignal/integrations/sidekiq_spec.rb +175 -17
- data/spec/lib/appsignal/integrations/sinatra_spec.rb +57 -33
- data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +250 -0
- data/spec/lib/appsignal/rack/event_handler_spec.rb +56 -3
- data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +26 -79
- data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +80 -164
- data/support/install_deps +6 -0
- metadata +4 -2
@@ -1,19 +1,83 @@
|
|
1
1
|
require "appsignal/integrations/sidekiq"
|
2
2
|
|
3
|
-
describe Appsignal::Integrations::
|
4
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
25
|
-
|
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
|
29
|
-
"
|
30
|
-
"
|
31
|
-
|
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
|
16
|
+
before do
|
17
|
+
Appsignal.config = nil
|
18
|
+
end
|
17
19
|
after { uninstall_sinatra_integration }
|
18
20
|
|
19
|
-
context "
|
20
|
-
|
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 "
|
31
|
+
it "adds the instrumentation middleware to Sinatra::Base" do
|
23
32
|
install_sinatra_integration
|
24
|
-
|
33
|
+
expect(Sinatra::Base.middleware.to_a).to include(
|
34
|
+
[Appsignal::Rack::SinatraBaseInstrumentation, [], nil]
|
35
|
+
)
|
25
36
|
end
|
26
37
|
end
|
27
38
|
|
28
|
-
|
29
|
-
context "
|
30
|
-
|
39
|
+
context "when not active" do
|
40
|
+
context "Appsignal.internal_logger" do
|
41
|
+
subject { Appsignal.internal_logger }
|
31
42
|
|
32
|
-
it "
|
43
|
+
it "sets a logger" do
|
33
44
|
install_sinatra_integration
|
34
|
-
|
35
|
-
[Appsignal::Rack::SinatraBaseInstrumentation, [], nil]
|
36
|
-
)
|
45
|
+
is_expected.to be_a Logger
|
37
46
|
end
|
38
47
|
end
|
39
48
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
54
|
-
|
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
|
-
|
57
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
-
|
68
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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")
|