airbrake-ruby 3.1.0 → 3.2.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.
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