airbrake-ruby 4.8.0 → 4.10.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby.rb +84 -5
  3. data/lib/airbrake-ruby/async_sender.rb +3 -3
  4. data/lib/airbrake-ruby/backtrace.rb +2 -2
  5. data/lib/airbrake-ruby/code_hunk.rb +1 -1
  6. data/lib/airbrake-ruby/config.rb +1 -1
  7. data/lib/airbrake-ruby/config/validator.rb +3 -3
  8. data/lib/airbrake-ruby/deploy_notifier.rb +1 -1
  9. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +2 -2
  10. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +2 -2
  11. data/lib/airbrake-ruby/filters/keys_filter.rb +1 -1
  12. data/lib/airbrake-ruby/filters/sql_filter.rb +3 -3
  13. data/lib/airbrake-ruby/filters/thread_filter.rb +1 -1
  14. data/lib/airbrake-ruby/grouppable.rb +12 -0
  15. data/lib/airbrake-ruby/inspectable.rb +2 -2
  16. data/lib/airbrake-ruby/mergeable.rb +12 -0
  17. data/lib/airbrake-ruby/notice.rb +7 -7
  18. data/lib/airbrake-ruby/notice_notifier.rb +1 -1
  19. data/lib/airbrake-ruby/performance_breakdown.rb +2 -1
  20. data/lib/airbrake-ruby/performance_notifier.rb +42 -21
  21. data/lib/airbrake-ruby/query.rb +3 -5
  22. data/lib/airbrake-ruby/queue.rb +52 -0
  23. data/lib/airbrake-ruby/request.rb +3 -5
  24. data/lib/airbrake-ruby/stat.rb +1 -1
  25. data/lib/airbrake-ruby/version.rb +1 -1
  26. data/spec/airbrake_spec.rb +135 -45
  27. data/spec/async_sender_spec.rb +4 -4
  28. data/spec/backtrace_spec.rb +18 -18
  29. data/spec/code_hunk_spec.rb +9 -9
  30. data/spec/config/validator_spec.rb +5 -5
  31. data/spec/config_spec.rb +5 -5
  32. data/spec/deploy_notifier_spec.rb +2 -2
  33. data/spec/filter_chain_spec.rb +1 -1
  34. data/spec/filters/dependency_filter_spec.rb +1 -1
  35. data/spec/filters/gem_root_filter_spec.rb +5 -5
  36. data/spec/filters/git_last_checkout_filter_spec.rb +1 -1
  37. data/spec/filters/git_repository_filter.rb +1 -1
  38. data/spec/filters/git_revision_filter_spec.rb +10 -10
  39. data/spec/filters/keys_blacklist_spec.rb +22 -22
  40. data/spec/filters/keys_whitelist_spec.rb +21 -21
  41. data/spec/filters/root_directory_filter_spec.rb +5 -5
  42. data/spec/filters/sql_filter_spec.rb +53 -53
  43. data/spec/filters/system_exit_filter_spec.rb +1 -1
  44. data/spec/filters/thread_filter_spec.rb +28 -28
  45. data/spec/fixtures/project_root/code.rb +9 -9
  46. data/spec/notice_notifier/options_spec.rb +12 -12
  47. data/spec/notice_notifier_spec.rb +17 -17
  48. data/spec/notice_spec.rb +5 -5
  49. data/spec/performance_notifier_spec.rb +171 -60
  50. data/spec/query_spec.rb +1 -1
  51. data/spec/queue_spec.rb +11 -0
  52. data/spec/request_spec.rb +1 -1
  53. data/spec/response_spec.rb +8 -8
  54. data/spec/spec_helper.rb +2 -2
  55. data/spec/stat_spec.rb +2 -2
  56. data/spec/sync_sender_spec.rb +12 -12
  57. data/spec/tdigest_spec.rb +6 -6
  58. data/spec/thread_pool_spec.rb +5 -5
  59. data/spec/timed_trace_spec.rb +1 -1
  60. data/spec/truncator_spec.rb +12 -12
  61. metadata +7 -2
@@ -10,7 +10,7 @@ module Airbrake
10
10
  NOTIFIER = {
11
11
  name: 'airbrake-ruby'.freeze,
12
12
  version: Airbrake::AIRBRAKE_RUBY_VERSION,
13
- url: 'https://github.com/airbrake/airbrake-ruby'.freeze
13
+ url: 'https://github.com/airbrake/airbrake-ruby'.freeze,
14
14
  }.freeze
15
15
 
16
16
  ##
@@ -19,7 +19,7 @@ module Airbrake
19
19
  CONTEXT = {
20
20
  os: RUBY_PLATFORM,
21
21
  language: "#{RUBY_ENGINE}/#{RUBY_VERSION}".freeze,
22
- notifier: NOTIFIER
22
+ notifier: NOTIFIER,
23
23
  }.freeze
24
24
 
25
25
  ##
@@ -38,7 +38,7 @@ module Airbrake
38
38
  IOError,
39
39
  NotImplementedError,
40
40
  JSON::GeneratorError,
41
- Encoding::UndefinedConversionError
41
+ Encoding::UndefinedConversionError,
42
42
  ].freeze
43
43
 
44
44
  # @return [Array<Symbol>] the list of keys that can be be overwritten with
@@ -71,10 +71,10 @@ module Airbrake
71
71
  errors: NestedException.new(config, exception).as_json,
72
72
  context: context,
73
73
  environment: {
74
- program_name: $PROGRAM_NAME
74
+ program_name: $PROGRAM_NAME,
75
75
  },
76
76
  session: {},
77
- params: params
77
+ params: params,
78
78
  }
79
79
  @stash = { exception: exception }
80
80
  @truncator = Airbrake::Truncator.new(PAYLOAD_MAX_SIZE)
@@ -170,7 +170,7 @@ module Airbrake
170
170
  # Make sure we always send hostname.
171
171
  hostname: HOSTNAME,
172
172
 
173
- severity: DEFAULT_SEVERITY
173
+ severity: DEFAULT_SEVERITY,
174
174
  }.merge(CONTEXT).delete_if { |_key, val| val.nil? || val.empty? }
175
175
  end
176
176
 
@@ -187,7 +187,7 @@ module Airbrake
187
187
  @config.logger.error(
188
188
  "#{LOG_LABEL} truncation failed. File an issue at " \
189
189
  "https://github.com/airbrake/airbrake-ruby " \
190
- "and attach the following payload: #{@payload}"
190
+ "and attach the following payload: #{@payload}",
191
191
  )
192
192
  end
193
193
 
@@ -202,7 +202,7 @@ module Airbrake
202
202
  attributes = exception.to_airbrake
203
203
  rescue StandardError => ex
204
204
  @config.logger.error(
205
- "#{LOG_LABEL} #{exception.class}#to_airbrake failed: #{ex.class}: #{ex}"
205
+ "#{LOG_LABEL} #{exception.class}#to_airbrake failed: #{ex.class}: #{ex}",
206
206
  )
207
207
  end
208
208
 
@@ -213,7 +213,7 @@ module Airbrake
213
213
  rescue TypeError
214
214
  @config.logger.error(
215
215
  "#{LOG_LABEL} #{exception.class}#to_airbrake failed:" \
216
- " #{attributes} must be a Hash"
216
+ " #{attributes} must be a Hash",
217
217
  )
218
218
  end
219
219
  end
@@ -15,7 +15,7 @@ RSpec.describe Airbrake::NoticeNotifier do
15
15
 
16
16
  Airbrake::Config.instance = Airbrake::Config.new(
17
17
  project_id: project_id,
18
- project_key: project_key
18
+ project_key: project_key,
19
19
  )
20
20
  end
21
21
 
@@ -64,7 +64,7 @@ RSpec.describe Airbrake::NoticeNotifier do
64
64
  describe ":root_directory" do
65
65
  before do
66
66
  subject.add_filter(
67
- Airbrake::Filters::RootDirectoryFilter.new('/home/kyrylo/code')
67
+ Airbrake::Filters::RootDirectoryFilter.new('/home/kyrylo/code'),
68
68
  )
69
69
  end
70
70
 
@@ -73,7 +73,7 @@ RSpec.describe Airbrake::NoticeNotifier do
73
73
 
74
74
  expect(
75
75
  a_request(:post, endpoint)
76
- .with(body: %r|{"file":"/PROJECT_ROOT/airbrake/ruby/spec/airbrake_spec.+|)
76
+ .with(body: %r|{"file":"/PROJECT_ROOT/airbrake/ruby/spec/airbrake_spec.+|),
77
77
  ).to have_been_made.once
78
78
  end
79
79
 
@@ -85,7 +85,7 @@ RSpec.describe Airbrake::NoticeNotifier do
85
85
  subject.notify_sync(ex)
86
86
  expect(
87
87
  a_request(:post, endpoint)
88
- .with(body: %r{"rootDirectory":"/bingo/bango"})
88
+ .with(body: %r{"rootDirectory":"/bingo/bango"}),
89
89
  ).to have_been_made.once
90
90
  end
91
91
  end
@@ -105,7 +105,7 @@ RSpec.describe Airbrake::NoticeNotifier do
105
105
  WEBrick::HTTPServer.new(
106
106
  Port: 0,
107
107
  Logger: WEBrick::Log.new('/dev/null'),
108
- AccessLog: []
108
+ AccessLog: [],
109
109
  )
110
110
  end
111
111
 
@@ -121,7 +121,7 @@ RSpec.describe Airbrake::NoticeNotifier do
121
121
  before do
122
122
  Airbrake::Config.instance.merge(
123
123
  proxy: proxy_params,
124
- host: "http://localhost:#{proxy.config[:Port]}"
124
+ host: "http://localhost:#{proxy.config[:Port]}",
125
125
  )
126
126
 
127
127
  proxy.mount_proc '/' do |req, res|
@@ -139,7 +139,7 @@ RSpec.describe Airbrake::NoticeNotifier do
139
139
  if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.6.0")
140
140
  skip(
141
141
  "We use Webmock 2, which doesn't support Ruby 2.6+. It's " \
142
- "safe to run this test on 2.6+ once we upgrade to Webmock 3.5+"
142
+ "safe to run this test on 2.6+ once we upgrade to Webmock 3.5+",
143
143
  )
144
144
  end
145
145
  subject.notify_sync(ex)
@@ -164,7 +164,7 @@ RSpec.describe Airbrake::NoticeNotifier do
164
164
  subject.notify_sync(ex)
165
165
  expect(
166
166
  a_request(:post, endpoint)
167
- .with(body: /"context":{.*"environment":"production".*}/)
167
+ .with(body: /"context":{.*"environment":"production".*}/),
168
168
  ).to have_been_made.once
169
169
  end
170
170
  end
@@ -192,7 +192,7 @@ RSpec.describe Airbrake::NoticeNotifier do
192
192
  context "when env is set and ignore_environments doesn't mention it" do
193
193
  params = {
194
194
  environment: :development,
195
- ignore_environments: [:production]
195
+ ignore_environments: [:production],
196
196
  }
197
197
 
198
198
  include_examples 'sent notice', params
@@ -201,7 +201,7 @@ RSpec.describe Airbrake::NoticeNotifier do
201
201
  context "when the current env and notify envs are the same" do
202
202
  params = {
203
203
  environment: :development,
204
- ignore_environments: %i[production development]
204
+ ignore_environments: %i[production development],
205
205
  }
206
206
 
207
207
  include_examples 'ignored notice', params
@@ -227,7 +227,7 @@ RSpec.describe Airbrake::NoticeNotifier do
227
227
  context "when ignore_environments specifies a Regexp pattern" do
228
228
  params = {
229
229
  environment: :testing,
230
- ignore_environments: ['staging', /test.+/]
230
+ ignore_environments: ['staging', /test.+/],
231
231
  }
232
232
 
233
233
  include_examples 'ignored notice', params
@@ -241,7 +241,7 @@ RSpec.describe Airbrake::NoticeNotifier do
241
241
  before do
242
242
  Airbrake::Config.instance.merge(
243
243
  blacklist_keys: %i[password password_confirmation],
244
- whitelist_keys: [:email, /user/i, 'account_id']
244
+ whitelist_keys: [:email, /user/i, 'account_id'],
245
245
  )
246
246
  end
247
247
 
@@ -4,7 +4,7 @@ RSpec.describe Airbrake::NoticeNotifier do
4
4
  project_id: 1,
5
5
  project_key: 'abc',
6
6
  logger: Logger.new('/dev/null'),
7
- performance_stats: true
7
+ performance_stats: true,
8
8
  )
9
9
  end
10
10
 
@@ -32,7 +32,7 @@ RSpec.describe Airbrake::NoticeNotifier do
32
32
  let(:body) do
33
33
  {
34
34
  'id' => '00054414-b147-6ffa-85d6-1524d83362a6',
35
- 'url' => 'http://localhost/locate/00054414-b147-6ffa-85d6-1524d83362a6'
35
+ 'url' => 'http://localhost/locate/00054414-b147-6ffa-85d6-1524d83362a6',
36
36
  }.to_json
37
37
  end
38
38
 
@@ -110,7 +110,7 @@ RSpec.describe Airbrake::NoticeNotifier do
110
110
  before do
111
111
  Airbrake::Config.instance.merge(
112
112
  environment: 'test',
113
- ignore_environments: %w[test]
113
+ ignore_environments: %w[test],
114
114
  )
115
115
  end
116
116
 
@@ -132,7 +132,7 @@ RSpec.describe Airbrake::NoticeNotifier do
132
132
  let(:body) do
133
133
  {
134
134
  'id' => '00054414-b147-6ffa-85d6-1524d83362a6',
135
- 'url' => 'http://localhost/locate/00054414-b147-6ffa-85d6-1524d83362a6'
135
+ 'url' => 'http://localhost/locate/00054414-b147-6ffa-85d6-1524d83362a6',
136
136
  }
137
137
  end
138
138
 
@@ -153,8 +153,8 @@ RSpec.describe Airbrake::NoticeNotifier do
153
153
  subject.notify_sync('foo', bingo: 'bango')
154
154
  expect(
155
155
  a_request(:post, endpoint).with(
156
- body: /"params":{.*"bingo":"bango".*}/
157
- )
156
+ body: /"params":{.*"bingo":"bango".*}/,
157
+ ),
158
158
  ).to have_been_made.once
159
159
  end
160
160
 
@@ -190,7 +190,7 @@ RSpec.describe Airbrake::NoticeNotifier do
190
190
  context "when the provided environment is ignored" do
191
191
  before do
192
192
  Airbrake::Config.instance.merge(
193
- environment: 'test', ignore_environments: %w[test]
193
+ environment: 'test', ignore_environments: %w[test],
194
194
  )
195
195
  end
196
196
 
@@ -256,7 +256,7 @@ RSpec.describe Airbrake::NoticeNotifier do
256
256
  it "returns the full generated backtrace" do
257
257
  backtrace = [
258
258
  "/lib/airbrake-ruby/a.rb:84:in `build_notice'",
259
- "/lib/airbrake-ruby/b.rb:124:in `send_notice'"
259
+ "/lib/airbrake-ruby/b.rb:124:in `send_notice'",
260
260
  ]
261
261
  allow(Kernel).to receive(:caller).and_return(backtrace)
262
262
 
@@ -265,8 +265,8 @@ RSpec.describe Airbrake::NoticeNotifier do
265
265
  expect(notice[:errors].first[:backtrace]).to eq(
266
266
  [
267
267
  { file: '/lib/airbrake-ruby/a.rb', line: 84, function: 'build_notice' },
268
- { file: '/lib/airbrake-ruby/b.rb', line: 124, function: 'send_notice' }
269
- ]
268
+ { file: '/lib/airbrake-ruby/b.rb', line: 124, function: 'send_notice' },
269
+ ],
270
270
  )
271
271
  end
272
272
  end
@@ -276,7 +276,7 @@ RSpec.describe Airbrake::NoticeNotifier do
276
276
  backtrace = [
277
277
  "/airbrake-ruby/lib/airbrake-ruby/a.rb:84:in `b'",
278
278
  "/airbrake-ruby/lib/foo/b.rb:84:in `build'",
279
- "/airbrake-ruby/lib/bar/c.rb:124:in `send'"
279
+ "/airbrake-ruby/lib/bar/c.rb:124:in `send'",
280
280
  ]
281
281
  allow(Kernel).to receive(:caller).and_return(backtrace)
282
282
 
@@ -285,8 +285,8 @@ RSpec.describe Airbrake::NoticeNotifier do
285
285
  expect(notice[:errors].first[:backtrace]).to eq(
286
286
  [
287
287
  { file: '/airbrake-ruby/lib/foo/b.rb', line: 84, function: 'build' },
288
- { file: '/airbrake-ruby/lib/bar/c.rb', line: 124, function: 'send' }
289
- ]
288
+ { file: '/airbrake-ruby/lib/bar/c.rb', line: 124, function: 'send' },
289
+ ],
290
290
  )
291
291
  end
292
292
  end
@@ -303,7 +303,7 @@ RSpec.describe Airbrake::NoticeNotifier do
303
303
  backtrace = [
304
304
  "org/jruby/RubyKernel.java:998:in `eval'",
305
305
  "/ruby/stdlib/irb/workspace.rb:87:in `evaluate'",
306
- "/ruby/stdlib/irb.rb:489:in `block in eval_input'"
306
+ "/ruby/stdlib/irb.rb:489:in `block in eval_input'",
307
307
  ]
308
308
  allow(Kernel).to receive(:caller).and_return(backtrace)
309
309
 
@@ -314,8 +314,8 @@ RSpec.describe Airbrake::NoticeNotifier do
314
314
  [
315
315
  { file: 'org/jruby/RubyKernel.java', line: 998, function: 'eval' },
316
316
  { file: '/ruby/stdlib/irb/workspace.rb', line: 87, function: 'evaluate' },
317
- { file: '/ruby/stdlib/irb.rb:489', line: 489, function: 'block in eval_input' }
318
- ]
317
+ { file: '/ruby/stdlib/irb.rb:489', line: 489, function: 'block in eval_input' },
318
+ ],
319
319
  )
320
320
  # rubocop:enable Metrics/LineLength
321
321
  end
@@ -330,7 +330,7 @@ RSpec.describe Airbrake::NoticeNotifier do
330
330
  it "raises error" do
331
331
  expect { subject.build_notice(Exception.new) }.to raise_error(
332
332
  Airbrake::Error,
333
- 'attempted to build Exception with closed Airbrake instance'
333
+ 'attempted to build Exception with closed Airbrake instance',
334
334
  )
335
335
  end
336
336
  end
@@ -17,7 +17,7 @@ RSpec.describe Airbrake::Notice do
17
17
  before do
18
18
  Airbrake::Config.instance.merge(
19
19
  app_version: "1.2.3",
20
- root_directory: "/one/two"
20
+ root_directory: "/one/two",
21
21
  )
22
22
  end
23
23
 
@@ -34,7 +34,7 @@ RSpec.describe Airbrake::Notice do
34
34
  context "when versions is empty" do
35
35
  it "doesn't set the 'versions' payload" do
36
36
  expect(notice.to_json).not_to match(
37
- /"context":{"versions":{"dep":"1.2.3"}}/
37
+ /"context":{"versions":{"dep":"1.2.3"}}/,
38
38
  )
39
39
  end
40
40
  end
@@ -43,7 +43,7 @@ RSpec.describe Airbrake::Notice do
43
43
  it "sets the 'versions' payload" do
44
44
  notice[:context][:versions] = { 'dep' => '1.2.3' }
45
45
  expect(notice.to_json).to match(
46
- /"context":{.*"versions":{"dep":"1.2.3"}.*}/
46
+ /"context":{.*"versions":{"dep":"1.2.3"}.*}/,
47
47
  )
48
48
  end
49
49
  end
@@ -192,7 +192,7 @@ RSpec.describe Airbrake::Notice do
192
192
  it "doesn't fail" do
193
193
  notice[:params] = { a: { b: { c: ObjectWithIoIvars.new } } }
194
194
  expect(notice.to_json).to match(
195
- /"params":{"a":{"b":{"c":".+ObjectWithIoIvars.+"}}.*}/
195
+ /"params":{"a":{"b":{"c":".+ObjectWithIoIvars.+"}}.*}/,
196
196
  )
197
197
  end
198
198
  end
@@ -201,7 +201,7 @@ RSpec.describe Airbrake::Notice do
201
201
  it "doesn't fail" do
202
202
  notice[:params] = { a: [[ObjectWithIoIvars.new]] }
203
203
  expect(notice.to_json).to match(
204
- /"params":{"a":\[\[".+ObjectWithIoIvars.+"\]\].*}/
204
+ /"params":{"a":\[\[".+ObjectWithIoIvars.+"\]\].*}/,
205
205
  )
206
206
  end
207
207
  end
@@ -2,18 +2,20 @@ RSpec.describe Airbrake::PerformanceNotifier do
2
2
  let(:routes) { 'https://api.airbrake.io/api/v5/projects/1/routes-stats' }
3
3
  let(:queries) { 'https://api.airbrake.io/api/v5/projects/1/queries-stats' }
4
4
  let(:breakdowns) { 'https://api.airbrake.io/api/v5/projects/1/routes-breakdowns' }
5
+ let(:queues) { 'https://api.airbrake.io/api/v5/projects/1/queues-stats' }
5
6
 
6
7
  before do
7
8
  stub_request(:put, routes).to_return(status: 200, body: '')
8
9
  stub_request(:put, queries).to_return(status: 200, body: '')
9
10
  stub_request(:put, breakdowns).to_return(status: 200, body: '')
11
+ stub_request(:put, queues).to_return(status: 200, body: '')
10
12
 
11
13
  Airbrake::Config.instance = Airbrake::Config.new(
12
14
  project_id: 1,
13
15
  project_key: 'banana',
14
16
  performance_stats: true,
15
17
  performance_stats_flush_period: 0,
16
- query_stats: true
18
+ query_stats: true,
17
19
  )
18
20
  end
19
21
 
@@ -28,8 +30,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
28
30
  file: 'foo.rb',
29
31
  line: 123,
30
32
  start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
31
- end_time: Time.new(2018, 1, 1, 0, 50, 0, 0)
32
- )
33
+ end_time: Time.new(2018, 1, 1, 0, 50, 0, 0),
34
+ ),
33
35
  )
34
36
  subject.close
35
37
 
@@ -47,7 +49,7 @@ RSpec.describe Airbrake::PerformanceNotifier do
47
49
  "sum":60000.0,
48
50
  "sumsq":3600000000.0,
49
51
  "tdigest":"AAAAAkA0AAAAAAAAAAAAAUdqYAAB"
50
- }\]}\z|x)
52
+ }\]}\z|x),
51
53
  ).to have_been_made
52
54
  end
53
55
 
@@ -58,8 +60,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
58
60
  route: '/foo',
59
61
  status_code: 200,
60
62
  start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
61
- end_time: Time.new(2018, 1, 1, 0, 50, 0, 0)
62
- )
63
+ end_time: Time.new(2018, 1, 1, 0, 50, 0, 0),
64
+ ),
63
65
  )
64
66
  subject.close
65
67
 
@@ -74,7 +76,7 @@ RSpec.describe Airbrake::PerformanceNotifier do
74
76
  "sum":60000.0,
75
77
  "sumsq":3600000000.0,
76
78
  "tdigest":"AAAAAkA0AAAAAAAAAAAAAUdqYAAB"
77
- }\]}\z|x)
79
+ }\]}\z|x),
78
80
  ).to have_been_made
79
81
  end
80
82
 
@@ -86,8 +88,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
86
88
  response_type: 'json',
87
89
  start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
88
90
  end_time: Time.new(2018, 1, 1, 0, 50, 0, 0),
89
- groups: { db: 131, view: 421 }
90
- )
91
+ groups: { db: 131, view: 421 },
92
+ ),
91
93
  )
92
94
  subject.close
93
95
 
@@ -116,7 +118,47 @@ RSpec.describe Airbrake::PerformanceNotifier do
116
118
  "tdigest":"AAAAAkA0AAAAAAAAAAAAAUPSgAAB"
117
119
  }
118
120
  }
119
- }\]}\z|x)
121
+ }\]}\z|x),
122
+ ).to have_been_made
123
+ end
124
+
125
+ it "sends full queue" do
126
+ subject.notify(
127
+ Airbrake::Queue.new(
128
+ queue: 'emails',
129
+ error_count: 2,
130
+ groups: { redis: 131, sql: 421 },
131
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
132
+ end_time: Time.new(2018, 1, 1, 0, 50, 0, 0),
133
+ ),
134
+ )
135
+ subject.close
136
+
137
+ expect(
138
+ a_request(:put, queues).with(body: /
139
+ \A{"queues":\[{
140
+ "queue":"emails",
141
+ "errorCount":2,
142
+ "time":"2018-01-01T00:49:00\+00:00",
143
+ "count":1,
144
+ "sum":60000.0,
145
+ "sumsq":3600000000.0,
146
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUdqYAAB",
147
+ "groups":{
148
+ "redis":{
149
+ "count":1,
150
+ "sum":131.0,
151
+ "sumsq":17161.0,
152
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUMDAAAB"
153
+ },
154
+ "sql":{
155
+ "count":1,
156
+ "sum":421.0,
157
+ "sumsq":177241.0,
158
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUPSgAAB"
159
+ }
160
+ }
161
+ }\]}\z/x),
120
162
  ).to have_been_made
121
163
  end
122
164
 
@@ -126,13 +168,13 @@ RSpec.describe Airbrake::PerformanceNotifier do
126
168
  method: 'GET',
127
169
  route: '/foo',
128
170
  status_code: 200,
129
- start_time: Time.new(2018, 1, 1, 0, 0, 20, 0)
130
- )
171
+ start_time: Time.new(2018, 1, 1, 0, 0, 20, 0),
172
+ ),
131
173
  )
132
174
  subject.close
133
175
 
134
176
  expect(
135
- a_request(:put, routes).with(body: /"time":"2018-01-01T00:00:00\+00:00"/)
177
+ a_request(:put, routes).with(body: /"time":"2018-01-01T00:00:00\+00:00"/),
136
178
  ).to have_been_made
137
179
  end
138
180
 
@@ -142,21 +184,21 @@ RSpec.describe Airbrake::PerformanceNotifier do
142
184
  method: 'GET',
143
185
  route: '/foo',
144
186
  status_code: 200,
145
- start_time: Time.new(2018, 1, 1, 0, 0, 20, 0)
146
- )
187
+ start_time: Time.new(2018, 1, 1, 0, 0, 20, 0),
188
+ ),
147
189
  )
148
190
  subject.notify(
149
191
  Airbrake::Request.new(
150
192
  method: 'GET',
151
193
  route: '/foo',
152
194
  status_code: 200,
153
- start_time: Time.new(2018, 1, 1, 0, 0, 50, 0)
154
- )
195
+ start_time: Time.new(2018, 1, 1, 0, 0, 50, 0),
196
+ ),
155
197
  )
156
198
  subject.close
157
199
 
158
200
  expect(
159
- a_request(:put, routes).with(body: /"count":2/)
201
+ a_request(:put, routes).with(body: /"count":2/),
160
202
  ).to have_been_made
161
203
  end
162
204
 
@@ -167,8 +209,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
167
209
  route: '/foo',
168
210
  status_code: 200,
169
211
  start_time: Time.new(2018, 1, 1, 0, 0, 49, 0),
170
- end_time: Time.new(2018, 1, 1, 0, 0, 50, 0)
171
- )
212
+ end_time: Time.new(2018, 1, 1, 0, 0, 50, 0),
213
+ ),
172
214
  )
173
215
  subject.notify(
174
216
  Airbrake::Request.new(
@@ -176,8 +218,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
176
218
  route: '/foo',
177
219
  status_code: 200,
178
220
  start_time: Time.new(2018, 1, 1, 0, 1, 49, 0),
179
- end_time: Time.new(2018, 1, 1, 0, 1, 55, 0)
180
- )
221
+ end_time: Time.new(2018, 1, 1, 0, 1, 55, 0),
222
+ ),
181
223
  )
182
224
  subject.close
183
225
 
@@ -191,8 +233,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
191
233
  {"method":"GET","route":"/foo","statusCode":200,
192
234
  "time":"2018-01-01T00:01:00\+00:00","count":1,"sum":6000.0,
193
235
  "sumsq":36000000.0,"tdigest":"AAAAAkA0AAAAAAAAAAAAAUW7gAAB"}\]}
194
- \z|x
195
- )
236
+ \z|x,
237
+ ),
196
238
  ).to have_been_made
197
239
  end
198
240
 
@@ -203,8 +245,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
203
245
  route: '/foo',
204
246
  status_code: 200,
205
247
  start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
206
- end_time: Time.new(2018, 1, 1, 0, 50, 0, 0)
207
- )
248
+ end_time: Time.new(2018, 1, 1, 0, 50, 0, 0),
249
+ ),
208
250
  )
209
251
  subject.notify(
210
252
  Airbrake::Request.new(
@@ -212,8 +254,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
212
254
  route: '/foo',
213
255
  status_code: 200,
214
256
  start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
215
- end_time: Time.new(2018, 1, 1, 0, 50, 0, 0)
216
- )
257
+ end_time: Time.new(2018, 1, 1, 0, 50, 0, 0),
258
+ ),
217
259
  )
218
260
  subject.close
219
261
 
@@ -227,8 +269,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
227
269
  {"method":"POST","route":"/foo","statusCode":200,
228
270
  "time":"2018-01-01T00:49:00\+00:00","count":1,"sum":60000.0,
229
271
  "sumsq":3600000000.0,"tdigest":"AAAAAkA0AAAAAAAAAAAAAUdqYAAB"}\]}
230
- \z|x
231
- )
272
+ \z|x,
273
+ ),
232
274
  ).to have_been_made
233
275
  end
234
276
 
@@ -240,8 +282,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
240
282
  response_type: 'json',
241
283
  start_time: Time.new(2018, 1, 1, 0, 0, 20, 0),
242
284
  end_time: Time.new(2018, 1, 1, 0, 0, 22, 0),
243
- groups: { db: 131, view: 421 }
244
- )
285
+ groups: { db: 131, view: 421 },
286
+ ),
245
287
  )
246
288
  subject.notify(
247
289
  Airbrake::PerformanceBreakdown.new(
@@ -250,8 +292,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
250
292
  response_type: 'json',
251
293
  start_time: Time.new(2018, 1, 1, 0, 0, 30, 0),
252
294
  end_time: Time.new(2018, 1, 1, 0, 0, 32, 0),
253
- groups: { db: 55, view: 11 }
254
- )
295
+ groups: { db: 55, view: 11 },
296
+ ),
255
297
  )
256
298
  subject.close
257
299
 
@@ -280,7 +322,56 @@ RSpec.describe Airbrake::PerformanceNotifier do
280
322
  "tdigest":"AAAAAkA0AAAAAAAAAAAAAkEwAABDzQAAAQE="
281
323
  }
282
324
  }
283
- }\]}\z|x)
325
+ }\]}\z|x),
326
+ ).to have_been_made
327
+ end
328
+
329
+ it "groups queues by queue key" do
330
+ subject.notify(
331
+ Airbrake::Queue.new(
332
+ queue: 'emails',
333
+ error_count: 2,
334
+ groups: { redis: 131, sql: 421 },
335
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
336
+ end_time: Time.new(2018, 1, 1, 0, 50, 0, 0),
337
+ ),
338
+ )
339
+ subject.notify(
340
+ Airbrake::Queue.new(
341
+ queue: 'emails',
342
+ error_count: 3,
343
+ groups: { redis: 131, sql: 421 },
344
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
345
+ end_time: Time.new(2018, 1, 1, 0, 50, 0, 0),
346
+ ),
347
+ )
348
+ subject.close
349
+
350
+ expect(
351
+ a_request(:put, queues).with(body: /
352
+ \A{"queues":\[{
353
+ "queue":"emails",
354
+ "errorCount":5,
355
+ "time":"2018-01-01T00:49:00\+00:00",
356
+ "count":2,
357
+ "sum":120000.0,
358
+ "sumsq":7200000000.0,
359
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUdqYAAC",
360
+ "groups":{
361
+ "redis":{
362
+ "count":2,
363
+ "sum":262.0,
364
+ "sumsq":34322.0,
365
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUMDAAAC"
366
+ },
367
+ "sql":{
368
+ "count":2,
369
+ "sum":842.0,
370
+ "sumsq":354482.0,
371
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUPSgAAC"
372
+ }
373
+ }
374
+ }\]}\z/x),
284
375
  ).to have_been_made
285
376
  end
286
377
 
@@ -290,8 +381,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
290
381
  method: 'GET',
291
382
  route: '/foo',
292
383
  status_code: 200,
293
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
294
- )
384
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
385
+ ),
295
386
  )
296
387
  subject.close
297
388
 
@@ -301,7 +392,7 @@ RSpec.describe Airbrake::PerformanceNotifier do
301
392
 
302
393
  it "checks performance stat configuration" do
303
394
  request = Airbrake::Request.new(
304
- method: 'GET', route: '/foo', status_code: 200, start_time: Time.new
395
+ method: 'GET', route: '/foo', status_code: 200, start_time: Time.new,
305
396
  )
306
397
  expect(Airbrake::Config.instance).to receive(:check_performance_options)
307
398
  .with(request).and_return(Airbrake::Promise.new)
@@ -317,15 +408,15 @@ RSpec.describe Airbrake::PerformanceNotifier do
317
408
  method: 'POST',
318
409
  route: '/foo',
319
410
  status_code: 200,
320
- start_time: Time.new
321
- )
411
+ start_time: Time.new,
412
+ ),
322
413
  )
323
414
  subject.close
324
415
 
325
416
  expect(
326
417
  a_request(:put, routes).with(
327
- body: /\A{"routes":\[.+\],"environment":"test"}\z/x
328
- )
418
+ body: /\A{"routes":\[.+\],"environment":"test"}\z/x,
419
+ ),
329
420
  ).to have_been_made
330
421
  end
331
422
 
@@ -346,7 +437,7 @@ RSpec.describe Airbrake::PerformanceNotifier do
346
437
  project_id: 1,
347
438
  project_key: 'banana',
348
439
  performance_stats: true,
349
- performance_stats_flush_period: flush_period
440
+ performance_stats_flush_period: flush_period,
350
441
  )
351
442
 
352
443
  subject.notify(
@@ -354,8 +445,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
354
445
  method: 'GET',
355
446
  route: '/foo',
356
447
  status_code: 200,
357
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
358
- )
448
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
449
+ ),
359
450
  )
360
451
 
361
452
  subject.notify(
@@ -363,8 +454,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
363
454
  method: 'POST',
364
455
  route: '/foo',
365
456
  query: 'SELECT * FROM things',
366
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
367
- )
457
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
458
+ ),
368
459
  )
369
460
 
370
461
  sleep(flush_period + 0.5)
@@ -383,8 +474,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
383
474
  method: 'GET',
384
475
  route: '/foo',
385
476
  status_code: 200,
386
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
387
- )
477
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
478
+ ),
388
479
  )
389
480
  subject.close
390
481
 
@@ -397,8 +488,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
397
488
  method: 'POST',
398
489
  route: '/foo',
399
490
  query: 'SELECT * FROM things',
400
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
401
- )
491
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
492
+ ),
402
493
  )
403
494
  subject.close
404
495
 
@@ -419,20 +510,40 @@ RSpec.describe Airbrake::PerformanceNotifier do
419
510
  method: 'POST',
420
511
  route: '/foo',
421
512
  query: 'SELECT * FROM things',
422
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
423
- )
513
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
514
+ ),
424
515
  )
425
516
  subject.close
426
517
 
427
518
  expect(
428
519
  a_request(:put, queries).with(
429
- body: /\A{"queries":\[{"method":"POST","route":"\[Filtered\]"/
430
- )
520
+ body: /\A{"queries":\[{"method":"POST","route":"\[Filtered\]"/,
521
+ ),
431
522
  ).to have_been_made
432
523
  end
433
524
  end
434
525
  end
435
526
 
527
+ describe "#notify_sync" do
528
+ it "notifies synchronously" do
529
+ retval = subject.notify_sync(
530
+ Airbrake::Query.new(
531
+ method: 'POST',
532
+ route: '/foo',
533
+ query: 'SELECT * FROM things',
534
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
535
+ ),
536
+ )
537
+
538
+ expect(
539
+ a_request(:put, queries).with(
540
+ body: %r|\A{"queries":\[{"method":"POST","route":"/foo"|,
541
+ ),
542
+ ).to have_been_made
543
+ expect(retval).to eq('' => nil)
544
+ end
545
+ end
546
+
436
547
  describe "#close" do
437
548
  before do
438
549
  Airbrake::Config.instance.merge(performance_stats_flush_period: 0.1)
@@ -449,8 +560,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
449
560
  method: 'POST',
450
561
  route: '/foo',
451
562
  query: 'SELECT * FROM things',
452
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
453
- )
563
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
564
+ ),
454
565
  )
455
566
  subject.close
456
567
  end
@@ -458,7 +569,7 @@ RSpec.describe Airbrake::PerformanceNotifier do
458
569
  it "logs the exit message" do
459
570
  allow(Airbrake::Loggable.instance).to receive(:debug)
460
571
  expect(Airbrake::Loggable.instance).to receive(:debug).with(
461
- /performance notifier closed/
572
+ /performance notifier closed/,
462
573
  )
463
574
  subject.close
464
575
  end
@@ -480,8 +591,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
480
591
  method: 'POST',
481
592
  route: '/foo',
482
593
  status_code: 200,
483
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
484
- )
594
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
595
+ ),
485
596
  )
486
597
  subject.close
487
598