airbrake-ruby 3.1.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby.rb +197 -43
  3. data/lib/airbrake-ruby/config.rb +43 -11
  4. data/lib/airbrake-ruby/deploy_notifier.rb +47 -0
  5. data/lib/airbrake-ruby/filter_chain.rb +32 -50
  6. data/lib/airbrake-ruby/filters/git_repository_filter.rb +9 -1
  7. data/lib/airbrake-ruby/filters/sql_filter.rb +104 -0
  8. data/lib/airbrake-ruby/hash_keyable.rb +37 -0
  9. data/lib/airbrake-ruby/ignorable.rb +44 -0
  10. data/lib/airbrake-ruby/notice.rb +2 -22
  11. data/lib/airbrake-ruby/{notifier.rb → notice_notifier.rb} +66 -46
  12. data/lib/airbrake-ruby/performance_notifier.rb +161 -0
  13. data/lib/airbrake-ruby/stat.rb +56 -0
  14. data/lib/airbrake-ruby/tdigest.rb +393 -0
  15. data/lib/airbrake-ruby/time_truncate.rb +17 -0
  16. data/lib/airbrake-ruby/version.rb +1 -1
  17. data/spec/airbrake_spec.rb +57 -13
  18. data/spec/async_sender_spec.rb +0 -2
  19. data/spec/backtrace_spec.rb +0 -2
  20. data/spec/code_hunk_spec.rb +0 -2
  21. data/spec/config/validator_spec.rb +0 -2
  22. data/spec/config_spec.rb +16 -4
  23. data/spec/deploy_notifier_spec.rb +41 -0
  24. data/spec/file_cache.rb +0 -2
  25. data/spec/filter_chain_spec.rb +1 -7
  26. data/spec/filters/context_filter_spec.rb +0 -2
  27. data/spec/filters/dependency_filter_spec.rb +0 -2
  28. data/spec/filters/exception_attributes_filter_spec.rb +0 -2
  29. data/spec/filters/gem_root_filter_spec.rb +0 -2
  30. data/spec/filters/git_last_checkout_filter_spec.rb +0 -2
  31. data/spec/filters/git_repository_filter.rb +0 -2
  32. data/spec/filters/git_revision_filter_spec.rb +0 -2
  33. data/spec/filters/keys_blacklist_spec.rb +0 -2
  34. data/spec/filters/keys_whitelist_spec.rb +0 -2
  35. data/spec/filters/root_directory_filter_spec.rb +0 -2
  36. data/spec/filters/sql_filter_spec.rb +219 -0
  37. data/spec/filters/system_exit_filter_spec.rb +0 -2
  38. data/spec/filters/thread_filter_spec.rb +0 -2
  39. data/spec/ignorable_spec.rb +14 -0
  40. data/spec/nested_exception_spec.rb +0 -2
  41. data/spec/{notifier_spec.rb → notice_notifier_spec.rb} +24 -114
  42. data/spec/{notifier_spec → notice_notifier_spec}/options_spec.rb +40 -39
  43. data/spec/notice_spec.rb +2 -4
  44. data/spec/performance_notifier_spec.rb +287 -0
  45. data/spec/promise_spec.rb +0 -2
  46. data/spec/response_spec.rb +0 -2
  47. data/spec/stat_spec.rb +35 -0
  48. data/spec/sync_sender_spec.rb +0 -2
  49. data/spec/tdigest_spec.rb +230 -0
  50. data/spec/time_truncate_spec.rb +13 -0
  51. data/spec/truncator_spec.rb +0 -2
  52. metadata +34 -15
  53. data/lib/airbrake-ruby/route_sender.rb +0 -175
  54. data/spec/route_sender_spec.rb +0 -130
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::Filters::SystemExitFilter do
4
2
  it "marks SystemExit exceptions as ignored" do
5
3
  notice = Airbrake::Notice.new(Airbrake::Config.new, SystemExit.new)
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::Filters::ThreadFilter do
4
2
  let(:notice) do
5
3
  Airbrake::Notice.new(Airbrake::Config.new, AirbrakeTestError.new)
@@ -0,0 +1,14 @@
1
+ RSpec.describe Airbrake::Ignorable do
2
+ let(:klass) do
3
+ mod = subject
4
+ Class.new { include(mod) }
5
+ end
6
+
7
+ it "ignores includee" do
8
+ instance = klass.new
9
+ expect(instance).not_to be_ignored
10
+
11
+ instance.ignore!
12
+ expect(instance).to be_ignored
13
+ end
14
+ end
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  RSpec.describe Airbrake::NestedException do
4
2
  let(:config) { Airbrake::Config.new }
5
3
 
@@ -1,17 +1,17 @@
1
- require 'spec_helper'
2
-
3
1
  # rubocop:disable Layout/DotPosition
4
- RSpec.describe Airbrake::Notifier do
2
+ RSpec.describe Airbrake::NoticeNotifier do
5
3
  let(:user_params) do
6
4
  {
7
5
  project_id: 1,
8
6
  project_key: 'abc',
9
7
  logger: Logger.new('/dev/null'),
10
- route_stats: true
8
+ performance_stats: true
11
9
  }
12
10
  end
13
11
 
14
- subject { described_class.new(user_params) }
12
+ let(:params) { {} }
13
+ let(:config) { Airbrake::Config.new(user_params.merge(params)) }
14
+ subject { described_class.new(config) }
15
15
 
16
16
  describe "#new" do
17
17
  describe "default filter addition" do
@@ -20,20 +20,22 @@ RSpec.describe Airbrake::Notifier do
20
20
  it "appends the context filter" do
21
21
  expect_any_instance_of(Airbrake::FilterChain).to receive(:add_filter)
22
22
  .with(instance_of(Airbrake::Filters::ContextFilter))
23
- described_class.new(user_params)
23
+ subject
24
24
  end
25
25
 
26
26
  it "appends the exception attributes filter" do
27
27
  expect_any_instance_of(Airbrake::FilterChain).to receive(:add_filter)
28
28
  .with(instance_of(Airbrake::Filters::ExceptionAttributesFilter))
29
- described_class.new(user_params)
29
+ subject
30
30
  end
31
31
 
32
32
  context "when user config has some whitelist keys" do
33
+ let(:params) { { whitelist_keys: %w[foo] } }
34
+
33
35
  it "appends the whitelist filter" do
34
36
  expect_any_instance_of(Airbrake::FilterChain).to receive(:add_filter)
35
37
  .with(instance_of(Airbrake::Filters::KeysWhitelist))
36
- described_class.new(user_params.merge(whitelist_keys: ['foo']))
38
+ described_class.new(config)
37
39
  end
38
40
  end
39
41
 
@@ -41,15 +43,17 @@ RSpec.describe Airbrake::Notifier do
41
43
  it "doesn't append the whitelist filter" do
42
44
  expect_any_instance_of(Airbrake::FilterChain).not_to receive(:add_filter)
43
45
  .with(instance_of(Airbrake::Filters::KeysWhitelist))
44
- described_class.new(user_params)
46
+ described_class.new(config)
45
47
  end
46
48
  end
47
49
 
48
50
  context "when user config has some blacklist keys" do
51
+ let(:params) { { blacklist_keys: %w[bar] } }
52
+
49
53
  it "appends the blacklist filter" do
50
54
  expect_any_instance_of(Airbrake::FilterChain).to receive(:add_filter)
51
55
  .with(instance_of(Airbrake::Filters::KeysBlacklist))
52
- described_class.new(user_params.merge(blacklist_keys: ['bar']))
56
+ described_class.new(config)
53
57
  end
54
58
  end
55
59
 
@@ -57,15 +61,17 @@ RSpec.describe Airbrake::Notifier do
57
61
  it "doesn't append the blacklist filter" do
58
62
  expect_any_instance_of(Airbrake::FilterChain).not_to receive(:add_filter)
59
63
  .with(instance_of(Airbrake::Filters::KeysBlacklist))
60
- described_class.new(user_params)
64
+ described_class.new(config)
61
65
  end
62
66
  end
63
67
 
64
68
  context "when user config specifies a root directory" do
69
+ let(:params) { { root_directory: '/foo' } }
70
+
65
71
  it "appends the root directory filter" do
66
72
  expect_any_instance_of(Airbrake::FilterChain).to receive(:add_filter)
67
73
  .with(instance_of(Airbrake::Filters::RootDirectoryFilter))
68
- described_class.new(user_params.merge(root_directory: '/foo'))
74
+ described_class.new(config)
69
75
  end
70
76
  end
71
77
 
@@ -75,34 +81,16 @@ RSpec.describe Airbrake::Notifier do
75
81
  .and_return(nil)
76
82
  expect_any_instance_of(Airbrake::FilterChain).not_to receive(:add_filter)
77
83
  .with(instance_of(Airbrake::Filters::RootDirectoryFilter))
78
- described_class.new(user_params)
84
+ described_class.new(config)
79
85
  end
80
86
  end
81
87
  end
82
-
83
- context "when user config doesn't contain a project id" do
84
- let(:user_config) { { project_id: nil } }
85
-
86
- it "raises error" do
87
- expect { described_class.new(user_config) }
88
- .to raise_error(Airbrake::Error, ':project_id is required')
89
- end
90
- end
91
-
92
- context "when user config doesn't contain a project key" do
93
- let(:user_config) { { project_id: 1, project_key: nil } }
94
-
95
- it "raises error" do
96
- expect { described_class.new(user_config) }
97
- .to raise_error(Airbrake::Error, ':project_key is required')
98
- end
99
- end
100
88
  end
101
89
 
102
90
  describe "#notify" do
103
91
  let(:endpoint) { 'https://api.airbrake.io/api/v3/projects/1/notices' }
104
92
 
105
- subject { described_class.new(user_params) }
93
+ subject { described_class.new(Airbrake::Config.new(user_params)) }
106
94
 
107
95
  let(:body) do
108
96
  {
@@ -173,11 +161,7 @@ RSpec.describe Airbrake::Notifier do
173
161
  end
174
162
 
175
163
  context "when the provided environment is ignored" do
176
- subject do
177
- described_class.new(
178
- user_params.merge(environment: 'test', ignore_environments: ['test'])
179
- )
180
- end
164
+ let(:user_params) { { environment: 'test', ignore_environments: %w[test] } }
181
165
 
182
166
  it "doesn't send an notice" do
183
167
  expect_any_instance_of(Airbrake::AsyncSender).not_to receive(:send)
@@ -257,11 +241,7 @@ RSpec.describe Airbrake::Notifier do
257
241
  end
258
242
 
259
243
  context "when the provided environment is ignored" do
260
- subject do
261
- described_class.new(
262
- user_params.merge(environment: 'test', ignore_environments: ['test'])
263
- )
264
- end
244
+ let(:params) { { environment: 'test', ignore_environments: %w[test] } }
265
245
 
266
246
  it "doesn't send an notice" do
267
247
  expect_any_instance_of(Airbrake::SyncSender).not_to receive(:send)
@@ -412,38 +392,6 @@ RSpec.describe Airbrake::Notifier do
412
392
  end
413
393
  end
414
394
 
415
- describe "#create_deploy" do
416
- it "returns a promise" do
417
- stub_request(:post, 'https://api.airbrake.io/api/v4/projects/1/deploys')
418
- .to_return(status: 201, body: '')
419
- expect(subject.create_deploy({})).to be_an(Airbrake::Promise)
420
- end
421
-
422
- context "when environment is configured" do
423
- it "prefers the passed environment to the config env" do
424
- expect_any_instance_of(Airbrake::SyncSender).to receive(:send).with(
425
- { environment: 'barenv' },
426
- instance_of(Airbrake::Promise),
427
- URI('https://api.airbrake.io/api/v4/projects/1/deploys')
428
- )
429
- described_class.new(
430
- user_params.merge(environment: 'fooenv')
431
- ).create_deploy(environment: 'barenv')
432
- end
433
- end
434
-
435
- context "when environment is not configured" do
436
- it "sets the environment from the config" do
437
- expect_any_instance_of(Airbrake::SyncSender).to receive(:send).with(
438
- { environment: 'fooenv' },
439
- instance_of(Airbrake::Promise),
440
- URI('https://api.airbrake.io/api/v4/projects/1/deploys')
441
- )
442
- subject.create_deploy(environment: 'fooenv')
443
- end
444
- end
445
- end
446
-
447
395
  describe "#configured?" do
448
396
  it { is_expected.to be_configured }
449
397
  end
@@ -455,48 +403,10 @@ RSpec.describe Airbrake::Notifier do
455
403
  end
456
404
  end
457
405
 
458
- describe "#notify_request" do
459
- let(:params) do
460
- {
461
- method: 'GET',
462
- route: '/foo',
463
- status_code: 200,
464
- start_time: Time.new(2018, 1, 1, 0, 20, 0, 0),
465
- end_time: Time.new(2018, 1, 1, 0, 19, 0, 0)
466
- }
467
- end
468
-
469
- it "forwards 'notify_request' to RouteSender" do
470
- expect_any_instance_of(Airbrake::RouteSender)
471
- .to receive(:notify_request).with(params, instance_of(Airbrake::Promise))
472
- subject.notify_request(params)
473
- end
474
-
475
- context "when route stats are disabled" do
476
- it "doesn't send route stats" do
477
- notifier = described_class.new(user_params.merge(route_stats: false))
478
- expect_any_instance_of(Airbrake::RouteSender)
479
- .not_to receive(:notify_request)
480
- notifier.notify_request(params)
481
- end
482
- end
483
-
484
- context "when current environment is ignored" do
485
- it "doesn't send route stats" do
486
- notifier = described_class.new(
487
- user_params.merge(environment: 'test', ignore_environments: %w[test])
488
- )
489
- expect_any_instance_of(Airbrake::RouteSender)
490
- .not_to receive(:notify_request)
491
- notifier.notify_request(params)
492
- end
493
- end
494
- end
495
-
496
406
  describe "#inspect" do
497
407
  it "displays object information" do
498
408
  expect(subject.inspect).to match(/
499
- #<Airbrake::Notifier:0x\w+\s
409
+ #<Airbrake::NoticeNotifier:0x\w+\s
500
410
  project_id="\d+"\s
501
411
  project_key=".+"\s
502
412
  host="http.+"\s
@@ -514,7 +424,7 @@ RSpec.describe Airbrake::Notifier do
514
424
  q.guard_inspect_key { subject.pretty_print(q) }
515
425
 
516
426
  expect(q.output).to match(/
517
- #<Airbrake::Notifier:0x\w+\s
427
+ #<Airbrake::NoticeNotifier:0x\w+\s
518
428
  project_id="\d+"\s
519
429
  project_key=".+"\s
520
430
  host="http.+"\s
@@ -1,6 +1,4 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe Airbrake::Notifier do
1
+ RSpec.describe Airbrake::NoticeNotifier do
4
2
  def expect_a_request_with_body(body)
5
3
  expect(a_request(:post, endpoint).with(body: body)).to have_been_made.once
6
4
  end
@@ -13,27 +11,31 @@ RSpec.describe Airbrake::Notifier do
13
11
  "https://api.airbrake.io/api/v3/projects/#{project_id}/notices"
14
12
  end
15
13
 
16
- let(:airbrake_params) do
14
+ let(:user_params) do
17
15
  { project_id: project_id,
18
16
  project_key: project_key,
19
17
  logger: Logger.new(StringIO.new) }
20
18
  end
21
19
 
20
+ let(:params) { {} }
22
21
  let(:ex) { AirbrakeTestError.new }
22
+ let(:config) { Airbrake::Config.new(user_params.merge(params)) }
23
+
24
+ subject { described_class.new(config) }
23
25
 
24
26
  before do
25
27
  stub_request(:post, endpoint).to_return(status: 201, body: '{}')
26
- @airbrake = described_class.new(airbrake_params)
27
28
  end
28
29
 
29
30
  describe "options" do
30
31
  describe ":host" do
31
32
  context "when custom" do
32
33
  shared_examples 'endpoint' do |host, endpoint, title|
34
+ let(:params) { { host: host } }
35
+
33
36
  example(title) do
34
37
  stub_request(:post, endpoint).to_return(status: 201, body: '{}')
35
- @airbrake = described_class.new(airbrake_params.merge(host: host))
36
- @airbrake.notify_sync(ex)
38
+ subject.notify_sync(ex)
37
39
 
38
40
  expect(a_request(:post, endpoint)).to have_been_made.once
39
41
  end
@@ -68,9 +70,10 @@ RSpec.describe Airbrake::Notifier do
68
70
  end
69
71
 
70
72
  describe ":root_directory" do
73
+ let(:params) { { root_directory: '/home/kyrylo/code' } }
74
+
71
75
  it "filters out frames" do
72
- params = airbrake_params.merge(root_directory: '/home/kyrylo/code')
73
- airbrake = described_class.new(params)
76
+ airbrake = described_class.new(config)
74
77
  airbrake.notify_sync(ex)
75
78
 
76
79
  expect(
@@ -81,11 +84,10 @@ RSpec.describe Airbrake::Notifier do
81
84
 
82
85
  context "when present and is a" do
83
86
  shared_examples 'root directory' do |dir|
84
- it "being included into the notice's payload" do
85
- params = airbrake_params.merge(root_directory: dir)
86
- airbrake = described_class.new(params)
87
- airbrake.notify_sync(ex)
87
+ let(:params) { { root_directory: dir } }
88
88
 
89
+ it "being included into the notice's payload" do
90
+ subject.notify_sync(ex)
89
91
  expect(
90
92
  a_request(:post, endpoint).
91
93
  with(body: %r{"rootDirectory":"/bingo/bango"})
@@ -121,6 +123,13 @@ RSpec.describe Airbrake::Notifier do
121
123
  password: 'password' }
122
124
  end
123
125
 
126
+ let(:params) do
127
+ {
128
+ proxy: proxy_params,
129
+ host: "http://localhost:#{proxy.config[:Port]}"
130
+ }
131
+ end
132
+
124
133
  before do
125
134
  proxy.mount_proc '/' do |req, res|
126
135
  requests << req
@@ -129,13 +138,6 @@ RSpec.describe Airbrake::Notifier do
129
138
  end
130
139
 
131
140
  Thread.new { proxy.start }
132
-
133
- params = airbrake_params.merge(
134
- proxy: proxy_params,
135
- host: "http://localhost:#{proxy.config[:Port]}"
136
- )
137
-
138
- @airbrake = described_class.new(params)
139
141
  end
140
142
 
141
143
  after { proxy.stop }
@@ -147,7 +149,7 @@ RSpec.describe Airbrake::Notifier do
147
149
  "safe to run this test on 2.6+ once we upgrade to Webmock 3.5+"
148
150
  )
149
151
  end
150
- @airbrake.notify_sync(ex)
152
+ subject.notify_sync(ex)
151
153
 
152
154
  proxied_request = requests.pop(true)
153
155
 
@@ -163,11 +165,10 @@ RSpec.describe Airbrake::Notifier do
163
165
 
164
166
  describe ":environment" do
165
167
  context "when present" do
166
- it "being included into the notice's payload" do
167
- params = airbrake_params.merge(environment: :production)
168
- airbrake = described_class.new(params)
169
- airbrake.notify_sync(ex)
168
+ let(:params) { { environment: :production } }
170
169
 
170
+ it "being included into the notice's payload" do
171
+ subject.notify_sync(ex)
171
172
  expect(
172
173
  a_request(:post, endpoint).
173
174
  with(body: /"context":{.*"environment":"production".*}/)
@@ -178,19 +179,19 @@ RSpec.describe Airbrake::Notifier do
178
179
 
179
180
  describe ":ignore_environments" do
180
181
  shared_examples 'sent notice' do |params|
181
- it "sends a notice" do
182
- airbrake = described_class.new(airbrake_params.merge(params))
183
- airbrake.notify_sync(ex)
182
+ let(:params) { params }
184
183
 
184
+ it "sends a notice" do
185
+ subject.notify_sync(ex)
185
186
  expect(a_request(:post, endpoint)).to have_been_made
186
187
  end
187
188
  end
188
189
 
189
190
  shared_examples 'ignored notice' do |params|
190
- it "ignores exceptions occurring in envs that were not configured" do
191
- airbrake = described_class.new(airbrake_params.merge(params))
192
- airbrake.notify_sync(ex)
191
+ let(:params) { params }
193
192
 
193
+ it "ignores exceptions occurring in envs that were not configured" do
194
+ subject.notify_sync(ex)
194
195
  expect(a_request(:post, endpoint)).not_to have_been_made
195
196
  end
196
197
  end
@@ -213,10 +214,8 @@ RSpec.describe Airbrake::Notifier do
213
214
  include_examples 'ignored notice', params
214
215
 
215
216
  it "returns early and doesn't try to parse the given exception" do
216
- airbrake = described_class.new(airbrake_params.merge(params))
217
-
218
217
  expect(Airbrake::Notice).not_to receive(:new)
219
- expect(airbrake.notify_sync(ex)).
218
+ expect(subject.notify_sync(ex)).
220
219
  to eq('error' => "The 'development' environment is ignored")
221
220
  expect(a_request(:post, endpoint)).not_to have_been_made
222
221
  end
@@ -246,15 +245,17 @@ RSpec.describe Airbrake::Notifier do
246
245
  # Fixes https://github.com/airbrake/airbrake-ruby/issues/276
247
246
  context "when specified along with :whitelist_keys" do
248
247
  context "and when context payload is present" do
249
- it "sends a notice" do
250
- params = {
248
+ let(:params) do
249
+ {
251
250
  blacklist_keys: %i[password password_confirmation],
252
251
  whitelist_keys: [:email, /user/i, 'account_id']
253
252
  }
254
- airbrake = described_class.new(airbrake_params.merge(params))
255
- notice = airbrake.build_notice(ex)
253
+ end
254
+
255
+ it "sends a notice" do
256
+ notice = subject.build_notice(ex)
256
257
  notice[:context][:headers] = 'banana'
257
- airbrake.notify_sync(notice)
258
+ subject.notify_sync(notice)
258
259
 
259
260
  expect(a_request(:post, endpoint)).to have_been_made
260
261
  end