airbrake-ruby 3.2.2-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/lib/airbrake-ruby.rb +554 -0
  3. data/lib/airbrake-ruby/async_sender.rb +119 -0
  4. data/lib/airbrake-ruby/backtrace.rb +194 -0
  5. data/lib/airbrake-ruby/code_hunk.rb +53 -0
  6. data/lib/airbrake-ruby/config.rb +238 -0
  7. data/lib/airbrake-ruby/config/validator.rb +63 -0
  8. data/lib/airbrake-ruby/deploy_notifier.rb +47 -0
  9. data/lib/airbrake-ruby/file_cache.rb +48 -0
  10. data/lib/airbrake-ruby/filter_chain.rb +95 -0
  11. data/lib/airbrake-ruby/filters/context_filter.rb +29 -0
  12. data/lib/airbrake-ruby/filters/dependency_filter.rb +31 -0
  13. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +45 -0
  14. data/lib/airbrake-ruby/filters/gem_root_filter.rb +33 -0
  15. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +90 -0
  16. data/lib/airbrake-ruby/filters/git_repository_filter.rb +42 -0
  17. data/lib/airbrake-ruby/filters/git_revision_filter.rb +66 -0
  18. data/lib/airbrake-ruby/filters/keys_blacklist.rb +50 -0
  19. data/lib/airbrake-ruby/filters/keys_filter.rb +140 -0
  20. data/lib/airbrake-ruby/filters/keys_whitelist.rb +49 -0
  21. data/lib/airbrake-ruby/filters/root_directory_filter.rb +28 -0
  22. data/lib/airbrake-ruby/filters/sql_filter.rb +104 -0
  23. data/lib/airbrake-ruby/filters/system_exit_filter.rb +23 -0
  24. data/lib/airbrake-ruby/filters/thread_filter.rb +92 -0
  25. data/lib/airbrake-ruby/hash_keyable.rb +37 -0
  26. data/lib/airbrake-ruby/ignorable.rb +44 -0
  27. data/lib/airbrake-ruby/nested_exception.rb +39 -0
  28. data/lib/airbrake-ruby/notice.rb +165 -0
  29. data/lib/airbrake-ruby/notice_notifier.rb +228 -0
  30. data/lib/airbrake-ruby/performance_notifier.rb +161 -0
  31. data/lib/airbrake-ruby/promise.rb +99 -0
  32. data/lib/airbrake-ruby/response.rb +71 -0
  33. data/lib/airbrake-ruby/stat.rb +56 -0
  34. data/lib/airbrake-ruby/sync_sender.rb +111 -0
  35. data/lib/airbrake-ruby/tdigest.rb +393 -0
  36. data/lib/airbrake-ruby/time_truncate.rb +17 -0
  37. data/lib/airbrake-ruby/truncator.rb +115 -0
  38. data/lib/airbrake-ruby/version.rb +6 -0
  39. data/spec/airbrake_spec.rb +171 -0
  40. data/spec/async_sender_spec.rb +154 -0
  41. data/spec/backtrace_spec.rb +438 -0
  42. data/spec/code_hunk_spec.rb +118 -0
  43. data/spec/config/validator_spec.rb +189 -0
  44. data/spec/config_spec.rb +281 -0
  45. data/spec/deploy_notifier_spec.rb +41 -0
  46. data/spec/file_cache.rb +36 -0
  47. data/spec/filter_chain_spec.rb +83 -0
  48. data/spec/filters/context_filter_spec.rb +25 -0
  49. data/spec/filters/dependency_filter_spec.rb +14 -0
  50. data/spec/filters/exception_attributes_filter_spec.rb +63 -0
  51. data/spec/filters/gem_root_filter_spec.rb +44 -0
  52. data/spec/filters/git_last_checkout_filter_spec.rb +48 -0
  53. data/spec/filters/git_repository_filter.rb +53 -0
  54. data/spec/filters/git_revision_filter_spec.rb +126 -0
  55. data/spec/filters/keys_blacklist_spec.rb +236 -0
  56. data/spec/filters/keys_whitelist_spec.rb +205 -0
  57. data/spec/filters/root_directory_filter_spec.rb +42 -0
  58. data/spec/filters/sql_filter_spec.rb +219 -0
  59. data/spec/filters/system_exit_filter_spec.rb +14 -0
  60. data/spec/filters/thread_filter_spec.rb +279 -0
  61. data/spec/fixtures/notroot.txt +7 -0
  62. data/spec/fixtures/project_root/code.rb +221 -0
  63. data/spec/fixtures/project_root/empty_file.rb +0 -0
  64. data/spec/fixtures/project_root/long_line.txt +1 -0
  65. data/spec/fixtures/project_root/short_file.rb +3 -0
  66. data/spec/fixtures/project_root/vendor/bundle/ignored_file.rb +5 -0
  67. data/spec/helpers.rb +9 -0
  68. data/spec/ignorable_spec.rb +14 -0
  69. data/spec/nested_exception_spec.rb +75 -0
  70. data/spec/notice_notifier_spec.rb +436 -0
  71. data/spec/notice_notifier_spec/options_spec.rb +266 -0
  72. data/spec/notice_spec.rb +297 -0
  73. data/spec/performance_notifier_spec.rb +287 -0
  74. data/spec/promise_spec.rb +165 -0
  75. data/spec/response_spec.rb +82 -0
  76. data/spec/spec_helper.rb +102 -0
  77. data/spec/stat_spec.rb +35 -0
  78. data/spec/sync_sender_spec.rb +140 -0
  79. data/spec/tdigest_spec.rb +230 -0
  80. data/spec/time_truncate_spec.rb +13 -0
  81. data/spec/truncator_spec.rb +238 -0
  82. metadata +278 -0
@@ -0,0 +1,287 @@
1
+ RSpec.describe Airbrake::PerformanceNotifier do
2
+ let(:routes) { 'https://api.airbrake.io/api/v5/projects/1/routes-stats' }
3
+ let(:queries) { 'https://api.airbrake.io/api/v5/projects/1/queries-stats' }
4
+
5
+ let(:config) do
6
+ Airbrake::Config.new(
7
+ project_id: 1,
8
+ project_key: 'banana',
9
+ performance_stats: true,
10
+ performance_stats_flush_period: 0
11
+ )
12
+ end
13
+
14
+ subject { described_class.new(config) }
15
+
16
+ before do
17
+ stub_request(:put, routes).to_return(status: 200, body: '')
18
+ stub_request(:put, queries).to_return(status: 200, body: '')
19
+ end
20
+
21
+ describe "#notify" do
22
+ it "rounds time to the floor minute" do
23
+ subject.notify(
24
+ Airbrake::Request.new(
25
+ method: 'GET',
26
+ route: '/foo',
27
+ status_code: 200,
28
+ start_time: Time.new(2018, 1, 1, 0, 0, 20, 0)
29
+ )
30
+ )
31
+ expect(
32
+ a_request(:put, routes).with(body: /"time":"2018-01-01T00:00:00\+00:00"/)
33
+ ).to have_been_made
34
+ end
35
+
36
+ it "increments routes with the same key" do
37
+ subject.notify(
38
+ Airbrake::Request.new(
39
+ method: 'GET',
40
+ route: '/foo',
41
+ status_code: 200,
42
+ start_time: Time.new(2018, 1, 1, 0, 0, 20, 0)
43
+ )
44
+ )
45
+ subject.notify(
46
+ Airbrake::Request.new(
47
+ method: 'GET',
48
+ route: '/foo',
49
+ status_code: 200,
50
+ start_time: Time.new(2018, 1, 1, 0, 0, 50, 0)
51
+ )
52
+ )
53
+ expect(
54
+ a_request(:put, routes).with(body: /"count":2/)
55
+ ).to have_been_made
56
+ end
57
+
58
+ it "groups routes by time" do
59
+ subject.notify(
60
+ Airbrake::Request.new(
61
+ method: 'GET',
62
+ route: '/foo',
63
+ status_code: 200,
64
+ start_time: Time.new(2018, 1, 1, 0, 0, 49, 0),
65
+ end_time: Time.new(2018, 1, 1, 0, 0, 50, 0)
66
+ )
67
+ )
68
+ subject.notify(
69
+ Airbrake::Request.new(
70
+ method: 'GET',
71
+ route: '/foo',
72
+ status_code: 200,
73
+ start_time: Time.new(2018, 1, 1, 0, 1, 49, 0),
74
+ end_time: Time.new(2018, 1, 1, 0, 1, 55, 0)
75
+ )
76
+ )
77
+ expect(
78
+ a_request(:put, routes).with(
79
+ body: %r|\A
80
+ {"routes":\[
81
+ {"method":"GET","route":"/foo","statusCode":200,
82
+ "time":"2018-01-01T00:00:00\+00:00","count":1,"sum":1000.0,
83
+ "sumsq":1000000.0,"tdigest":"AAAAAkA0AAAAAAAAAAAAAUR6AAAB"},
84
+ {"method":"GET","route":"/foo","statusCode":200,
85
+ "time":"2018-01-01T00:01:00\+00:00","count":1,"sum":6000.0,
86
+ "sumsq":36000000.0,"tdigest":"AAAAAkA0AAAAAAAAAAAAAUW7gAAB"}\]}
87
+ \z|x
88
+ )
89
+ ).to have_been_made
90
+ end
91
+
92
+ it "groups routes by route key" do
93
+ subject.notify(
94
+ Airbrake::Request.new(
95
+ method: 'GET',
96
+ route: '/foo',
97
+ status_code: 200,
98
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
99
+ end_time: Time.new(2018, 1, 1, 0, 50, 0, 0)
100
+ )
101
+ )
102
+ subject.notify(
103
+ Airbrake::Request.new(
104
+ method: 'POST',
105
+ route: '/foo',
106
+ status_code: 200,
107
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
108
+ end_time: Time.new(2018, 1, 1, 0, 50, 0, 0)
109
+ )
110
+ )
111
+ expect(
112
+ a_request(:put, routes).with(
113
+ body: %r|\A
114
+ {"routes":\[
115
+ {"method":"GET","route":"/foo","statusCode":200,
116
+ "time":"2018-01-01T00:49:00\+00:00","count":1,"sum":60000.0,
117
+ "sumsq":3600000000.0,"tdigest":"AAAAAkA0AAAAAAAAAAAAAUdqYAAB"},
118
+ {"method":"POST","route":"/foo","statusCode":200,
119
+ "time":"2018-01-01T00:49:00\+00:00","count":1,"sum":60000.0,
120
+ "sumsq":3600000000.0,"tdigest":"AAAAAkA0AAAAAAAAAAAAAUdqYAAB"}\]}
121
+ \z|x
122
+ )
123
+ ).to have_been_made
124
+ end
125
+
126
+ it "returns a promise" do
127
+ promise = subject.notify(
128
+ Airbrake::Request.new(
129
+ method: 'GET',
130
+ route: '/foo',
131
+ status_code: 200,
132
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
133
+ )
134
+ )
135
+ expect(promise).to be_an(Airbrake::Promise)
136
+ expect(promise.value).to eq('' => nil)
137
+ end
138
+
139
+ it "doesn't send route stats when performance stats are disabled" do
140
+ notifier = described_class.new(
141
+ Airbrake::Config.new(
142
+ project_id: 1, project_key: '2', performance_stats: false
143
+ )
144
+ )
145
+ promise = notifier.notify(
146
+ Airbrake::Request.new(
147
+ method: 'GET', route: '/foo', status_code: 200, start_time: Time.new
148
+ )
149
+ )
150
+ expect(a_request(:put, routes)).not_to have_been_made
151
+ expect(promise.value).to eq(
152
+ 'error' => "The Performance Stats feature is disabled"
153
+ )
154
+ end
155
+
156
+ it "doesn't send route stats when current environment is ignored" do
157
+ notifier = described_class.new(
158
+ Airbrake::Config.new(
159
+ project_id: 1, project_key: '2', performance_stats: true,
160
+ environment: 'test', ignore_environments: %w[test]
161
+ )
162
+ )
163
+ promise = notifier.notify(
164
+ Airbrake::Request.new(
165
+ method: 'GET', route: '/foo', status_code: 200, start_time: Time.new
166
+ )
167
+ )
168
+ expect(a_request(:put, routes)).not_to have_been_made
169
+ expect(promise.value).to eq('error' => "The 'test' environment is ignored")
170
+ end
171
+
172
+ describe "payload grouping" do
173
+ let(:flush_period) { 0.5 }
174
+
175
+ let(:config) do
176
+ Airbrake::Config.new(
177
+ project_id: 1,
178
+ project_key: 'banana',
179
+ performance_stats: true,
180
+ performance_stats_flush_period: flush_period
181
+ )
182
+ end
183
+
184
+ it "groups payload by performance name and sends it separately" do
185
+ subject.notify(
186
+ Airbrake::Request.new(
187
+ method: 'GET',
188
+ route: '/foo',
189
+ status_code: 200,
190
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
191
+ )
192
+ )
193
+
194
+ subject.notify(
195
+ Airbrake::Query.new(
196
+ method: 'POST',
197
+ route: '/foo',
198
+ query: 'SELECT * FROM things',
199
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
200
+ )
201
+ )
202
+
203
+ sleep(flush_period + 0.5)
204
+
205
+ expect(a_request(:put, routes)).to have_been_made
206
+ expect(a_request(:put, queries)).to have_been_made
207
+ end
208
+ end
209
+
210
+ context "when an ignore filter was defined" do
211
+ before { subject.add_filter(&:ignore!) }
212
+
213
+ it "doesn't notify airbrake of requests" do
214
+ subject.notify(
215
+ Airbrake::Request.new(
216
+ method: 'GET',
217
+ route: '/foo',
218
+ status_code: 200,
219
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
220
+ )
221
+ )
222
+ expect(a_request(:put, routes)).not_to have_been_made
223
+ end
224
+
225
+ it "doesn't notify airbrake of queries" do
226
+ subject.notify(
227
+ Airbrake::Query.new(
228
+ method: 'POST',
229
+ route: '/foo',
230
+ query: 'SELECT * FROM things',
231
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
232
+ )
233
+ )
234
+ expect(a_request(:put, queries)).not_to have_been_made
235
+ end
236
+ end
237
+
238
+ context "when a filter that modifies payload was defined" do
239
+ before do
240
+ subject.add_filter do |resource|
241
+ resource.route = '[Filtered]'
242
+ end
243
+ end
244
+
245
+ it "notifies airbrake with modified payload" do
246
+ subject.notify(
247
+ Airbrake::Query.new(
248
+ method: 'POST',
249
+ route: '/foo',
250
+ query: 'SELECT * FROM things',
251
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
252
+ )
253
+ )
254
+ expect(
255
+ a_request(:put, queries).with(
256
+ body: /\A{"queries":\[{"method":"POST","route":"\[Filtered\]"/
257
+ )
258
+ ).to have_been_made
259
+ end
260
+ end
261
+ end
262
+
263
+ describe "#delete_filter" do
264
+ let(:filter) do
265
+ Class.new do
266
+ def call(resource)
267
+ resource.ignore!
268
+ end
269
+ end
270
+ end
271
+
272
+ before { subject.add_filter(filter.new) }
273
+
274
+ it "deletes a filter" do
275
+ subject.delete_filter(filter)
276
+ subject.notify(
277
+ Airbrake::Request.new(
278
+ method: 'POST',
279
+ route: '/foo',
280
+ status_code: 200,
281
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
282
+ )
283
+ )
284
+ expect(a_request(:put, routes)).to have_been_made
285
+ end
286
+ end
287
+ end
@@ -0,0 +1,165 @@
1
+ RSpec.describe Airbrake::Promise do
2
+ describe ".then" do
3
+ let(:resolved_with) { [] }
4
+ let(:rejected_with) { [] }
5
+
6
+ context "when it is not resolved" do
7
+ it "returns self" do
8
+ expect(subject.then {}).to eq(subject)
9
+ end
10
+
11
+ it "doesn't call the resolve callbacks yet" do
12
+ subject.then { resolved_with << 1 }.then { resolved_with << 2 }
13
+ expect(resolved_with).to be_empty
14
+ end
15
+ end
16
+
17
+ context "when it is resolved" do
18
+ shared_examples "then specs" do
19
+ it "returns self" do
20
+ expect(subject.then {}).to eq(subject)
21
+ end
22
+
23
+ it "yields the resolved value" do
24
+ yielded = nil
25
+ subject.then { |value| yielded = value }
26
+ expect(yielded).to eq('id' => '123')
27
+ end
28
+
29
+ it "calls the resolve callbacks" do
30
+ expect(resolved_with).to match_array([1, 2])
31
+ end
32
+
33
+ it "doesn't call the reject callbacks" do
34
+ expect(rejected_with).to be_empty
35
+ end
36
+ end
37
+
38
+ context "and there are some resolve and reject callbacks in place" do
39
+ before do
40
+ subject.then { resolved_with << 1 }.then { resolved_with << 2 }
41
+ subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
42
+ subject.resolve('id' => '123')
43
+ end
44
+
45
+ include_examples "then specs"
46
+
47
+ it "registers the resolve callbacks" do
48
+ subject.resolve('id' => '456')
49
+ expect(resolved_with).to match_array([1, 2, 1, 2])
50
+ end
51
+ end
52
+
53
+ context "and additional then callbacks are added" do
54
+ before do
55
+ subject.resolve('id' => '123')
56
+ subject.then { resolved_with << 1 }.then { resolved_with << 2 }
57
+ end
58
+
59
+ include_examples "then specs"
60
+
61
+ it "doesn't register new resolve callbacks" do
62
+ subject.resolve('id' => '456')
63
+ expect(resolved_with).to match_array([1, 2])
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ describe ".rescue" do
70
+ let(:resolved_with) { [] }
71
+ let(:rejected_with) { [] }
72
+
73
+ context "when it is not rejected" do
74
+ it "returns self" do
75
+ expect(subject.then {}).to eq(subject)
76
+ end
77
+
78
+ it "doesn't call the reject callbacks yet" do
79
+ subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
80
+ expect(rejected_with).to be_empty
81
+ end
82
+ end
83
+
84
+ context "when it is rejected" do
85
+ shared_examples "rescue specs" do
86
+ it "returns self" do
87
+ expect(subject.rescue {}).to eq(subject)
88
+ end
89
+
90
+ it "yields the rejected value" do
91
+ yielded = nil
92
+ subject.rescue { |value| yielded = value }
93
+ expect(yielded).to eq('bingo')
94
+ end
95
+
96
+ it "doesn't call the resolve callbacks" do
97
+ expect(resolved_with).to be_empty
98
+ end
99
+
100
+ it "calls the reject callbacks" do
101
+ expect(rejected_with).to match_array([1, 2])
102
+ end
103
+ end
104
+
105
+ context "and there are some resolve and reject callbacks in place" do
106
+ before do
107
+ subject.then { resolved_with << 1 }.then { resolved_with << 2 }
108
+ subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
109
+ subject.reject('bingo')
110
+ end
111
+
112
+ include_examples "rescue specs"
113
+
114
+ it "registers the reject callbacks" do
115
+ subject.reject('bingo again')
116
+ expect(rejected_with).to match_array([1, 2, 1, 2])
117
+ end
118
+ end
119
+
120
+ context "and additional reject callbacks are added" do
121
+ before do
122
+ subject.reject('bingo')
123
+ subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
124
+ end
125
+
126
+ include_examples "rescue specs"
127
+
128
+ it "doesn't register new reject callbacks" do
129
+ subject.reject('bingo again')
130
+ expect(rejected_with).to match_array([1, 2])
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ describe ".resolve" do
137
+ it "returns self" do
138
+ expect(subject.resolve(1)).to eq(subject)
139
+ end
140
+
141
+ it "executes callbacks attached with .then" do
142
+ array = []
143
+ subject.then { |notice_id| array << notice_id }.rescue { array << 999 }
144
+
145
+ expect(array.size).to be_zero
146
+ subject.resolve(1)
147
+ expect(array).to match_array([1])
148
+ end
149
+ end
150
+
151
+ describe ".reject" do
152
+ it "returns self" do
153
+ expect(subject.reject(1)).to eq(subject)
154
+ end
155
+
156
+ it "executes callbacks attached with .rescue" do
157
+ array = []
158
+ subject.then { array << 1 }.rescue { |error| array << error }
159
+
160
+ expect(array.size).to be_zero
161
+ subject.reject(999)
162
+ expect(array).to match_array([999])
163
+ end
164
+ end
165
+ end