airbrake-ruby 4.6.0
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 +7 -0
- data/lib/airbrake-ruby.rb +513 -0
- data/lib/airbrake-ruby/async_sender.rb +142 -0
- data/lib/airbrake-ruby/backtrace.rb +196 -0
- data/lib/airbrake-ruby/benchmark.rb +39 -0
- data/lib/airbrake-ruby/code_hunk.rb +51 -0
- data/lib/airbrake-ruby/config.rb +229 -0
- data/lib/airbrake-ruby/config/validator.rb +91 -0
- data/lib/airbrake-ruby/deploy_notifier.rb +36 -0
- data/lib/airbrake-ruby/file_cache.rb +48 -0
- data/lib/airbrake-ruby/filter_chain.rb +95 -0
- data/lib/airbrake-ruby/filters/context_filter.rb +29 -0
- data/lib/airbrake-ruby/filters/dependency_filter.rb +31 -0
- data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +46 -0
- data/lib/airbrake-ruby/filters/gem_root_filter.rb +33 -0
- data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +92 -0
- data/lib/airbrake-ruby/filters/git_repository_filter.rb +64 -0
- data/lib/airbrake-ruby/filters/git_revision_filter.rb +66 -0
- data/lib/airbrake-ruby/filters/keys_blacklist.rb +49 -0
- data/lib/airbrake-ruby/filters/keys_filter.rb +140 -0
- data/lib/airbrake-ruby/filters/keys_whitelist.rb +48 -0
- data/lib/airbrake-ruby/filters/root_directory_filter.rb +28 -0
- data/lib/airbrake-ruby/filters/sql_filter.rb +104 -0
- data/lib/airbrake-ruby/filters/system_exit_filter.rb +23 -0
- data/lib/airbrake-ruby/filters/thread_filter.rb +92 -0
- data/lib/airbrake-ruby/hash_keyable.rb +37 -0
- data/lib/airbrake-ruby/ignorable.rb +44 -0
- data/lib/airbrake-ruby/inspectable.rb +39 -0
- data/lib/airbrake-ruby/loggable.rb +34 -0
- data/lib/airbrake-ruby/monotonic_time.rb +43 -0
- data/lib/airbrake-ruby/nested_exception.rb +38 -0
- data/lib/airbrake-ruby/notice.rb +162 -0
- data/lib/airbrake-ruby/notice_notifier.rb +134 -0
- data/lib/airbrake-ruby/performance_breakdown.rb +45 -0
- data/lib/airbrake-ruby/performance_notifier.rb +125 -0
- data/lib/airbrake-ruby/promise.rb +109 -0
- data/lib/airbrake-ruby/query.rb +53 -0
- data/lib/airbrake-ruby/request.rb +45 -0
- data/lib/airbrake-ruby/response.rb +74 -0
- data/lib/airbrake-ruby/stashable.rb +15 -0
- data/lib/airbrake-ruby/stat.rb +73 -0
- data/lib/airbrake-ruby/sync_sender.rb +113 -0
- data/lib/airbrake-ruby/tdigest.rb +393 -0
- data/lib/airbrake-ruby/time_truncate.rb +17 -0
- data/lib/airbrake-ruby/timed_trace.rb +58 -0
- data/lib/airbrake-ruby/truncator.rb +115 -0
- data/lib/airbrake-ruby/version.rb +6 -0
- data/spec/airbrake_spec.rb +324 -0
- data/spec/async_sender_spec.rb +155 -0
- data/spec/backtrace_spec.rb +427 -0
- data/spec/benchmark_spec.rb +33 -0
- data/spec/code_hunk_spec.rb +115 -0
- data/spec/config/validator_spec.rb +184 -0
- data/spec/config_spec.rb +154 -0
- data/spec/deploy_notifier_spec.rb +48 -0
- data/spec/file_cache.rb +36 -0
- data/spec/filter_chain_spec.rb +92 -0
- data/spec/filters/context_filter_spec.rb +23 -0
- data/spec/filters/dependency_filter_spec.rb +12 -0
- data/spec/filters/exception_attributes_filter_spec.rb +50 -0
- data/spec/filters/gem_root_filter_spec.rb +41 -0
- data/spec/filters/git_last_checkout_filter_spec.rb +46 -0
- data/spec/filters/git_repository_filter.rb +61 -0
- data/spec/filters/git_revision_filter_spec.rb +126 -0
- data/spec/filters/keys_blacklist_spec.rb +225 -0
- data/spec/filters/keys_whitelist_spec.rb +194 -0
- data/spec/filters/root_directory_filter_spec.rb +39 -0
- data/spec/filters/sql_filter_spec.rb +219 -0
- data/spec/filters/system_exit_filter_spec.rb +14 -0
- data/spec/filters/thread_filter_spec.rb +277 -0
- data/spec/fixtures/notroot.txt +7 -0
- data/spec/fixtures/project_root/code.rb +221 -0
- data/spec/fixtures/project_root/empty_file.rb +0 -0
- data/spec/fixtures/project_root/long_line.txt +1 -0
- data/spec/fixtures/project_root/short_file.rb +3 -0
- data/spec/fixtures/project_root/vendor/bundle/ignored_file.rb +5 -0
- data/spec/helpers.rb +9 -0
- data/spec/ignorable_spec.rb +14 -0
- data/spec/inspectable_spec.rb +45 -0
- data/spec/monotonic_time_spec.rb +12 -0
- data/spec/nested_exception_spec.rb +73 -0
- data/spec/notice_notifier_spec.rb +356 -0
- data/spec/notice_notifier_spec/options_spec.rb +259 -0
- data/spec/notice_spec.rb +296 -0
- data/spec/performance_breakdown_spec.rb +12 -0
- data/spec/performance_notifier_spec.rb +435 -0
- data/spec/promise_spec.rb +197 -0
- data/spec/query_spec.rb +11 -0
- data/spec/request_spec.rb +11 -0
- data/spec/response_spec.rb +88 -0
- data/spec/spec_helper.rb +100 -0
- data/spec/stashable_spec.rb +23 -0
- data/spec/stat_spec.rb +47 -0
- data/spec/sync_sender_spec.rb +133 -0
- data/spec/tdigest_spec.rb +230 -0
- data/spec/time_truncate_spec.rb +13 -0
- data/spec/timed_trace_spec.rb +125 -0
- data/spec/truncator_spec.rb +238 -0
- metadata +213 -0
@@ -0,0 +1,155 @@
|
|
1
|
+
RSpec.describe Airbrake::AsyncSender do
|
2
|
+
let(:endpoint) { 'https://api.airbrake.io/api/v3/projects/1/notices' }
|
3
|
+
let(:queue_size) { 10 }
|
4
|
+
let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
|
5
|
+
|
6
|
+
before do
|
7
|
+
stub_request(:post, endpoint).to_return(status: 201, body: '{}')
|
8
|
+
Airbrake::Config.instance = Airbrake::Config.new(
|
9
|
+
project_id: '1',
|
10
|
+
workers: 3,
|
11
|
+
queue_size: queue_size
|
12
|
+
)
|
13
|
+
|
14
|
+
allow(Airbrake::Loggable.instance).to receive(:debug)
|
15
|
+
expect(subject).to have_workers
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#send" do
|
19
|
+
it "sends payload to Airbrake" do
|
20
|
+
2.times do
|
21
|
+
subject.send(notice, Airbrake::Promise.new)
|
22
|
+
end
|
23
|
+
subject.close
|
24
|
+
|
25
|
+
expect(a_request(:post, endpoint)).to have_been_made.twice
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when the queue is full" do
|
29
|
+
before do
|
30
|
+
allow(subject.unsent).to receive(:size).and_return(queue_size)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "discards payload" do
|
34
|
+
200.times do
|
35
|
+
subject.send(notice, Airbrake::Promise.new)
|
36
|
+
end
|
37
|
+
subject.close
|
38
|
+
|
39
|
+
expect(a_request(:post, endpoint)).not_to have_been_made
|
40
|
+
end
|
41
|
+
|
42
|
+
it "logs discarded payload" do
|
43
|
+
expect(Airbrake::Loggable.instance).to receive(:error).with(
|
44
|
+
/reached its capacity/
|
45
|
+
).exactly(15).times
|
46
|
+
|
47
|
+
15.times do
|
48
|
+
subject.send(notice, Airbrake::Promise.new)
|
49
|
+
end
|
50
|
+
subject.close
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns a rejected promise" do
|
54
|
+
promise = Airbrake::Promise.new
|
55
|
+
200.times { subject.send(notice, promise) }
|
56
|
+
expect(promise.value).to eq(
|
57
|
+
'error' => "AsyncSender has reached its capacity of #{queue_size}"
|
58
|
+
)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#close" do
|
64
|
+
context "when there are no unsent notices" do
|
65
|
+
it "joins the spawned thread" do
|
66
|
+
workers = subject.workers.list
|
67
|
+
expect(workers).to all(be_alive)
|
68
|
+
|
69
|
+
subject.close
|
70
|
+
expect(workers).to all(be_stop)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "when there are some unsent notices" do
|
75
|
+
it "logs how many notices are left to send" do
|
76
|
+
expect(Airbrake::Loggable.instance).to receive(:debug).with(
|
77
|
+
/waiting to send \d+ unsent notice\(s\)/
|
78
|
+
)
|
79
|
+
expect(Airbrake::Loggable.instance).to receive(:debug).with(/closed/)
|
80
|
+
|
81
|
+
300.times { subject.send(notice, Airbrake::Promise.new) }
|
82
|
+
subject.close
|
83
|
+
end
|
84
|
+
|
85
|
+
it "waits until the unsent notices queue is empty" do
|
86
|
+
subject.close
|
87
|
+
expect(subject.unsent.size).to be_zero
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "when it was already closed" do
|
92
|
+
it "doesn't increase the unsent queue size" do
|
93
|
+
begin
|
94
|
+
subject.close
|
95
|
+
rescue Airbrake::Error
|
96
|
+
nil
|
97
|
+
end
|
98
|
+
|
99
|
+
expect(subject.unsent.size).to be_zero
|
100
|
+
end
|
101
|
+
|
102
|
+
it "raises error" do
|
103
|
+
subject.close
|
104
|
+
|
105
|
+
expect(subject).to be_closed
|
106
|
+
expect { subject.close }.to raise_error(
|
107
|
+
Airbrake::Error, 'attempted to close already closed sender'
|
108
|
+
)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context "when workers were not spawned" do
|
113
|
+
it "correctly closes the notifier nevertheless" do
|
114
|
+
subject.close
|
115
|
+
expect(subject).to be_closed
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "#has_workers?" do
|
121
|
+
it "returns false when the sender is not closed, but has 0 workers" do
|
122
|
+
subject.workers.list.each do |worker|
|
123
|
+
worker.kill.join
|
124
|
+
end
|
125
|
+
expect(subject).not_to have_workers
|
126
|
+
end
|
127
|
+
|
128
|
+
it "returns false when the sender is closed" do
|
129
|
+
subject.close
|
130
|
+
expect(subject).not_to have_workers
|
131
|
+
end
|
132
|
+
|
133
|
+
it "respawns workers on fork()", skip: %w[jruby rbx].include?(RUBY_ENGINE) do
|
134
|
+
pid = fork { expect(subject).to have_workers }
|
135
|
+
Process.wait(pid)
|
136
|
+
subject.close
|
137
|
+
expect(subject).not_to have_workers
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "#spawn_workers" do
|
142
|
+
it "spawns alive threads in an enclosed ThreadGroup" do
|
143
|
+
expect(subject.workers).to be_a(ThreadGroup)
|
144
|
+
expect(subject.workers.list).to all(be_alive)
|
145
|
+
expect(subject.workers).to be_enclosed
|
146
|
+
|
147
|
+
subject.close
|
148
|
+
end
|
149
|
+
|
150
|
+
it "spawns exactly config.workers workers" do
|
151
|
+
expect(subject.workers.list.size).to eq(Airbrake::Config.instance.workers)
|
152
|
+
subject.close
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,427 @@
|
|
1
|
+
RSpec.describe Airbrake::Backtrace do
|
2
|
+
describe ".parse" do
|
3
|
+
context "UNIX backtrace" do
|
4
|
+
let(:parsed_backtrace) do
|
5
|
+
# rubocop:disable Metrics/LineLength, Style/HashSyntax, Layout/SpaceAroundOperators, Layout/SpaceInsideHashLiteralBraces
|
6
|
+
[{:file=>"/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb", :line=>23, :function=>"<top (required)>"},
|
7
|
+
{:file=>"/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb", :line=>54, :function=>"require"},
|
8
|
+
{:file=>"/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb", :line=>54, :function=>"require"},
|
9
|
+
{:file=>"/home/kyrylo/code/airbrake/ruby/spec/airbrake_spec.rb", :line=>1, :function=>"<top (required)>"},
|
10
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb", :line=>1327, :function=>"load"},
|
11
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb", :line=>1327, :function=>"block in load_spec_files"},
|
12
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb", :line=>1325, :function=>"each"},
|
13
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb", :line=>1325, :function=>"load_spec_files"},
|
14
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb", :line=>102, :function=>"setup"},
|
15
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb", :line=>88, :function=>"run"},
|
16
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb", :line=>73, :function=>"run"},
|
17
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb", :line=>41, :function=>"invoke"},
|
18
|
+
{:file=>"/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/exe/rspec", :line=>4, :function=>"<main>"}]
|
19
|
+
# rubocop:enable Metrics/LineLength, Style/HashSyntax, Layout/SpaceAroundOperators, Layout/SpaceInsideHashLiteralBraces
|
20
|
+
end
|
21
|
+
|
22
|
+
it "returns a properly formatted array of hashes" do
|
23
|
+
expect(described_class.parse(AirbrakeTestError.new))
|
24
|
+
.to eq(parsed_backtrace)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "Windows backtrace" do
|
29
|
+
let(:windows_bt) do
|
30
|
+
["C:/Program Files/Server/app/models/user.rb:13:in `magic'",
|
31
|
+
"C:/Program Files/Server/app/controllers/users_controller.rb:8:in `index'"]
|
32
|
+
end
|
33
|
+
|
34
|
+
let(:ex) { AirbrakeTestError.new.tap { |e| e.set_backtrace(windows_bt) } }
|
35
|
+
|
36
|
+
let(:parsed_backtrace) do
|
37
|
+
# rubocop:disable Metrics/LineLength, Style/HashSyntax, Layout/SpaceInsideHashLiteralBraces, Layout/SpaceAroundOperators
|
38
|
+
[{:file=>"C:/Program Files/Server/app/models/user.rb", :line=>13, :function=>"magic"},
|
39
|
+
{:file=>"C:/Program Files/Server/app/controllers/users_controller.rb", :line=>8, :function=>"index"}]
|
40
|
+
# rubocop:enable Metrics/LineLength, Style/HashSyntax, Layout/SpaceInsideHashLiteralBraces, Layout/SpaceAroundOperators
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns a properly formatted array of hashes" do
|
44
|
+
expect(described_class.parse(ex)).to eq(parsed_backtrace)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "JRuby Java exceptions" do
|
49
|
+
let(:backtrace_array) do
|
50
|
+
# rubocop:disable Metrics/LineLength, Style/HashSyntax, Layout/SpaceInsideHashLiteralBraces, Layout/SpaceAroundOperators
|
51
|
+
[{:file=>"InstanceMethodInvoker.java", :line=>26, :function=>"org.jruby.java.invokers.InstanceMethodInvoker.call"},
|
52
|
+
{:file=>"Interpreter.java", :line=>126, :function=>"org.jruby.ir.interpreter.Interpreter.INTERPRET_EVAL"},
|
53
|
+
{:file=>"RubyKernel$INVOKER$s$0$3$eval19.gen", :line=>nil, :function=>"org.jruby.RubyKernel$INVOKER$s$0$3$eval19.call"},
|
54
|
+
{:file=>"RubyKernel$INVOKER$s$0$0$loop.gen", :line=>nil, :function=>"org.jruby.RubyKernel$INVOKER$s$0$0$loop.call"},
|
55
|
+
{:file=>"IRBlockBody.java", :line=>139, :function=>"org.jruby.runtime.IRBlockBody.doYield"},
|
56
|
+
{:file=>"RubyKernel$INVOKER$s$rbCatch19.gen", :line=>nil, :function=>"org.jruby.RubyKernel$INVOKER$s$rbCatch19.call"},
|
57
|
+
{:file=>"/opt/rubies/jruby-9.0.0.0/bin/irb", :line=>nil, :function=>"opt.rubies.jruby_minus_9_dot_0_dot_0_dot_0.bin.irb.invokeOther4:start"},
|
58
|
+
{:file=>"/opt/rubies/jruby-9.0.0.0/bin/irb", :line=>13, :function=>"opt.rubies.jruby_minus_9_dot_0_dot_0_dot_0.bin.irb.RUBY$script"},
|
59
|
+
{:file=>"Compiler.java", :line=>111, :function=>"org.jruby.ir.Compiler$1.load"},
|
60
|
+
{:file=>"Main.java", :line=>225, :function=>"org.jruby.Main.run"},
|
61
|
+
{:file=>"Main.java", :line=>197, :function=>"org.jruby.Main.main"}]
|
62
|
+
# rubocop:enable Metrics/LineLength, Style/HashSyntax, Layout/SpaceInsideHashLiteralBraces, Layout/SpaceAroundOperators
|
63
|
+
end
|
64
|
+
|
65
|
+
it "returns a properly formatted array of hashes" do
|
66
|
+
allow(described_class).to receive(:java_exception?).and_return(true)
|
67
|
+
|
68
|
+
expect(described_class.parse(JavaAirbrakeTestError.new))
|
69
|
+
.to eq(backtrace_array)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "JRuby classloader exceptions" do
|
74
|
+
let(:backtrace) do
|
75
|
+
# rubocop:disable Metrics/LineLength
|
76
|
+
['uri_3a_classloader_3a_.META_minus_INF.jruby_dot_home.lib.ruby.stdlib.net.protocol.rbuf_fill(uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/net/protocol.rb:158)',
|
77
|
+
'bin.processors.image_uploader.block in make_streams(bin/processors/image_uploader.rb:21)',
|
78
|
+
'uri_3a_classloader_3a_.gems.faye_minus_websocket_minus_0_dot_10_dot_5.lib.faye.websocket.api.invokeOther13:dispatch_event(uri_3a_classloader_3a_/gems/faye_minus_websocket_minus_0_dot_10_dot_5/lib/faye/websocket/uri:classloader:/gems/faye-websocket-0.10.5/lib/faye/websocket/api.rb:109)',
|
79
|
+
'tmp.jruby9022301782566983632extract.$dot.META_minus_INF.rails.file(/tmp/jruby9022301782566983632extract/./META-INF/rails.rb:13)']
|
80
|
+
# rubocop:enable Metrics/LineLength
|
81
|
+
end
|
82
|
+
|
83
|
+
let(:parsed_backtrace) do
|
84
|
+
# rubocop:disable Metrics/LineLength
|
85
|
+
[{ file: 'uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/net/protocol.rb', line: 158, function: 'uri_3a_classloader_3a_.META_minus_INF.jruby_dot_home.lib.ruby.stdlib.net.protocol.rbuf_fill' },
|
86
|
+
{ file: 'bin/processors/image_uploader.rb', line: 21, function: 'bin.processors.image_uploader.block in make_streams' },
|
87
|
+
{ file: 'uri_3a_classloader_3a_/gems/faye_minus_websocket_minus_0_dot_10_dot_5/lib/faye/websocket/uri:classloader:/gems/faye-websocket-0.10.5/lib/faye/websocket/api.rb', line: 109, function: 'uri_3a_classloader_3a_.gems.faye_minus_websocket_minus_0_dot_10_dot_5.lib.faye.websocket.api.invokeOther13:dispatch_event' },
|
88
|
+
{ file: '/tmp/jruby9022301782566983632extract/./META-INF/rails.rb', line: 13, function: 'tmp.jruby9022301782566983632extract.$dot.META_minus_INF.rails.file' }]
|
89
|
+
# rubocop:enable Metrics/LineLength
|
90
|
+
end
|
91
|
+
|
92
|
+
let(:ex) { JavaAirbrakeTestError.new.tap { |e| e.set_backtrace(backtrace) } }
|
93
|
+
|
94
|
+
it "returns a properly formatted array of hashes" do
|
95
|
+
allow(described_class).to receive(:java_exception?).and_return(true)
|
96
|
+
expect(described_class.parse(ex)).to eq(parsed_backtrace)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "JRuby non-throwable exceptions" do
|
101
|
+
let(:backtrace) do
|
102
|
+
# rubocop:disable Metrics/LineLength
|
103
|
+
['org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(org/postgresql/core/v3/ConnectionFactoryImpl.java:257)',
|
104
|
+
'org.postgresql.core.ConnectionFactory.openConnection(org/postgresql/core/ConnectionFactory.java:65)',
|
105
|
+
'org.postgresql.jdbc2.AbstractJdbc2Connection.<init>(org/postgresql/jdbc2/AbstractJdbc2Connection.java:149)']
|
106
|
+
# rubocop:enable Metrics/LineLength
|
107
|
+
end
|
108
|
+
|
109
|
+
let(:parsed_backtrace) do
|
110
|
+
# rubocop:disable Metrics/LineLength
|
111
|
+
[{ file: 'org/postgresql/core/v3/ConnectionFactoryImpl.java', line: 257, function: 'org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl' },
|
112
|
+
{ file: 'org/postgresql/core/ConnectionFactory.java', line: 65, function: 'org.postgresql.core.ConnectionFactory.openConnection' },
|
113
|
+
{ file: 'org/postgresql/jdbc2/AbstractJdbc2Connection.java', line: 149, function: 'org.postgresql.jdbc2.AbstractJdbc2Connection.<init>' }]
|
114
|
+
# rubocop:enable Metrics/LineLength
|
115
|
+
end
|
116
|
+
|
117
|
+
let(:ex) { AirbrakeTestError.new.tap { |e| e.set_backtrace(backtrace) } }
|
118
|
+
|
119
|
+
it "returns a properly formatted array of hashes" do
|
120
|
+
expect(described_class.parse(ex)).to eq(parsed_backtrace)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context "generic backtrace" do
|
125
|
+
context "when function is absent" do
|
126
|
+
# rubocop:disable Metrics/LineLength
|
127
|
+
let(:generic_bt) do
|
128
|
+
["/home/bingo/bango/assets/stylesheets/error_pages.scss:139:in `animation'",
|
129
|
+
"/home/bingo/bango/assets/stylesheets/error_pages.scss:139",
|
130
|
+
"/home/bingo/.gem/ruby/2.2.2/gems/sass-3.4.20/lib/sass/tree/visitors/perform.rb:349:in `block in visit_mixin'"]
|
131
|
+
end
|
132
|
+
# rubocop:enable Metrics/LineLength
|
133
|
+
|
134
|
+
let(:ex) { AirbrakeTestError.new.tap { |e| e.set_backtrace(generic_bt) } }
|
135
|
+
|
136
|
+
let(:parsed_backtrace) do
|
137
|
+
# rubocop:disable Metrics/LineLength, Style/HashSyntax, Layout/SpaceInsideHashLiteralBraces, Layout/SpaceAroundOperators
|
138
|
+
[{:file=>"/home/bingo/bango/assets/stylesheets/error_pages.scss", :line=>139, :function=>"animation"},
|
139
|
+
{:file=>"/home/bingo/bango/assets/stylesheets/error_pages.scss", :line=>139, :function=>nil},
|
140
|
+
{:file=>"/home/bingo/.gem/ruby/2.2.2/gems/sass-3.4.20/lib/sass/tree/visitors/perform.rb", :line=>349, :function=>"block in visit_mixin"}]
|
141
|
+
# rubocop:enable Metrics/LineLength, Style/HashSyntax, Layout/SpaceInsideHashLiteralBraces, Layout/SpaceAroundOperators
|
142
|
+
end
|
143
|
+
|
144
|
+
it "returns a properly formatted array of hashes" do
|
145
|
+
expect(described_class.parse(ex)).to eq(parsed_backtrace)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context "when line is absent" do
|
150
|
+
let(:generic_bt) do
|
151
|
+
["/Users/grammakov/repositories/weintervene/config.ru:in `new'"]
|
152
|
+
end
|
153
|
+
|
154
|
+
let(:ex) { AirbrakeTestError.new.tap { |e| e.set_backtrace(generic_bt) } }
|
155
|
+
|
156
|
+
let(:parsed_backtrace) do
|
157
|
+
[{ file: '/Users/grammakov/repositories/weintervene/config.ru',
|
158
|
+
line: nil,
|
159
|
+
function: 'new' }]
|
160
|
+
end
|
161
|
+
|
162
|
+
it "returns a properly formatted array of hashes" do
|
163
|
+
expect(described_class.parse(ex)).to eq(parsed_backtrace)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context "unknown backtrace" do
|
169
|
+
let(:unknown_bt) { ['a b c 1 23 321 .rb'] }
|
170
|
+
|
171
|
+
let(:ex) { AirbrakeTestError.new.tap { |e| e.set_backtrace(unknown_bt) } }
|
172
|
+
|
173
|
+
it "returns array of hashes where each unknown frame is marked as 'function'" do
|
174
|
+
expect(
|
175
|
+
described_class.parse(ex)
|
176
|
+
).to eq([file: nil, line: nil, function: 'a b c 1 23 321 .rb'])
|
177
|
+
end
|
178
|
+
|
179
|
+
it "logs frames that cannot be parsed" do
|
180
|
+
expect(Airbrake::Loggable.instance).to receive(:error).with(
|
181
|
+
/can't parse 'a b c 1 23 321 .rb'/
|
182
|
+
)
|
183
|
+
described_class.parse(ex)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context "given a backtrace with an empty function" do
|
188
|
+
let(:bt) do
|
189
|
+
["/airbrake-ruby/vendor/jruby/1.9/gems/rspec-core-3.4.1/exe/rspec:3:in `'"]
|
190
|
+
end
|
191
|
+
|
192
|
+
let(:ex) { AirbrakeTestError.new.tap { |e| e.set_backtrace(bt) } }
|
193
|
+
|
194
|
+
let(:parsed_backtrace) do
|
195
|
+
[{ file: '/airbrake-ruby/vendor/jruby/1.9/gems/rspec-core-3.4.1/exe/rspec',
|
196
|
+
line: 3,
|
197
|
+
function: '' }]
|
198
|
+
end
|
199
|
+
|
200
|
+
it "returns a properly formatted array of hashes" do
|
201
|
+
expect(described_class.parse(ex)).to eq(parsed_backtrace)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context "given an Oracle backtrace" do
|
206
|
+
let(:bt) do
|
207
|
+
['ORA-06512: at "STORE.LI_LICENSES_PACK", line 1945',
|
208
|
+
'ORA-06512: at "ACTIVATION.LI_ACT_LICENSES_PACK", line 101',
|
209
|
+
'ORA-06512: at line 2',
|
210
|
+
'from stmt.c:243:in oci8lib_220.bundle']
|
211
|
+
end
|
212
|
+
|
213
|
+
let(:ex) { OCIError.new.tap { |e| e.set_backtrace(bt) } }
|
214
|
+
|
215
|
+
let(:parsed_backtrace) do
|
216
|
+
[{ file: nil, line: 1945, function: 'STORE.LI_LICENSES_PACK' },
|
217
|
+
{ file: nil, line: 101, function: 'ACTIVATION.LI_ACT_LICENSES_PACK' },
|
218
|
+
{ file: nil, line: 2, function: nil },
|
219
|
+
{ file: 'stmt.c', line: 243, function: 'oci8lib_220.bundle' }]
|
220
|
+
end
|
221
|
+
|
222
|
+
it "returns a properly formatted array of hashes" do
|
223
|
+
stub_const('OCIError', AirbrakeTestError)
|
224
|
+
expect(described_class.parse(ex)).to eq(parsed_backtrace)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
context "given an ExecJS exception" do
|
229
|
+
let(:bt) do
|
230
|
+
['compile ((execjs):6692:19)',
|
231
|
+
'eval (<anonymous>:1:10)',
|
232
|
+
'(execjs):6703:8',
|
233
|
+
'require../helpers.exports ((execjs):1:102)',
|
234
|
+
'Object.<anonymous> ((execjs):1:120)',
|
235
|
+
'Object.Module._extensions..js (module.js:550:10)',
|
236
|
+
'bootstrap_node.js:467:3',
|
237
|
+
"/opt/rubies/ruby-2.3.1/lib/ruby/2.3.0/benchmark.rb:308:in `realtime'"]
|
238
|
+
end
|
239
|
+
|
240
|
+
let(:ex) { ExecJS::RuntimeError.new.tap { |e| e.set_backtrace(bt) } }
|
241
|
+
|
242
|
+
let(:parsed_backtrace) do
|
243
|
+
[{ file: '(execjs)', line: 6692, function: 'compile' },
|
244
|
+
{ file: '<anonymous>', line: 1, function: 'eval' },
|
245
|
+
{ file: '(execjs)', line: 6703, function: '' },
|
246
|
+
{ file: '(execjs)', line: 1, function: 'require../helpers.exports' },
|
247
|
+
{ file: '(execjs)', line: 1, function: 'Object.<anonymous>' },
|
248
|
+
{ file: 'module.js', line: 550, function: 'Object.Module._extensions..js' },
|
249
|
+
{ file: 'bootstrap_node.js', line: 467, function: '' },
|
250
|
+
{ file: '/opt/rubies/ruby-2.3.1/lib/ruby/2.3.0/benchmark.rb',
|
251
|
+
line: 308,
|
252
|
+
function: 'realtime' }]
|
253
|
+
end
|
254
|
+
|
255
|
+
it "returns a properly formatted array of hashes" do
|
256
|
+
stub_const('ExecJS::RuntimeError', AirbrakeTestError)
|
257
|
+
expect(described_class.parse(ex)).to eq(parsed_backtrace)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
context "when code hunks are enabled" do
|
262
|
+
before { Airbrake::Config.instance.merge(code_hunks: true) }
|
263
|
+
|
264
|
+
context "and when root_directory is configured" do
|
265
|
+
before do
|
266
|
+
Airbrake::Config.instance.merge(root_directory: project_root_path(''))
|
267
|
+
end
|
268
|
+
|
269
|
+
let(:parsed_backtrace) do
|
270
|
+
[
|
271
|
+
{
|
272
|
+
file: project_root_path('code.rb'),
|
273
|
+
line: 94,
|
274
|
+
function: 'to_json',
|
275
|
+
code: {
|
276
|
+
92 => ' loop do',
|
277
|
+
93 => ' begin',
|
278
|
+
94 => ' json = @payload.to_json',
|
279
|
+
95 => ' rescue *JSON_EXCEPTIONS => ex',
|
280
|
+
# rubocop:disable Metrics/LineLength,Lint/InterpolationCheck
|
281
|
+
96 => ' @config.logger.debug("#{LOG_LABEL} `notice.to_json` failed: #{ex.class}: #{ex}")',
|
282
|
+
# rubocop:enable Metrics/LineLength,Lint/InterpolationCheck
|
283
|
+
}
|
284
|
+
},
|
285
|
+
{
|
286
|
+
file: fixture_path('notroot.txt'),
|
287
|
+
line: 3,
|
288
|
+
function: 'pineapple'
|
289
|
+
},
|
290
|
+
{
|
291
|
+
file: project_root_path('vendor/bundle/ignored_file.rb'),
|
292
|
+
line: 2,
|
293
|
+
function: 'ignore_me'
|
294
|
+
}
|
295
|
+
]
|
296
|
+
end
|
297
|
+
|
298
|
+
it "attaches code to those frames files of which match root_directory" do
|
299
|
+
ex = RuntimeError.new
|
300
|
+
backtrace = [
|
301
|
+
project_root_path('code.rb') + ":94:in `to_json'",
|
302
|
+
fixture_path('notroot.txt' + ":3:in `pineapple'"),
|
303
|
+
project_root_path('vendor/bundle/ignored_file.rb') + ":2:in `ignore_me'"
|
304
|
+
]
|
305
|
+
ex.set_backtrace(backtrace)
|
306
|
+
expect(described_class.parse(ex)).to eq(parsed_backtrace)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
context "and when root_directory is a Pathname" do
|
311
|
+
before do
|
312
|
+
Airbrake::Config.instance.merge(
|
313
|
+
root_directory: Pathname.new(project_root_path(''))
|
314
|
+
)
|
315
|
+
end
|
316
|
+
|
317
|
+
let(:parsed_backtrace) do
|
318
|
+
[
|
319
|
+
{
|
320
|
+
file: project_root_path('code.rb'),
|
321
|
+
line: 94,
|
322
|
+
function: 'to_json',
|
323
|
+
code: {
|
324
|
+
92 => ' loop do',
|
325
|
+
93 => ' begin',
|
326
|
+
94 => ' json = @payload.to_json',
|
327
|
+
95 => ' rescue *JSON_EXCEPTIONS => ex',
|
328
|
+
# rubocop:disable Metrics/LineLength,Lint/InterpolationCheck
|
329
|
+
96 => ' @config.logger.debug("#{LOG_LABEL} `notice.to_json` failed: #{ex.class}: #{ex}")',
|
330
|
+
# rubocop:enable Metrics/LineLength,Lint/InterpolationCheck
|
331
|
+
}
|
332
|
+
}
|
333
|
+
]
|
334
|
+
end
|
335
|
+
|
336
|
+
it "attaches code to those frames files of which match root_directory" do
|
337
|
+
ex = RuntimeError.new
|
338
|
+
ex.set_backtrace([project_root_path('code.rb') + ":94:in `to_json'"])
|
339
|
+
expect(described_class.parse(ex)).to eq(parsed_backtrace)
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
context "and when root_directory isn't configured" do
|
344
|
+
before do
|
345
|
+
Airbrake::Config.instance.merge(root_directory: nil)
|
346
|
+
stub_const('Airbrake::Backtrace::CODE_FRAME_LIMIT', 2)
|
347
|
+
end
|
348
|
+
|
349
|
+
let(:parsed_backtrace) do
|
350
|
+
[
|
351
|
+
{
|
352
|
+
file: project_root_path('code.rb'),
|
353
|
+
line: 94,
|
354
|
+
function: 'to_json',
|
355
|
+
code: {
|
356
|
+
92 => ' loop do',
|
357
|
+
93 => ' begin',
|
358
|
+
94 => ' json = @payload.to_json',
|
359
|
+
95 => ' rescue *JSON_EXCEPTIONS => ex',
|
360
|
+
# rubocop:disable Metrics/LineLength,Lint/InterpolationCheck
|
361
|
+
96 => ' @config.logger.debug("#{LOG_LABEL} `notice.to_json` failed: #{ex.class}: #{ex}")',
|
362
|
+
# rubocop:enable Metrics/LineLength,Lint/InterpolationCheck
|
363
|
+
}
|
364
|
+
},
|
365
|
+
{
|
366
|
+
file: project_root_path('code.rb'),
|
367
|
+
line: 95,
|
368
|
+
function: 'to_json',
|
369
|
+
code: {
|
370
|
+
93 => ' begin',
|
371
|
+
94 => ' json = @payload.to_json',
|
372
|
+
95 => ' rescue *JSON_EXCEPTIONS => ex',
|
373
|
+
# rubocop:disable Metrics/LineLength,Lint/InterpolationCheck
|
374
|
+
96 => ' @config.logger.debug("#{LOG_LABEL} `notice.to_json` failed: #{ex.class}: #{ex}")',
|
375
|
+
# rubocop:enable Metrics/LineLength,Lint/InterpolationCheck
|
376
|
+
97 => ' else'
|
377
|
+
}
|
378
|
+
},
|
379
|
+
{
|
380
|
+
file: project_root_path('code.rb'),
|
381
|
+
line: 96,
|
382
|
+
function: 'to_json'
|
383
|
+
}
|
384
|
+
]
|
385
|
+
end
|
386
|
+
|
387
|
+
it "attaches code to first N frames" do
|
388
|
+
ex = RuntimeError.new
|
389
|
+
backtrace = [
|
390
|
+
project_root_path('code.rb') + ":94:in `to_json'",
|
391
|
+
project_root_path('code.rb') + ":95:in `to_json'",
|
392
|
+
project_root_path('code.rb') + ":96:in `to_json'"
|
393
|
+
]
|
394
|
+
ex.set_backtrace(backtrace)
|
395
|
+
expect(described_class.parse(ex)).to eq(parsed_backtrace)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
context "when code hunks are disabled" do
|
401
|
+
before { Airbrake::Config.instance.merge(code_hunks: false) }
|
402
|
+
|
403
|
+
context "and when root_directory is configured" do
|
404
|
+
before do
|
405
|
+
Airbrake::Config.instance.merge(root_directory: project_root_path(''))
|
406
|
+
end
|
407
|
+
|
408
|
+
let(:parsed_backtrace) do
|
409
|
+
[
|
410
|
+
{
|
411
|
+
file: project_root_path('code.rb'),
|
412
|
+
line: 94,
|
413
|
+
function: 'to_json'
|
414
|
+
}
|
415
|
+
]
|
416
|
+
end
|
417
|
+
|
418
|
+
it "doesn't attach code to frames" do
|
419
|
+
ex = RuntimeError.new
|
420
|
+
backtrace = [project_root_path('code.rb') + ":94:in `to_json'"]
|
421
|
+
ex.set_backtrace(backtrace)
|
422
|
+
expect(described_class.parse(ex)).to eq(parsed_backtrace)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|