airbrake-ruby 3.2.2-java

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 (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