airbrake-ruby 4.8.0 → 4.11.1

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby.rb +101 -25
  3. data/lib/airbrake-ruby/async_sender.rb +3 -3
  4. data/lib/airbrake-ruby/backtrace.rb +2 -2
  5. data/lib/airbrake-ruby/benchmark.rb +1 -1
  6. data/lib/airbrake-ruby/code_hunk.rb +1 -1
  7. data/lib/airbrake-ruby/config.rb +1 -1
  8. data/lib/airbrake-ruby/config/validator.rb +3 -3
  9. data/lib/airbrake-ruby/deploy_notifier.rb +1 -1
  10. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +2 -2
  11. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +2 -2
  12. data/lib/airbrake-ruby/filters/keys_filter.rb +1 -1
  13. data/lib/airbrake-ruby/filters/sql_filter.rb +3 -3
  14. data/lib/airbrake-ruby/filters/thread_filter.rb +1 -1
  15. data/lib/airbrake-ruby/grouppable.rb +12 -0
  16. data/lib/airbrake-ruby/inspectable.rb +2 -2
  17. data/lib/airbrake-ruby/mergeable.rb +12 -0
  18. data/lib/airbrake-ruby/notice.rb +7 -7
  19. data/lib/airbrake-ruby/notice_notifier.rb +3 -2
  20. data/lib/airbrake-ruby/performance_breakdown.rb +12 -6
  21. data/lib/airbrake-ruby/performance_notifier.rb +69 -22
  22. data/lib/airbrake-ruby/query.rb +15 -11
  23. data/lib/airbrake-ruby/queue.rb +56 -0
  24. data/lib/airbrake-ruby/request.rb +14 -12
  25. data/lib/airbrake-ruby/stat.rb +1 -1
  26. data/lib/airbrake-ruby/version.rb +1 -1
  27. data/spec/airbrake_spec.rb +135 -45
  28. data/spec/async_sender_spec.rb +4 -4
  29. data/spec/backtrace_spec.rb +18 -18
  30. data/spec/code_hunk_spec.rb +9 -9
  31. data/spec/config/validator_spec.rb +5 -5
  32. data/spec/config_spec.rb +5 -9
  33. data/spec/deploy_notifier_spec.rb +2 -2
  34. data/spec/filter_chain_spec.rb +1 -1
  35. data/spec/filters/dependency_filter_spec.rb +1 -1
  36. data/spec/filters/gem_root_filter_spec.rb +5 -5
  37. data/spec/filters/git_last_checkout_filter_spec.rb +1 -1
  38. data/spec/filters/git_repository_filter.rb +1 -1
  39. data/spec/filters/git_revision_filter_spec.rb +10 -10
  40. data/spec/filters/keys_blacklist_spec.rb +22 -22
  41. data/spec/filters/keys_whitelist_spec.rb +21 -21
  42. data/spec/filters/root_directory_filter_spec.rb +5 -5
  43. data/spec/filters/sql_filter_spec.rb +53 -55
  44. data/spec/filters/system_exit_filter_spec.rb +1 -1
  45. data/spec/filters/thread_filter_spec.rb +28 -28
  46. data/spec/fixtures/project_root/code.rb +9 -9
  47. data/spec/notice_notifier/options_spec.rb +12 -12
  48. data/spec/notice_notifier_spec.rb +18 -18
  49. data/spec/notice_spec.rb +5 -5
  50. data/spec/performance_breakdown_spec.rb +11 -0
  51. data/spec/performance_notifier_spec.rb +243 -72
  52. data/spec/query_spec.rb +11 -1
  53. data/spec/queue_spec.rb +21 -0
  54. data/spec/request_spec.rb +11 -1
  55. data/spec/response_spec.rb +8 -8
  56. data/spec/spec_helper.rb +2 -2
  57. data/spec/stat_spec.rb +2 -2
  58. data/spec/sync_sender_spec.rb +12 -12
  59. data/spec/tdigest_spec.rb +6 -6
  60. data/spec/thread_pool_spec.rb +5 -5
  61. data/spec/timed_trace_spec.rb +1 -1
  62. data/spec/truncator_spec.rb +12 -12
  63. metadata +7 -2
@@ -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
@@ -328,9 +328,9 @@ RSpec.describe Airbrake::NoticeNotifier do
328
328
  end
329
329
 
330
330
  it "raises error" do
331
- expect { subject.build_notice(Exception.new) }.to raise_error(
331
+ expect { subject.build_notice(Exception.new('oops')) }.to raise_error(
332
332
  Airbrake::Error,
333
- 'attempted to build Exception with closed Airbrake instance'
333
+ "Airbrake is closed; can't build exception: Exception: oops",
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
@@ -9,4 +9,15 @@ RSpec.describe Airbrake::PerformanceBreakdown do
9
9
 
10
10
  it { is_expected.to respond_to(:stash) }
11
11
  end
12
+
13
+ describe "#end_time" do
14
+ it "is always equal to start_time + 1 second by default" do
15
+ time = Time.now
16
+ performance_breakdown = described_class.new(
17
+ method: 'GET', route: '/', response_type: '', groups: {},
18
+ start_time: time
19
+ )
20
+ expect(performance_breakdown.end_time).to eq(time + 1)
21
+ end
22
+ end
12
23
  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
 
@@ -27,9 +29,9 @@ RSpec.describe Airbrake::PerformanceNotifier do
27
29
  func: 'foo',
28
30
  file: 'foo.rb',
29
31
  line: 123,
30
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
31
- end_time: Time.new(2018, 1, 1, 0, 50, 0, 0)
32
- )
32
+ timing: 60000,
33
+ time: Time.new(2018, 1, 1, 0, 49, 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
 
@@ -57,9 +59,9 @@ RSpec.describe Airbrake::PerformanceNotifier do
57
59
  method: 'POST',
58
60
  route: '/foo',
59
61
  status_code: 200,
60
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
61
- end_time: Time.new(2018, 1, 1, 0, 50, 0, 0)
62
- )
62
+ timing: 60000,
63
+ time: Time.new(2018, 1, 1, 0, 49, 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
 
@@ -84,10 +86,10 @@ RSpec.describe Airbrake::PerformanceNotifier do
84
86
  method: 'DELETE',
85
87
  route: '/routes-breakdowns',
86
88
  response_type: 'json',
87
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
88
- end_time: Time.new(2018, 1, 1, 0, 50, 0, 0),
89
- groups: { db: 131, view: 421 }
90
- )
89
+ timing: 60000,
90
+ time: Time.new(2018, 1, 1, 0, 49, 0, 0),
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
+ timing: 60000,
132
+ time: Time.new(2018, 1, 1, 0, 49, 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,14 @@ 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
+ timing: 60000,
172
+ time: Time.new(2018, 1, 1, 0, 0, 20, 0),
173
+ ),
131
174
  )
132
175
  subject.close
133
176
 
134
177
  expect(
135
- a_request(:put, routes).with(body: /"time":"2018-01-01T00:00:00\+00:00"/)
178
+ a_request(:put, routes).with(body: /"time":"2018-01-01T00:00:00\+00:00"/),
136
179
  ).to have_been_made
137
180
  end
138
181
 
@@ -142,21 +185,21 @@ RSpec.describe Airbrake::PerformanceNotifier do
142
185
  method: 'GET',
143
186
  route: '/foo',
144
187
  status_code: 200,
145
- start_time: Time.new(2018, 1, 1, 0, 0, 20, 0)
146
- )
188
+ timing: 213,
189
+ ),
147
190
  )
148
191
  subject.notify(
149
192
  Airbrake::Request.new(
150
193
  method: 'GET',
151
194
  route: '/foo',
152
195
  status_code: 200,
153
- start_time: Time.new(2018, 1, 1, 0, 0, 50, 0)
154
- )
196
+ timing: 123,
197
+ ),
155
198
  )
156
199
  subject.close
157
200
 
158
201
  expect(
159
- a_request(:put, routes).with(body: /"count":2/)
202
+ a_request(:put, routes).with(body: /"count":2/),
160
203
  ).to have_been_made
161
204
  end
162
205
 
@@ -166,18 +209,18 @@ RSpec.describe Airbrake::PerformanceNotifier do
166
209
  method: 'GET',
167
210
  route: '/foo',
168
211
  status_code: 200,
169
- 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
+ timing: 1000,
213
+ time: Time.new(2018, 1, 1, 0, 0, 49, 0),
214
+ ),
172
215
  )
173
216
  subject.notify(
174
217
  Airbrake::Request.new(
175
218
  method: 'GET',
176
219
  route: '/foo',
177
220
  status_code: 200,
178
- 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
+ timing: 6000,
222
+ time: Time.new(2018, 1, 1, 0, 1, 49, 0),
223
+ ),
181
224
  )
182
225
  subject.close
183
226
 
@@ -191,8 +234,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
191
234
  {"method":"GET","route":"/foo","statusCode":200,
192
235
  "time":"2018-01-01T00:01:00\+00:00","count":1,"sum":6000.0,
193
236
  "sumsq":36000000.0,"tdigest":"AAAAAkA0AAAAAAAAAAAAAUW7gAAB"}\]}
194
- \z|x
195
- )
237
+ \z|x,
238
+ ),
196
239
  ).to have_been_made
197
240
  end
198
241
 
@@ -202,18 +245,18 @@ RSpec.describe Airbrake::PerformanceNotifier do
202
245
  method: 'GET',
203
246
  route: '/foo',
204
247
  status_code: 200,
205
- 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
+ timing: 60000,
249
+ time: Time.new(2018, 1, 1, 0, 49, 0, 0),
250
+ ),
208
251
  )
209
252
  subject.notify(
210
253
  Airbrake::Request.new(
211
254
  method: 'POST',
212
255
  route: '/foo',
213
256
  status_code: 200,
214
- 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
+ timing: 60000,
258
+ time: Time.new(2018, 1, 1, 0, 49, 0, 0),
259
+ ),
217
260
  )
218
261
  subject.close
219
262
 
@@ -227,8 +270,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
227
270
  {"method":"POST","route":"/foo","statusCode":200,
228
271
  "time":"2018-01-01T00:49:00\+00:00","count":1,"sum":60000.0,
229
272
  "sumsq":3600000000.0,"tdigest":"AAAAAkA0AAAAAAAAAAAAAUdqYAAB"}\]}
230
- \z|x
231
- )
273
+ \z|x,
274
+ ),
232
275
  ).to have_been_made
233
276
  end
234
277
 
@@ -238,20 +281,20 @@ RSpec.describe Airbrake::PerformanceNotifier do
238
281
  method: 'DELETE',
239
282
  route: '/routes-breakdowns',
240
283
  response_type: 'json',
241
- start_time: Time.new(2018, 1, 1, 0, 0, 20, 0),
242
- end_time: Time.new(2018, 1, 1, 0, 0, 22, 0),
243
- groups: { db: 131, view: 421 }
244
- )
284
+ timing: 2000,
285
+ time: Time.new(2018, 1, 1, 0, 0, 20, 0),
286
+ groups: { db: 131, view: 421 },
287
+ ),
245
288
  )
246
289
  subject.notify(
247
290
  Airbrake::PerformanceBreakdown.new(
248
291
  method: 'DELETE',
249
292
  route: '/routes-breakdowns',
250
293
  response_type: 'json',
251
- start_time: Time.new(2018, 1, 1, 0, 0, 30, 0),
252
- end_time: Time.new(2018, 1, 1, 0, 0, 32, 0),
253
- groups: { db: 55, view: 11 }
254
- )
294
+ timing: 2000,
295
+ time: Time.new(2018, 1, 1, 0, 0, 30, 0),
296
+ groups: { db: 55, view: 11 },
297
+ ),
255
298
  )
256
299
  subject.close
257
300
 
@@ -280,7 +323,56 @@ RSpec.describe Airbrake::PerformanceNotifier do
280
323
  "tdigest":"AAAAAkA0AAAAAAAAAAAAAkEwAABDzQAAAQE="
281
324
  }
282
325
  }
283
- }\]}\z|x)
326
+ }\]}\z|x),
327
+ ).to have_been_made
328
+ end
329
+
330
+ it "groups queues by queue key" do
331
+ subject.notify(
332
+ Airbrake::Queue.new(
333
+ queue: 'emails',
334
+ error_count: 2,
335
+ groups: { redis: 131, sql: 421 },
336
+ timing: 60000,
337
+ time: Time.new(2018, 1, 1, 0, 49, 0, 0),
338
+ ),
339
+ )
340
+ subject.notify(
341
+ Airbrake::Queue.new(
342
+ queue: 'emails',
343
+ error_count: 3,
344
+ groups: { redis: 131, sql: 421 },
345
+ timing: 60000,
346
+ time: Time.new(2018, 1, 1, 0, 49, 0, 0),
347
+ ),
348
+ )
349
+ subject.close
350
+
351
+ expect(
352
+ a_request(:put, queues).with(body: /
353
+ \A{"queues":\[{
354
+ "queue":"emails",
355
+ "errorCount":5,
356
+ "time":"2018-01-01T00:49:00\+00:00",
357
+ "count":2,
358
+ "sum":120000.0,
359
+ "sumsq":7200000000.0,
360
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUdqYAAC",
361
+ "groups":{
362
+ "redis":{
363
+ "count":2,
364
+ "sum":262.0,
365
+ "sumsq":34322.0,
366
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUMDAAAC"
367
+ },
368
+ "sql":{
369
+ "count":2,
370
+ "sum":842.0,
371
+ "sumsq":354482.0,
372
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUPSgAAC"
373
+ }
374
+ }
375
+ }\]}\z/x),
284
376
  ).to have_been_made
285
377
  end
286
378
 
@@ -290,8 +382,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
290
382
  method: 'GET',
291
383
  route: '/foo',
292
384
  status_code: 200,
293
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
294
- )
385
+ timing: 123,
386
+ ),
295
387
  )
296
388
  subject.close
297
389
 
@@ -301,7 +393,7 @@ RSpec.describe Airbrake::PerformanceNotifier do
301
393
 
302
394
  it "checks performance stat configuration" do
303
395
  request = Airbrake::Request.new(
304
- method: 'GET', route: '/foo', status_code: 200, start_time: Time.new
396
+ method: 'GET', route: '/foo', status_code: 200, timing: 123,
305
397
  )
306
398
  expect(Airbrake::Config.instance).to receive(:check_performance_options)
307
399
  .with(request).and_return(Airbrake::Promise.new)
@@ -317,15 +409,15 @@ RSpec.describe Airbrake::PerformanceNotifier do
317
409
  method: 'POST',
318
410
  route: '/foo',
319
411
  status_code: 200,
320
- start_time: Time.new
321
- )
412
+ timing: 123,
413
+ ),
322
414
  )
323
415
  subject.close
324
416
 
325
417
  expect(
326
418
  a_request(:put, routes).with(
327
- body: /\A{"routes":\[.+\],"environment":"test"}\z/x
328
- )
419
+ body: /\A{"routes":\[.+\],"environment":"test"}\z/x,
420
+ ),
329
421
  ).to have_been_made
330
422
  end
331
423
 
@@ -346,7 +438,7 @@ RSpec.describe Airbrake::PerformanceNotifier do
346
438
  project_id: 1,
347
439
  project_key: 'banana',
348
440
  performance_stats: true,
349
- performance_stats_flush_period: flush_period
441
+ performance_stats_flush_period: flush_period,
350
442
  )
351
443
 
352
444
  subject.notify(
@@ -354,8 +446,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
354
446
  method: 'GET',
355
447
  route: '/foo',
356
448
  status_code: 200,
357
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
358
- )
449
+ timing: 123,
450
+ ),
359
451
  )
360
452
 
361
453
  subject.notify(
@@ -363,8 +455,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
363
455
  method: 'POST',
364
456
  route: '/foo',
365
457
  query: 'SELECT * FROM things',
366
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
367
- )
458
+ timing: 123,
459
+ ),
368
460
  )
369
461
 
370
462
  sleep(flush_period + 0.5)
@@ -383,8 +475,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
383
475
  method: 'GET',
384
476
  route: '/foo',
385
477
  status_code: 200,
386
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
387
- )
478
+ timing: 1,
479
+ ),
388
480
  )
389
481
  subject.close
390
482
 
@@ -397,13 +489,29 @@ RSpec.describe Airbrake::PerformanceNotifier do
397
489
  method: 'POST',
398
490
  route: '/foo',
399
491
  query: 'SELECT * FROM things',
400
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
401
- )
492
+ timing: 1,
493
+ ),
402
494
  )
403
495
  subject.close
404
496
 
405
497
  expect(a_request(:put, queries)).not_to have_been_made
406
498
  end
499
+
500
+ it "returns a rejected promise" do
501
+ promise = subject.notify(
502
+ Airbrake::Query.new(
503
+ method: 'POST',
504
+ route: '/foo',
505
+ query: 'SELECT * FROM things',
506
+ timing: 1,
507
+ ),
508
+ )
509
+ subject.close
510
+
511
+ expect(promise.value).to eq(
512
+ 'error' => 'Airbrake::Query was ignored by a filter',
513
+ )
514
+ end
407
515
  end
408
516
 
409
517
  context "when a filter that modifies payload was defined" do
@@ -419,18 +527,81 @@ RSpec.describe Airbrake::PerformanceNotifier do
419
527
  method: 'POST',
420
528
  route: '/foo',
421
529
  query: 'SELECT * FROM things',
422
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
423
- )
530
+ timing: 123,
531
+ ),
424
532
  )
425
533
  subject.close
426
534
 
427
535
  expect(
428
536
  a_request(:put, queries).with(
429
- body: /\A{"queries":\[{"method":"POST","route":"\[Filtered\]"/
430
- )
537
+ body: /\A{"queries":\[{"method":"POST","route":"\[Filtered\]"/,
538
+ ),
431
539
  ).to have_been_made
432
540
  end
433
541
  end
542
+
543
+ context "when :start_time is specified (deprecated)" do
544
+ before do
545
+ allow(Kernel).to receive(:warn)
546
+ end
547
+
548
+ it "uses the value of :start_time to update stat" do
549
+ subject.notify(
550
+ Airbrake::Query.new(
551
+ method: 'POST',
552
+ route: '/foo',
553
+ query: 'SELECT * FROM things',
554
+ start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
555
+ end_time: Time.new(2018, 1, 1, 0, 50, 0, 0),
556
+ ),
557
+ )
558
+ subject.close
559
+
560
+ expect(
561
+ a_request(:put, queries).with(
562
+ body: /"count":1,"sum":60000.0,"sumsq":3600000000.0/,
563
+ ),
564
+ ).to have_been_made
565
+ end
566
+ end
567
+
568
+ context "when provided :timing is zero" do
569
+ it "doesn't notify" do
570
+ queue = Airbrake::Queue.new(queue: 'bananas', error_count: 0, timing: 0)
571
+ subject.notify(queue)
572
+ subject.close
573
+
574
+ expect(a_request(:put, queues)).not_to have_been_made
575
+ end
576
+
577
+ it "returns a rejected promise" do
578
+ queue = Airbrake::Queue.new(queue: 'bananas', error_count: 0, timing: 0)
579
+ promise = subject.notify(queue)
580
+ subject.close
581
+
582
+ expect(promise.value).to eq('error' => ':timing cannot be zero')
583
+ end
584
+ end
585
+ end
586
+
587
+ describe "#notify_sync" do
588
+ it "notifies synchronously" do
589
+ retval = subject.notify_sync(
590
+ Airbrake::Query.new(
591
+ method: 'POST',
592
+ route: '/foo',
593
+ query: 'SELECT * FROM things',
594
+ timing: 123,
595
+ ),
596
+ )
597
+
598
+ expect(
599
+ a_request(:put, queries).with(
600
+ body: %r|\A{"queries":\[{"method":"POST","route":"/foo"|,
601
+ ),
602
+ ).to have_been_made
603
+ expect(retval).to eq('' => nil)
604
+ end
434
605
  end
435
606
 
436
607
  describe "#close" do
@@ -449,8 +620,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
449
620
  method: 'POST',
450
621
  route: '/foo',
451
622
  query: 'SELECT * FROM things',
452
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
453
- )
623
+ timing: 123,
624
+ ),
454
625
  )
455
626
  subject.close
456
627
  end
@@ -458,7 +629,7 @@ RSpec.describe Airbrake::PerformanceNotifier do
458
629
  it "logs the exit message" do
459
630
  allow(Airbrake::Loggable.instance).to receive(:debug)
460
631
  expect(Airbrake::Loggable.instance).to receive(:debug).with(
461
- /performance notifier closed/
632
+ /performance notifier closed/,
462
633
  )
463
634
  subject.close
464
635
  end
@@ -480,8 +651,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
480
651
  method: 'POST',
481
652
  route: '/foo',
482
653
  status_code: 200,
483
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
484
- )
654
+ timing: 123,
655
+ ),
485
656
  )
486
657
  subject.close
487
658