airbrake-ruby 5.0.1-java → 5.2.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby.rb +4 -2
  3. data/lib/airbrake-ruby/async_sender.rb +3 -1
  4. data/lib/airbrake-ruby/config.rb +15 -6
  5. data/lib/airbrake-ruby/config/processor.rb +7 -20
  6. data/lib/airbrake-ruby/context.rb +51 -0
  7. data/lib/airbrake-ruby/file_cache.rb +1 -1
  8. data/lib/airbrake-ruby/filter_chain.rb +2 -0
  9. data/lib/airbrake-ruby/filters/context_filter.rb +4 -5
  10. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +1 -1
  11. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +1 -1
  12. data/lib/airbrake-ruby/filters/git_revision_filter.rb +1 -1
  13. data/lib/airbrake-ruby/filters/keys_filter.rb +2 -2
  14. data/lib/airbrake-ruby/filters/sql_filter.rb +2 -2
  15. data/lib/airbrake-ruby/filters/thread_filter.rb +2 -3
  16. data/lib/airbrake-ruby/grouppable.rb +1 -1
  17. data/lib/airbrake-ruby/ignorable.rb +0 -2
  18. data/lib/airbrake-ruby/mergeable.rb +1 -1
  19. data/lib/airbrake-ruby/notice_notifier.rb +3 -4
  20. data/lib/airbrake-ruby/performance_notifier.rb +1 -2
  21. data/lib/airbrake-ruby/remote_settings.rb +10 -50
  22. data/lib/airbrake-ruby/remote_settings/callback.rb +44 -0
  23. data/lib/airbrake-ruby/remote_settings/settings_data.rb +3 -8
  24. data/lib/airbrake-ruby/tdigest.rb +7 -6
  25. data/lib/airbrake-ruby/thread_pool.rb +5 -3
  26. data/lib/airbrake-ruby/timed_trace.rb +1 -3
  27. data/lib/airbrake-ruby/version.rb +2 -2
  28. data/spec/airbrake_spec.rb +139 -76
  29. data/spec/async_sender_spec.rb +10 -8
  30. data/spec/backtrace_spec.rb +13 -10
  31. data/spec/benchmark_spec.rb +5 -3
  32. data/spec/code_hunk_spec.rb +24 -15
  33. data/spec/config/processor_spec.rb +29 -87
  34. data/spec/config/validator_spec.rb +5 -2
  35. data/spec/config_spec.rb +26 -17
  36. data/spec/context_spec.rb +54 -0
  37. data/spec/deploy_notifier_spec.rb +6 -4
  38. data/spec/file_cache_spec.rb +1 -0
  39. data/spec/filter_chain_spec.rb +29 -24
  40. data/spec/filters/context_filter_spec.rb +14 -5
  41. data/spec/filters/dependency_filter_spec.rb +3 -1
  42. data/spec/filters/exception_attributes_filter_spec.rb +5 -3
  43. data/spec/filters/gem_root_filter_spec.rb +5 -2
  44. data/spec/filters/git_last_checkout_filter_spec.rb +10 -12
  45. data/spec/filters/git_repository_filter.rb +9 -9
  46. data/spec/filters/git_revision_filter_spec.rb +19 -19
  47. data/spec/filters/keys_allowlist_spec.rb +25 -16
  48. data/spec/filters/keys_blocklist_spec.rb +25 -18
  49. data/spec/filters/root_directory_filter_spec.rb +3 -3
  50. data/spec/filters/sql_filter_spec.rb +26 -26
  51. data/spec/filters/system_exit_filter_spec.rb +4 -2
  52. data/spec/filters/thread_filter_spec.rb +16 -14
  53. data/spec/loggable_spec.rb +2 -2
  54. data/spec/monotonic_time_spec.rb +8 -6
  55. data/spec/nested_exception_spec.rb +46 -46
  56. data/spec/notice_notifier/options_spec.rb +23 -13
  57. data/spec/notice_notifier_spec.rb +52 -47
  58. data/spec/notice_spec.rb +6 -2
  59. data/spec/performance_notifier_spec.rb +67 -60
  60. data/spec/promise_spec.rb +38 -32
  61. data/spec/remote_settings/callback_spec.rb +162 -0
  62. data/spec/remote_settings/settings_data_spec.rb +23 -53
  63. data/spec/remote_settings_spec.rb +42 -75
  64. data/spec/response_spec.rb +34 -12
  65. data/spec/stashable_spec.rb +5 -5
  66. data/spec/stat_spec.rb +7 -5
  67. data/spec/sync_sender_spec.rb +49 -16
  68. data/spec/tdigest_spec.rb +61 -56
  69. data/spec/thread_pool_spec.rb +65 -55
  70. data/spec/time_truncate_spec.rb +4 -2
  71. data/spec/timed_trace_spec.rb +32 -30
  72. data/spec/truncator_spec.rb +72 -43
  73. metadata +53 -47
data/spec/tdigest_spec.rb CHANGED
@@ -1,16 +1,18 @@
1
1
  RSpec.describe Airbrake::TDigest do
2
+ subject(:tdigest) { described_class.new }
3
+
2
4
  describe "byte serialization" do
3
5
  it "loads serialized data" do
4
- subject.push(60, 100)
5
- 10.times { subject.push(rand * 100) }
6
- bytes = subject.as_bytes
6
+ tdigest.push(60, 100)
7
+ 10.times { tdigest.push(rand * 100) }
8
+ bytes = tdigest.as_bytes
7
9
  new_tdigest = described_class.from_bytes(bytes)
8
- expect(new_tdigest.percentile(0.9)).to eq(subject.percentile(0.9))
10
+ expect(new_tdigest.percentile(0.9)).to eq(tdigest.percentile(0.9))
9
11
  expect(new_tdigest.as_bytes).to eq(bytes)
10
12
  end
11
13
 
12
14
  it "handles zero size" do
13
- bytes = subject.as_bytes
15
+ bytes = tdigest.as_bytes
14
16
  expect(described_class.from_bytes(bytes).size).to be_zero
15
17
  end
16
18
 
@@ -24,76 +26,75 @@ RSpec.describe Airbrake::TDigest do
24
26
 
25
27
  describe "small byte serialization" do
26
28
  it "loads serialized data" do
27
- 10.times { subject.push(10) }
28
- bytes = subject.as_small_bytes
29
+ 10.times { tdigest.push(10) }
30
+ bytes = tdigest.as_small_bytes
29
31
  new_tdigest = described_class.from_bytes(bytes)
30
32
  # Expect some rounding error due to compression
31
33
  expect(new_tdigest.percentile(0.9).round(5)).to eq(
32
- subject.percentile(0.9).round(5),
34
+ tdigest.percentile(0.9).round(5),
33
35
  )
34
36
  expect(new_tdigest.as_small_bytes).to eq(bytes)
35
37
  end
36
38
 
37
39
  it "handles zero size" do
38
- bytes = subject.as_small_bytes
40
+ bytes = tdigest.as_small_bytes
39
41
  expect(described_class.from_bytes(bytes).size).to be_zero
40
42
  end
41
43
  end
42
44
 
43
45
  describe "JSON serialization" do
44
46
  it "loads serialized data" do
45
- subject.push(60, 100)
46
- json = subject.as_json
47
+ tdigest.push(60, 100)
48
+ json = tdigest.as_json
47
49
  new_tdigest = described_class.from_json(json)
48
- expect(new_tdigest.percentile(0.9)).to eq(subject.percentile(0.9))
50
+ expect(new_tdigest.percentile(0.9)).to eq(tdigest.percentile(0.9))
49
51
  end
50
52
  end
51
53
 
52
54
  describe "#percentile" do
53
55
  it "returns nil if empty" do
54
- expect(subject.percentile(0.90)).to be_nil # This should not crash
56
+ expect(tdigest.percentile(0.90)).to be_nil # This should not crash
55
57
  end
56
58
 
57
59
  it "raises ArgumentError of input not between 0 and 1" do
58
- expect { subject.percentile(1.1) }.to raise_error(ArgumentError)
60
+ expect { tdigest.percentile(1.1) }.to raise_error(ArgumentError)
59
61
  end
60
62
 
61
63
  describe "with only single value" do
62
64
  it "returns the value" do
63
- subject.push(60, 100)
64
- expect(subject.percentile(0.90)).to eq(60)
65
+ tdigest.push(60, 100)
66
+ expect(tdigest.percentile(0.90)).to eq(60)
65
67
  end
66
68
 
67
69
  it "returns 0 for all percentiles when only 0 present" do
68
- subject.push(0)
69
- expect(subject.percentile([0.0, 0.5, 1.0])).to eq([0, 0, 0])
70
+ tdigest.push(0)
71
+ expect(tdigest.percentile([0.0, 0.5, 1.0])).to eq([0, 0, 0])
70
72
  end
71
73
  end
72
74
 
73
75
  describe "with alot of uniformly distributed points" do
74
76
  it "has minimal error" do
75
77
  seed = srand(1234) # Makes the values a proper fixture
76
- N = 100_000
77
78
  maxerr = 0
78
- values = Array.new(N).map { rand }
79
+ values = Array.new(100_000).map { rand }
79
80
  srand(seed)
80
81
 
81
- subject.push(values)
82
- subject.compress!
82
+ tdigest.push(values)
83
+ tdigest.compress!
83
84
 
84
85
  0.step(1, 0.1).each do |i|
85
- q = subject.percentile(i)
86
+ q = tdigest.percentile(i)
86
87
  maxerr = [maxerr, (i - q).abs].max
87
88
  end
88
89
 
89
- expect(maxerr).to be < 0.01
90
+ expect(maxerr).to be < 0.02
90
91
  end
91
92
  end
92
93
  end
93
94
 
94
95
  describe "#push" do
95
96
  it "calls _cumulate so won't crash because of uninitialized mean_cumn" do
96
- subject.push(
97
+ tdigest.push(
97
98
  [
98
99
  125000000.0,
99
100
  104166666.66666666,
@@ -132,59 +133,62 @@ RSpec.describe Airbrake::TDigest do
132
133
  end
133
134
 
134
135
  it "does not blow up if data comes in sorted" do
135
- subject.push(0..10_000)
136
- expect(subject.centroids.size).to be < 5_000
137
- subject.compress!
138
- expect(subject.centroids.size).to be < 1_000
136
+ tdigest.push(0..10_000)
137
+ expect(tdigest.centroids.size).to be < 5_000
138
+ tdigest.compress!
139
+ expect(tdigest.centroids.size).to be < 1_000
139
140
  end
140
141
  end
141
142
 
142
143
  describe "#size" do
143
144
  it "reports the number of observations" do
144
145
  n = 10_000
145
- n.times { subject.push(rand) }
146
- subject.compress!
147
- expect(subject.size).to eq(n)
146
+ n.times { tdigest.push(rand) }
147
+ tdigest.compress!
148
+ expect(tdigest.size).to eq(n)
148
149
  end
149
150
  end
150
151
 
151
152
  describe "#+" do
152
153
  it "works with empty tdigests" do
153
154
  other = described_class.new(0.001, 50, 1.2)
154
- expect((subject + other).centroids.size).to eq(0)
155
+ expect((tdigest + other).centroids.size).to eq(0)
155
156
  end
156
157
 
157
158
  describe "adding two tdigests" do
159
+ let(:other) { described_class.new(0.001, 50, 1.2) }
160
+
158
161
  before do
159
- @other = described_class.new(0.001, 50, 1.2)
160
- [subject, @other].each do |td|
162
+ [tdigest, other].each do |td|
161
163
  td.push(60, 100)
162
164
  10.times { td.push(rand * 100) }
163
165
  end
164
166
  end
165
167
 
168
+ # rubocop:disable RSpec/MultipleExpectations
166
169
  it "has the parameters of the left argument (the calling tdigest)" do
167
- new_tdigest = subject + @other
170
+ new_tdigest = tdigest + other
168
171
  expect(new_tdigest.instance_variable_get(:@delta)).to eq(
169
- subject.instance_variable_get(:@delta),
172
+ tdigest.instance_variable_get(:@delta),
170
173
  )
171
174
  expect(new_tdigest.instance_variable_get(:@k)).to eq(
172
- subject.instance_variable_get(:@k),
175
+ tdigest.instance_variable_get(:@k),
173
176
  )
174
177
  expect(new_tdigest.instance_variable_get(:@cx)).to eq(
175
- subject.instance_variable_get(:@cx),
178
+ tdigest.instance_variable_get(:@cx),
176
179
  )
177
180
  end
181
+ # rubocop:enable RSpec/MultipleExpectations
178
182
 
179
183
  it "returns a tdigest with less than or equal centroids" do
180
- new_tdigest = subject + @other
184
+ new_tdigest = tdigest + other
181
185
  expect(new_tdigest.centroids.size)
182
- .to be <= subject.centroids.size + @other.centroids.size
186
+ .to be <= tdigest.centroids.size + other.centroids.size
183
187
  end
184
188
 
185
189
  it "has the size of the two digests combined" do
186
- new_tdigest = subject + @other
187
- expect(new_tdigest.size).to eq(subject.size + @other.size)
190
+ new_tdigest = tdigest + other
191
+ expect(new_tdigest.size).to eq(tdigest.size + other.size)
188
192
  end
189
193
  end
190
194
  end
@@ -192,14 +196,15 @@ RSpec.describe Airbrake::TDigest do
192
196
  describe "#merge!" do
193
197
  it "works with empty tdigests" do
194
198
  other = described_class.new(0.001, 50, 1.2)
195
- subject.merge!(other)
196
- expect(subject.centroids.size).to be_zero
199
+ tdigest.merge!(other)
200
+ expect(tdigest.centroids.size).to be_zero
197
201
  end
198
202
 
199
203
  describe "with populated tdigests" do
204
+ let(:other) { described_class.new(0.001, 50, 1.2) }
205
+
200
206
  before do
201
- @other = described_class.new(0.001, 50, 1.2)
202
- [subject, @other].each do |td|
207
+ [tdigest, other].each do |td|
203
208
  td.push(60, 100)
204
209
  10.times { td.push(rand * 100) }
205
210
  end
@@ -207,23 +212,23 @@ RSpec.describe Airbrake::TDigest do
207
212
 
208
213
  it "has the parameters of the calling tdigest" do
209
214
  vars = %i[@delta @k @cx]
210
- expected = Hash[vars.map { |v| [v, subject.instance_variable_get(v)] }]
211
- subject.merge!(@other)
215
+ expected = vars.map { |v| [v, tdigest.instance_variable_get(v)] }.to_h
216
+ tdigest.merge!(other)
212
217
  vars.each do |v|
213
- expect(subject.instance_variable_get(v)).to eq(expected[v])
218
+ expect(tdigest.instance_variable_get(v)).to eq(expected[v])
214
219
  end
215
220
  end
216
221
 
217
222
  it "returns a tdigest with less than or equal centroids" do
218
- combined_size = subject.centroids.size + @other.centroids.size
219
- subject.merge!(@other)
220
- expect(subject.centroids.size).to be <= combined_size
223
+ combined_size = tdigest.centroids.size + other.centroids.size
224
+ tdigest.merge!(other)
225
+ expect(tdigest.centroids.size).to be <= combined_size
221
226
  end
222
227
 
223
228
  it "has the size of the two digests combined" do
224
- combined_size = subject.size + @other.size
225
- subject.merge!(@other)
226
- expect(subject.size).to eq(combined_size)
229
+ combined_size = tdigest.size + other.size
230
+ tdigest.merge!(other)
231
+ expect(tdigest.size).to eq(combined_size)
227
232
  end
228
233
  end
229
234
  end
@@ -1,9 +1,5 @@
1
1
  RSpec.describe Airbrake::ThreadPool do
2
- let(:tasks) { [] }
3
- let(:worker_size) { 1 }
4
- let(:queue_size) { 2 }
5
-
6
- subject do
2
+ subject(:thread_pool) do
7
3
  described_class.new(
8
4
  worker_size: worker_size,
9
5
  queue_size: queue_size,
@@ -11,27 +7,27 @@ RSpec.describe Airbrake::ThreadPool do
11
7
  )
12
8
  end
13
9
 
10
+ let(:tasks) { [] }
11
+ let(:worker_size) { 1 }
12
+ let(:queue_size) { 2 }
13
+
14
14
  describe "#<<" do
15
15
  it "returns true" do
16
- retval = subject << 1
17
- subject.close
16
+ retval = thread_pool << 1
17
+ thread_pool.close
18
18
  expect(retval).to eq(true)
19
19
  end
20
20
 
21
21
  it "performs work in background" do
22
- subject << 2
23
- subject << 1
24
- subject.close
22
+ thread_pool << 2
23
+ thread_pool << 1
24
+ thread_pool.close
25
25
 
26
26
  expect(tasks).to eq([2, 1])
27
27
  end
28
28
 
29
29
  context "when the queue is full" do
30
- before do
31
- allow(subject).to receive(:backlog).and_return(queue_size)
32
- end
33
-
34
- subject do
30
+ subject(:full_thread_pool) do
35
31
  described_class.new(
36
32
  worker_size: 1,
37
33
  queue_size: 1,
@@ -39,26 +35,34 @@ RSpec.describe Airbrake::ThreadPool do
39
35
  )
40
36
  end
41
37
 
38
+ before do
39
+ # rubocop:disable RSpec/SubjectStub
40
+ allow(full_thread_pool).to receive(:backlog).and_return(queue_size)
41
+ # rubocop:enable RSpec/SubjectStub
42
+ end
43
+
42
44
  it "returns false" do
43
- retval = subject << 1
44
- subject.close
45
+ retval = full_thread_pool << 1
46
+ full_thread_pool.close
45
47
  expect(retval).to eq(false)
46
48
  end
47
49
 
48
50
  it "discards tasks" do
49
- 200.times { subject << 1 }
50
- subject.close
51
+ 200.times { full_thread_pool << 1 }
52
+ full_thread_pool.close
51
53
 
52
54
  expect(tasks.size).to be_zero
53
55
  end
54
56
 
55
57
  it "logs discarded tasks" do
56
- expect(Airbrake::Loggable.instance).to receive(:error).with(
58
+ allow(Airbrake::Loggable.instance).to receive(:error)
59
+
60
+ 15.times { full_thread_pool << 1 }
61
+ full_thread_pool.close
62
+
63
+ expect(Airbrake::Loggable.instance).to have_received(:error).with(
57
64
  /reached its capacity/,
58
65
  ).exactly(15).times
59
-
60
- 15.times { subject << 1 }
61
- subject.close
62
66
  end
63
67
  end
64
68
  end
@@ -67,22 +71,22 @@ RSpec.describe Airbrake::ThreadPool do
67
71
  let(:worker_size) { 0 }
68
72
 
69
73
  it "returns the size of the queue" do
70
- subject << 1
71
- expect(subject.backlog).to eq(1)
74
+ thread_pool << 1
75
+ expect(thread_pool.backlog).to eq(1)
72
76
  end
73
77
  end
74
78
 
75
79
  describe "#has_workers?" do
76
80
  it "returns false when the thread pool is not closed, but has 0 workers" do
77
- subject.workers.list.each do |worker|
81
+ thread_pool.workers.list.each do |worker|
78
82
  worker.kill.join
79
83
  end
80
- expect(subject).not_to have_workers
84
+ expect(thread_pool).not_to have_workers
81
85
  end
82
86
 
83
87
  it "returns false when the thread pool is closed" do
84
- subject.close
85
- expect(subject).not_to have_workers
88
+ thread_pool.close
89
+ expect(thread_pool).not_to have_workers
86
90
  end
87
91
 
88
92
  describe "forking behavior" do
@@ -93,20 +97,22 @@ RSpec.describe Airbrake::ThreadPool do
93
97
  end
94
98
  end
95
99
 
100
+ # rubocop:disable RSpec/MultipleExpectations
96
101
  it "respawns workers on fork()" do
97
- pid = fork { expect(subject).to have_workers }
102
+ pid = fork { expect(thread_pool).to have_workers }
98
103
  Process.wait(pid)
99
- subject.close
104
+ thread_pool.close
100
105
 
101
106
  expect(Process.last_status).to be_success
102
- expect(subject).not_to have_workers
107
+ expect(thread_pool).not_to have_workers
103
108
  end
109
+ # rubocop:enable RSpec/MultipleExpectations
104
110
 
105
111
  it "ensures that a new thread group is created per process" do
106
- subject << 1
107
- pid = fork { subject.has_workers? }
112
+ thread_pool << 1
113
+ pid = fork { thread_pool.has_workers? }
108
114
  Process.wait(pid)
109
- subject.close
115
+ thread_pool.close
110
116
 
111
117
  expect(Process.last_status).to be_success
112
118
  end
@@ -116,27 +122,29 @@ RSpec.describe Airbrake::ThreadPool do
116
122
  describe "#close" do
117
123
  context "when there's no work to do" do
118
124
  it "joins the spawned thread" do
119
- workers = subject.workers.list
125
+ workers = thread_pool.workers.list
120
126
  expect(workers).to all(be_alive)
121
127
 
122
- subject.close
128
+ thread_pool.close
123
129
  expect(workers).to all(be_stop)
124
130
  end
125
131
  end
126
132
 
127
133
  context "when there's some work to do" do
128
134
  it "logs how many tasks are left to process" do
129
- thread_pool = described_class.new(
130
- worker_size: 0, queue_size: 2, block: proc {},
131
- )
135
+ allow(Airbrake::Loggable.instance).to receive(:debug)
132
136
 
133
- expect(Airbrake::Loggable.instance).to receive(:debug).with(
134
- /waiting to process \d+ task\(s\)/,
137
+ thread_pool = described_class.new(
138
+ name: 'foo', worker_size: 0, queue_size: 2, block: proc {},
135
139
  )
136
- expect(Airbrake::Loggable.instance).to receive(:debug).with(/closed/)
137
140
 
138
141
  2.times { thread_pool << 1 }
139
142
  thread_pool.close
143
+
144
+ expect(Airbrake::Loggable.instance).to have_received(:debug).with(
145
+ /waiting to process \d+ task\(s\)/,
146
+ )
147
+ expect(Airbrake::Loggable.instance).to have_received(:debug).with(/foo.+closed/)
140
148
  end
141
149
 
142
150
  it "waits until the queue gets empty" do
@@ -144,7 +152,7 @@ RSpec.describe Airbrake::ThreadPool do
144
152
  worker_size: 1, queue_size: 2, block: proc {},
145
153
  )
146
154
 
147
- 10.times { subject << 1 }
155
+ 10.times { thread_pool << 1 }
148
156
  thread_pool.close
149
157
  expect(thread_pool.backlog).to be_zero
150
158
  end
@@ -153,17 +161,17 @@ RSpec.describe Airbrake::ThreadPool do
153
161
  context "when it was already closed" do
154
162
  it "doesn't increase the queue size" do
155
163
  begin
156
- subject.close
164
+ thread_pool.close
157
165
  rescue Airbrake::Error
158
166
  nil
159
167
  end
160
168
 
161
- expect(subject.backlog).to be_zero
169
+ expect(thread_pool.backlog).to be_zero
162
170
  end
163
171
 
164
172
  it "raises error" do
165
- subject.close
166
- expect { subject.close }.to raise_error(
173
+ thread_pool.close
174
+ expect { thread_pool.close }.to raise_error(
167
175
  Airbrake::Error, 'this thread pool is closed already'
168
176
  )
169
177
  end
@@ -171,17 +179,19 @@ RSpec.describe Airbrake::ThreadPool do
171
179
  end
172
180
 
173
181
  describe "#spawn_workers" do
174
- it "spawns alive threads in an enclosed ThreadGroup" do
175
- expect(subject.workers).to be_a(ThreadGroup)
176
- expect(subject.workers.list).to all(be_alive)
177
- expect(subject.workers).to be_enclosed
182
+ after { thread_pool.close }
183
+
184
+ it "spawns an enclosed thread group" do
185
+ expect(thread_pool.workers).to be_a(ThreadGroup)
186
+ expect(thread_pool.workers).to be_enclosed
187
+ end
178
188
 
179
- subject.close
189
+ it "spawns threads that are alive" do
190
+ expect(thread_pool.workers.list).to all(be_alive)
180
191
  end
181
192
 
182
193
  it "spawns exactly `workers_size` workers" do
183
- expect(subject.workers.list.size).to eq(worker_size)
184
- subject.close
194
+ expect(thread_pool.workers.list.size).to eq(worker_size)
185
195
  end
186
196
  end
187
197
  end