appsignal 3.4.13 → 3.6.1
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/.semaphore/semaphore.yml +180 -14
- data/CHANGELOG.md +164 -0
- data/README.md +2 -0
- data/Rakefile +3 -1
- data/build_matrix.yml +7 -13
- data/ext/Rakefile +8 -1
- data/ext/agent.rb +27 -27
- data/ext/appsignal_extension.c +0 -24
- data/ext/base.rb +4 -1
- data/gemfiles/redis-4.gemfile +5 -0
- data/gemfiles/redis-5.gemfile +6 -0
- data/lib/appsignal/cli/diagnose/paths.rb +33 -10
- data/lib/appsignal/cli/diagnose.rb +6 -1
- data/lib/appsignal/config.rb +19 -5
- data/lib/appsignal/demo.rb +1 -1
- data/lib/appsignal/environment.rb +24 -13
- data/lib/appsignal/event_formatter.rb +1 -1
- data/lib/appsignal/extension/jruby.rb +4 -17
- data/lib/appsignal/extension.rb +1 -1
- data/lib/appsignal/helpers/instrumentation.rb +10 -10
- data/lib/appsignal/helpers/metrics.rb +15 -13
- data/lib/appsignal/hooks/active_job.rb +9 -1
- data/lib/appsignal/hooks/redis.rb +1 -0
- data/lib/appsignal/hooks/redis_client.rb +27 -0
- data/lib/appsignal/hooks.rb +3 -2
- data/lib/appsignal/integrations/hanami.rb +1 -1
- data/lib/appsignal/integrations/padrino.rb +1 -1
- data/lib/appsignal/integrations/railtie.rb +1 -1
- data/lib/appsignal/integrations/redis_client.rb +20 -0
- data/lib/appsignal/integrations/sidekiq.rb +2 -2
- data/lib/appsignal/integrations/sinatra.rb +1 -1
- data/lib/appsignal/logger.rb +2 -0
- data/lib/appsignal/minutely.rb +4 -4
- data/lib/appsignal/probes/gvl.rb +1 -1
- data/lib/appsignal/probes/helpers.rb +1 -1
- data/lib/appsignal/probes/mri.rb +1 -1
- data/lib/appsignal/probes/sidekiq.rb +10 -8
- data/lib/appsignal/rack/body_wrapper.rb +161 -0
- data/lib/appsignal/rack/generic_instrumentation.rb +18 -5
- data/lib/appsignal/rack/rails_instrumentation.rb +17 -5
- data/lib/appsignal/rack/sinatra_instrumentation.rb +17 -5
- data/lib/appsignal/rack/streaming_listener.rb +27 -36
- data/lib/appsignal/span.rb +2 -2
- data/lib/appsignal/transaction.rb +46 -10
- data/lib/appsignal/utils/deprecation_message.rb +2 -2
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +38 -31
- data/resources/cacert.pem +321 -159
- data/spec/lib/appsignal/cli/diagnose/utils_spec.rb +11 -0
- data/spec/lib/appsignal/cli/diagnose_spec.rb +38 -12
- data/spec/lib/appsignal/config_spec.rb +3 -2
- data/spec/lib/appsignal/hooks/activejob_spec.rb +26 -1
- data/spec/lib/appsignal/hooks/redis_client_spec.rb +222 -0
- data/spec/lib/appsignal/hooks/redis_spec.rb +98 -76
- data/spec/lib/appsignal/hooks_spec.rb +4 -4
- data/spec/lib/appsignal/integrations/railtie_spec.rb +2 -2
- data/spec/lib/appsignal/integrations/sidekiq_spec.rb +3 -3
- data/spec/lib/appsignal/integrations/sinatra_spec.rb +2 -2
- data/spec/lib/appsignal/minutely_spec.rb +2 -2
- data/spec/lib/appsignal/probes/sidekiq_spec.rb +29 -6
- data/spec/lib/appsignal/rack/body_wrapper_spec.rb +220 -0
- data/spec/lib/appsignal/rack/generic_instrumentation_spec.rb +3 -2
- data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +5 -3
- data/spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb +3 -1
- data/spec/lib/appsignal/rack/streaming_listener_spec.rb +9 -53
- data/spec/lib/appsignal/transaction_spec.rb +95 -2
- data/spec/lib/appsignal_spec.rb +62 -60
- data/spec/spec_helper.rb +1 -1
- data/spec/support/fixtures/projects/valid/config/appsignal.yml +3 -3
- data/spec/support/helpers/config_helpers.rb +6 -2
- data/spec/support/helpers/dependency_helper.rb +9 -1
- data/spec/support/helpers/log_helpers.rb +2 -2
- metadata +9 -2
@@ -0,0 +1,220 @@
|
|
1
|
+
describe Appsignal::Rack::BodyWrapper do
|
2
|
+
let(:nil_txn) { Appsignal::Transaction::NilTransaction.new }
|
3
|
+
|
4
|
+
describe "with a body that supports all possible features" do
|
5
|
+
it "reduces the supported methods to just each()" do
|
6
|
+
# which is the safest thing to do, since the body is likely broken
|
7
|
+
fake_body = double(:each => nil, :call => nil, :to_ary => [], :to_path => "/tmp/foo.bin",
|
8
|
+
:close => nil)
|
9
|
+
wrapped = described_class.wrap(fake_body, nil_txn)
|
10
|
+
expect(wrapped).to respond_to(:each)
|
11
|
+
expect(wrapped).not_to respond_to(:to_ary)
|
12
|
+
expect(wrapped).not_to respond_to(:call)
|
13
|
+
expect(wrapped).to respond_to(:close)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "with a body only supporting each()" do
|
18
|
+
it "wraps with appropriate class" do
|
19
|
+
fake_body = double
|
20
|
+
allow(fake_body).to receive(:each)
|
21
|
+
|
22
|
+
wrapped = described_class.wrap(fake_body, nil_txn)
|
23
|
+
expect(wrapped).to respond_to(:each)
|
24
|
+
expect(wrapped).not_to respond_to(:to_ary)
|
25
|
+
expect(wrapped).not_to respond_to(:call)
|
26
|
+
expect(wrapped).to respond_to(:close)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "reads out the body in full using each" do
|
30
|
+
fake_body = double
|
31
|
+
expect(fake_body).to receive(:each).once.and_yield("a").and_yield("b").and_yield("c")
|
32
|
+
wrapped = described_class.wrap(fake_body, nil_txn)
|
33
|
+
expect { |b| wrapped.each(&b) }.to yield_successive_args("a", "b", "c")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "returns an Enumerator if each() gets called without a block" do
|
37
|
+
fake_body = double
|
38
|
+
expect(fake_body).to receive(:each).once.and_yield("a").and_yield("b").and_yield("c")
|
39
|
+
|
40
|
+
wrapped = described_class.wrap(fake_body, nil_txn)
|
41
|
+
enum = wrapped.each
|
42
|
+
expect(enum).to be_kind_of(Enumerator)
|
43
|
+
expect { |b| enum.each(&b) }.to yield_successive_args("a", "b", "c")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "sets the exception raised inside each() into the Appsignal transaction" do
|
47
|
+
fake_body = double
|
48
|
+
expect(fake_body).to receive(:each).once.and_raise(Exception.new("Oops"))
|
49
|
+
|
50
|
+
txn = double("Appsignal transaction", "nil_transaction?" => false)
|
51
|
+
expect(txn).to receive(:set_error).once.with(instance_of(Exception))
|
52
|
+
|
53
|
+
wrapped = described_class.wrap(fake_body, txn)
|
54
|
+
expect do
|
55
|
+
expect { |b| wrapped.each(&b) }.to yield_control
|
56
|
+
end.to raise_error(/Oops/)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "closes the body and the transaction when it gets closed" do
|
60
|
+
fake_body = double
|
61
|
+
expect(fake_body).to receive(:each).once.and_yield("a").and_yield("b").and_yield("c")
|
62
|
+
|
63
|
+
txn = double("Appsignal transaction", "nil_transaction?" => false)
|
64
|
+
expect(Appsignal::Transaction).to receive(:complete_current!).once
|
65
|
+
|
66
|
+
wrapped = described_class.wrap(fake_body, txn)
|
67
|
+
expect { |b| wrapped.each(&b) }.to yield_successive_args("a", "b", "c")
|
68
|
+
expect { wrapped.close }.not_to raise_error
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "with a body supporting both each() and call" do
|
73
|
+
it "wraps with the wrapper that conceals call() and exposes each" do
|
74
|
+
fake_body = double
|
75
|
+
allow(fake_body).to receive(:each)
|
76
|
+
allow(fake_body).to receive(:call)
|
77
|
+
|
78
|
+
wrapped = described_class.wrap(fake_body, nil_txn)
|
79
|
+
expect(wrapped).to respond_to(:each)
|
80
|
+
expect(wrapped).not_to respond_to(:to_ary)
|
81
|
+
expect(wrapped).not_to respond_to(:call)
|
82
|
+
expect(wrapped).not_to respond_to(:to_path)
|
83
|
+
expect(wrapped).to respond_to(:close)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "with a body supporting both to_ary and each" do
|
88
|
+
let(:fake_body) { double(:each => nil, :to_ary => []) }
|
89
|
+
it "wraps with appropriate class" do
|
90
|
+
wrapped = described_class.wrap(fake_body, nil_txn)
|
91
|
+
expect(wrapped).to respond_to(:each)
|
92
|
+
expect(wrapped).to respond_to(:to_ary)
|
93
|
+
expect(wrapped).not_to respond_to(:call)
|
94
|
+
expect(wrapped).not_to respond_to(:to_path)
|
95
|
+
expect(wrapped).to respond_to(:close)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "reads out the body in full using each" do
|
99
|
+
expect(fake_body).to receive(:each).once.and_yield("a").and_yield("b").and_yield("c")
|
100
|
+
|
101
|
+
wrapped = described_class.wrap(fake_body, nil_txn)
|
102
|
+
expect { |b| wrapped.each(&b) }.to yield_successive_args("a", "b", "c")
|
103
|
+
end
|
104
|
+
|
105
|
+
it "sets the exception raised inside each() into the Appsignal transaction" do
|
106
|
+
expect(fake_body).to receive(:each).once.and_raise(Exception.new("Oops"))
|
107
|
+
|
108
|
+
txn = double("Appsignal transaction", "nil_transaction?" => false)
|
109
|
+
expect(txn).to receive(:set_error).once.with(instance_of(Exception))
|
110
|
+
|
111
|
+
wrapped = described_class.wrap(fake_body, txn)
|
112
|
+
expect do
|
113
|
+
expect { |b| wrapped.each(&b) }.to yield_control
|
114
|
+
end.to raise_error(/Oops/)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "reads out the body in full using to_ary" do
|
118
|
+
expect(fake_body).to receive(:to_ary).and_return(["one", "two", "three"])
|
119
|
+
|
120
|
+
wrapped = described_class.wrap(fake_body, nil_txn)
|
121
|
+
expect(wrapped.to_ary).to eq(["one", "two", "three"])
|
122
|
+
end
|
123
|
+
|
124
|
+
it "sends the exception raised inside to_ary() into the Appsignal and closes txn" do
|
125
|
+
fake_body = double
|
126
|
+
allow(fake_body).to receive(:each)
|
127
|
+
expect(fake_body).to receive(:to_ary).once.and_raise(Exception.new("Oops"))
|
128
|
+
expect(fake_body).not_to receive(:close) # Per spec we expect the body has closed itself
|
129
|
+
|
130
|
+
txn = double("Appsignal transaction", "nil_transaction?" => false)
|
131
|
+
expect(txn).to receive(:set_error).once.with(instance_of(Exception))
|
132
|
+
expect(Appsignal::Transaction).to receive(:complete_current!).once
|
133
|
+
|
134
|
+
wrapped = described_class.wrap(fake_body, txn)
|
135
|
+
expect { wrapped.to_ary }.to raise_error(/Oops/)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "with a body supporting both to_path and each" do
|
140
|
+
let(:fake_body) { double(:each => nil, :to_path => nil) }
|
141
|
+
|
142
|
+
it "wraps with appropriate class" do
|
143
|
+
wrapped = described_class.wrap(fake_body, nil_txn)
|
144
|
+
expect(wrapped).to respond_to(:each)
|
145
|
+
expect(wrapped).not_to respond_to(:to_ary)
|
146
|
+
expect(wrapped).not_to respond_to(:call)
|
147
|
+
expect(wrapped).to respond_to(:to_path)
|
148
|
+
expect(wrapped).to respond_to(:close)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "reads out the body in full using each()" do
|
152
|
+
expect(fake_body).to receive(:each).once.and_yield("a").and_yield("b").and_yield("c")
|
153
|
+
|
154
|
+
wrapped = described_class.wrap(fake_body, nil_txn)
|
155
|
+
expect { |b| wrapped.each(&b) }.to yield_successive_args("a", "b", "c")
|
156
|
+
end
|
157
|
+
|
158
|
+
it "sets the exception raised inside each() into the Appsignal transaction" do
|
159
|
+
expect(fake_body).to receive(:each).once.and_raise(Exception.new("Oops"))
|
160
|
+
|
161
|
+
txn = double("Appsignal transaction", "nil_transaction?" => false)
|
162
|
+
expect(txn).to receive(:set_error).once.with(instance_of(Exception))
|
163
|
+
|
164
|
+
wrapped = described_class.wrap(fake_body, txn)
|
165
|
+
expect do
|
166
|
+
expect { |b| wrapped.each(&b) }.to yield_control
|
167
|
+
end.to raise_error(/Oops/)
|
168
|
+
end
|
169
|
+
|
170
|
+
it "sets the exception raised inside to_path() into the Appsignal transaction" do
|
171
|
+
allow(fake_body).to receive(:to_path).once.and_raise(Exception.new("Oops"))
|
172
|
+
|
173
|
+
txn = double("Appsignal transaction", "nil_transaction?" => false)
|
174
|
+
expect(txn).to receive(:set_error).once.with(instance_of(Exception))
|
175
|
+
expect(txn).not_to receive(:complete) # gets called by the caller via close()
|
176
|
+
|
177
|
+
wrapped = described_class.wrap(fake_body, txn)
|
178
|
+
expect { wrapped.to_path }.to raise_error(/Oops/)
|
179
|
+
end
|
180
|
+
|
181
|
+
it "exposes to_path to the sender" do
|
182
|
+
allow(fake_body).to receive(:to_path).and_return("/tmp/file.bin")
|
183
|
+
|
184
|
+
wrapped = described_class.wrap(fake_body, nil_txn)
|
185
|
+
expect(wrapped.to_path).to eq("/tmp/file.bin")
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe "with a body only supporting call()" do
|
190
|
+
let(:fake_body) { double(:call => nil) }
|
191
|
+
it "wraps with appropriate class" do
|
192
|
+
wrapped = described_class.wrap(fake_body, nil_txn)
|
193
|
+
expect(wrapped).not_to respond_to(:each)
|
194
|
+
expect(wrapped).not_to respond_to(:to_ary)
|
195
|
+
expect(wrapped).to respond_to(:call)
|
196
|
+
expect(wrapped).not_to respond_to(:to_path)
|
197
|
+
expect(wrapped).to respond_to(:close)
|
198
|
+
end
|
199
|
+
|
200
|
+
it "passes the stream into the call() of the body" do
|
201
|
+
fake_rack_stream = double("stream")
|
202
|
+
expect(fake_body).to receive(:call).with(fake_rack_stream)
|
203
|
+
|
204
|
+
wrapped = described_class.wrap(fake_body, nil_txn)
|
205
|
+
expect { wrapped.call(fake_rack_stream) }.not_to raise_error
|
206
|
+
end
|
207
|
+
|
208
|
+
it "sets the exception raised inside call() into the Appsignal transaction" do
|
209
|
+
fake_rack_stream = double
|
210
|
+
allow(fake_body).to receive(:call).with(fake_rack_stream).and_raise(Exception.new("Oopsie"))
|
211
|
+
|
212
|
+
txn = double("Appsignal transaction", "nil_transaction?" => false)
|
213
|
+
expect(txn).to receive(:set_error).once.with(instance_of(Exception))
|
214
|
+
expect(txn).not_to receive(:complete) # gets called by the caller via close()
|
215
|
+
wrapped = described_class.wrap(fake_body, txn)
|
216
|
+
|
217
|
+
expect { wrapped.call(fake_rack_stream) }.to raise_error(/Oopsie/)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -50,7 +50,7 @@ describe Appsignal::Rack::GenericInstrumentation do
|
|
50
50
|
expect(app).to receive(:call).with(env)
|
51
51
|
end
|
52
52
|
|
53
|
-
context "with an exception", :error => true do
|
53
|
+
context "with an exception raised from call()", :error => true do
|
54
54
|
let(:error) { ExampleException }
|
55
55
|
let(:app) do
|
56
56
|
double.tap do |d|
|
@@ -58,8 +58,9 @@ describe Appsignal::Rack::GenericInstrumentation do
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
it "records the exception" do
|
61
|
+
it "records the exception and completes the transaction" do
|
62
62
|
expect_any_instance_of(Appsignal::Transaction).to receive(:set_error).with(error)
|
63
|
+
expect(Appsignal::Transaction).to receive(:complete_current!)
|
63
64
|
end
|
64
65
|
end
|
65
66
|
|
@@ -6,7 +6,7 @@ if DependencyHelper.rails_present?
|
|
6
6
|
let(:log) { StringIO.new }
|
7
7
|
before do
|
8
8
|
start_agent
|
9
|
-
Appsignal.
|
9
|
+
Appsignal.internal_logger = test_logger(log)
|
10
10
|
end
|
11
11
|
|
12
12
|
let(:params) do
|
@@ -65,7 +65,8 @@ if DependencyHelper.rails_present?
|
|
65
65
|
|
66
66
|
describe "#call_with_appsignal_monitoring" do
|
67
67
|
def run
|
68
|
-
middleware.call(env)
|
68
|
+
_status, _headers, body = middleware.call(env)
|
69
|
+
body.close # Rack will always call close() on the body
|
69
70
|
end
|
70
71
|
|
71
72
|
it "calls the wrapped app" do
|
@@ -126,7 +127,8 @@ if DependencyHelper.rails_present?
|
|
126
127
|
end
|
127
128
|
end
|
128
129
|
|
129
|
-
it "records the exception" do
|
130
|
+
it "records the exception and completes the transaction" do
|
131
|
+
expect(Appsignal::Transaction).to receive(:complete_current!)
|
130
132
|
expect { run }.to raise_error(error)
|
131
133
|
|
132
134
|
transaction_hash = last_transaction.to_h
|
@@ -3,7 +3,9 @@ if DependencyHelper.sinatra_present?
|
|
3
3
|
|
4
4
|
module SinatraRequestHelpers
|
5
5
|
def make_request(env)
|
6
|
-
middleware.call(env)
|
6
|
+
_status, _headers, body = middleware.call(env)
|
7
|
+
# Close the body so that the transaction gets completed
|
8
|
+
body&.close
|
7
9
|
end
|
8
10
|
|
9
11
|
def make_request_with_error(env, error)
|
@@ -90,10 +90,11 @@ describe Appsignal::Rack::StreamingListener do
|
|
90
90
|
context "with an exception in the instrumentation call" do
|
91
91
|
let(:error) { ExampleException }
|
92
92
|
|
93
|
-
it "should add the exception to the transaction" do
|
93
|
+
it "should add the exception to the transaction and complete the transaction" do
|
94
94
|
allow(app).to receive(:call).and_raise(error)
|
95
95
|
|
96
96
|
expect(transaction).to receive(:set_error).with(error)
|
97
|
+
expect(Appsignal::Transaction).to receive(:complete_current!).and_call_original
|
97
98
|
|
98
99
|
expect do
|
99
100
|
listener.call_with_appsignal_monitoring(env)
|
@@ -101,64 +102,19 @@ describe Appsignal::Rack::StreamingListener do
|
|
101
102
|
end
|
102
103
|
end
|
103
104
|
|
104
|
-
it "should wrap the body in a wrapper" do
|
105
|
-
expect(Appsignal::StreamWrapper).to receive(:new)
|
106
|
-
.with("body", transaction)
|
107
|
-
.and_return(wrapper)
|
108
|
-
|
105
|
+
it "should wrap the response body in a wrapper" do
|
109
106
|
body = listener.call_with_appsignal_monitoring(env)[2]
|
110
107
|
|
111
|
-
expect(body).to
|
108
|
+
expect(body).to be_kind_of(Appsignal::Rack::BodyWrapper)
|
112
109
|
end
|
113
110
|
end
|
114
111
|
end
|
115
112
|
|
116
113
|
describe Appsignal::StreamWrapper do
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
describe "#each" do
|
124
|
-
it "calls the original stream" do
|
125
|
-
expect(stream).to receive(:each)
|
126
|
-
|
127
|
-
wrapper.each
|
128
|
-
end
|
129
|
-
|
130
|
-
context "when #each raises an error" do
|
131
|
-
let(:error) { ExampleException }
|
132
|
-
|
133
|
-
it "records the exception" do
|
134
|
-
allow(stream).to receive(:each).and_raise(error)
|
135
|
-
|
136
|
-
expect(transaction).to receive(:set_error).with(error)
|
137
|
-
|
138
|
-
expect { wrapper.send(:each) }.to raise_error(error)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
describe "#close" do
|
144
|
-
it "closes the original stream and completes the transaction" do
|
145
|
-
expect(stream).to receive(:close)
|
146
|
-
expect(Appsignal::Transaction).to receive(:complete_current!)
|
147
|
-
|
148
|
-
wrapper.close
|
149
|
-
end
|
150
|
-
|
151
|
-
context "when #close raises an error" do
|
152
|
-
let(:error) { ExampleException }
|
153
|
-
|
154
|
-
it "records the exception and completes the transaction" do
|
155
|
-
allow(stream).to receive(:close).and_raise(error)
|
156
|
-
|
157
|
-
expect(transaction).to receive(:set_error).with(error)
|
158
|
-
expect(transaction).to receive(:complete)
|
159
|
-
|
160
|
-
expect { wrapper.send(:close) }.to raise_error(error)
|
161
|
-
end
|
162
|
-
end
|
114
|
+
it ".new returns an EnumerableWrapper" do
|
115
|
+
fake_body = double(:each => nil)
|
116
|
+
fake_txn = double
|
117
|
+
stream_wrapper = Appsignal::StreamWrapper.new(fake_body, fake_txn)
|
118
|
+
expect(stream_wrapper).to be_kind_of(Appsignal::Rack::EnumerableBodyWrapper)
|
163
119
|
end
|
164
120
|
end
|
@@ -580,7 +580,7 @@ describe Appsignal::Transaction do
|
|
580
580
|
it "does not raise an error when the queue start is too big" do
|
581
581
|
expect(transaction.ext).to receive(:set_queue_start).and_raise(RangeError)
|
582
582
|
|
583
|
-
expect(Appsignal.
|
583
|
+
expect(Appsignal.internal_logger).to receive(:warn).with("Queue start value 10 is too big")
|
584
584
|
|
585
585
|
expect do
|
586
586
|
transaction.set_queue_start(10)
|
@@ -766,7 +766,7 @@ describe Appsignal::Transaction do
|
|
766
766
|
let(:error) { Object.new }
|
767
767
|
|
768
768
|
it "does not add the error" do
|
769
|
-
expect(Appsignal.
|
769
|
+
expect(Appsignal.internal_logger).to receive(:error).with(
|
770
770
|
"Appsignal::Transaction#set_error: Cannot set error. " \
|
771
771
|
"The given value is not an exception: #{error.inspect}"
|
772
772
|
)
|
@@ -788,6 +788,99 @@ describe Appsignal::Transaction do
|
|
788
788
|
end
|
789
789
|
end
|
790
790
|
|
791
|
+
context "when the error has no causes" do
|
792
|
+
it "should not send the causes information as sample data" do
|
793
|
+
expect(transaction.ext).to_not receive(:set_sample_data)
|
794
|
+
|
795
|
+
transaction.set_error(error)
|
796
|
+
end
|
797
|
+
end
|
798
|
+
|
799
|
+
context "when the error has multiple causes" do
|
800
|
+
let(:error) do
|
801
|
+
e = ExampleStandardError.new("test message")
|
802
|
+
e2 = RuntimeError.new("cause message")
|
803
|
+
e3 = StandardError.new("cause message 2")
|
804
|
+
allow(e).to receive(:backtrace).and_return(["line 1"])
|
805
|
+
allow(e).to receive(:cause).and_return(e2)
|
806
|
+
allow(e2).to receive(:cause).and_return(e3)
|
807
|
+
e
|
808
|
+
end
|
809
|
+
|
810
|
+
it "sends the causes information as sample data" do
|
811
|
+
expect(transaction.ext).to receive(:set_error).with(
|
812
|
+
"ExampleStandardError",
|
813
|
+
"test message",
|
814
|
+
Appsignal::Utils::Data.generate(["line 1"])
|
815
|
+
)
|
816
|
+
|
817
|
+
expect(transaction.ext).to receive(:set_sample_data).with(
|
818
|
+
"error_causes",
|
819
|
+
Appsignal::Utils::Data.generate(
|
820
|
+
[
|
821
|
+
{
|
822
|
+
:name => "RuntimeError",
|
823
|
+
:message => "cause message"
|
824
|
+
},
|
825
|
+
{
|
826
|
+
:name => "StandardError",
|
827
|
+
:message => "cause message 2"
|
828
|
+
}
|
829
|
+
]
|
830
|
+
)
|
831
|
+
)
|
832
|
+
|
833
|
+
expect(Appsignal.internal_logger).to_not receive(:debug)
|
834
|
+
|
835
|
+
transaction.set_error(error)
|
836
|
+
end
|
837
|
+
end
|
838
|
+
|
839
|
+
context "when the error has too many causes" do
|
840
|
+
let(:error) do
|
841
|
+
e = ExampleStandardError.new("root cause error")
|
842
|
+
|
843
|
+
11.times do |i|
|
844
|
+
next_e = ExampleStandardError.new("wrapper error #{i}")
|
845
|
+
allow(next_e).to receive(:cause).and_return(e)
|
846
|
+
e = next_e
|
847
|
+
end
|
848
|
+
|
849
|
+
allow(e).to receive(:backtrace).and_return(["line 1"])
|
850
|
+
e
|
851
|
+
end
|
852
|
+
|
853
|
+
it "sends only the first causes as sample data" do
|
854
|
+
expect(transaction.ext).to receive(:set_error).with(
|
855
|
+
"ExampleStandardError",
|
856
|
+
"wrapper error 10",
|
857
|
+
Appsignal::Utils::Data.generate(["line 1"])
|
858
|
+
)
|
859
|
+
|
860
|
+
expected_error_causes = Array.new(10) do |i|
|
861
|
+
{
|
862
|
+
:name => "ExampleStandardError",
|
863
|
+
:message => "wrapper error #{9 - i}"
|
864
|
+
}
|
865
|
+
end
|
866
|
+
|
867
|
+
expected_error_causes.last[:is_root_cause] = false
|
868
|
+
|
869
|
+
expect(transaction.ext).to receive(:set_sample_data).with(
|
870
|
+
"error_causes",
|
871
|
+
Appsignal::Utils::Data.generate(expected_error_causes)
|
872
|
+
)
|
873
|
+
|
874
|
+
expect(Appsignal.internal_logger).to receive(:debug).with(
|
875
|
+
"Appsignal::Transaction#set_error: Error has more " \
|
876
|
+
"than 10 error causes. Only the first 10 " \
|
877
|
+
"will be reported."
|
878
|
+
)
|
879
|
+
|
880
|
+
transaction.set_error(error)
|
881
|
+
end
|
882
|
+
end
|
883
|
+
|
791
884
|
context "when error message is nil" do
|
792
885
|
let(:error) do
|
793
886
|
e = ExampleStandardError.new
|