airbrake-ruby 4.8.0 → 4.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb6fd7fd474a31d89d2b532d467f5c319399b183a5805488bbb57789bc502bef
4
- data.tar.gz: 83493c7a39f6eea49668f7c94fc202c90d55efcc1d4822864a70cca03c96134f
3
+ metadata.gz: 2358d01604e7b5362fe0a714c0137a0a95053741e5856acd58daf889da2810cb
4
+ data.tar.gz: b76c2b4282f77e94d0879df60bd5283f472ef92377d0190166cadba682ade87c
5
5
  SHA512:
6
- metadata.gz: 149075c2e3a3cd9f43f19f7309f8b9e46aa36bde2994bc48b80c8b66a4c675d1fef2989e2020e5bc81dc55a8e8490f18aa445307dd2212f7c2467a3186fb1c8d
7
- data.tar.gz: 030edc9303bd0f1042a6aa3f3ec5b672ab58598fee90a71e6d7f6a6fed84716d2043afab9edf46b3bdd6ee9939ef6b979ad78dc37575be774663ac84e8af73a7
6
+ metadata.gz: 0f389a12166d80085b35551a29a6ec6b963b79b55da50a29c37894f0fa8d5f3bcc87c61bddca113c3ada2f250c1541be13337981da98254bb59d73c4c60aa801
7
+ data.tar.gz: d8f0db0d2f682421f75d2fd7647be688decb19d2aadfadfa3c39a8d7ba198c07b7d1a6dd6a1924f2aedd7c28c482ccb63cf77ff5376cb815cade58e146a007f5
@@ -9,6 +9,8 @@ require 'time'
9
9
  require 'airbrake-ruby/version'
10
10
  require 'airbrake-ruby/loggable'
11
11
  require 'airbrake-ruby/stashable'
12
+ require 'airbrake-ruby/mergeable'
13
+ require 'airbrake-ruby/grouppable'
12
14
  require 'airbrake-ruby/config'
13
15
  require 'airbrake-ruby/config/validator'
14
16
  require 'airbrake-ruby/promise'
@@ -52,6 +54,7 @@ require 'airbrake-ruby/performance_breakdown'
52
54
  require 'airbrake-ruby/benchmark'
53
55
  require 'airbrake-ruby/monotonic_time'
54
56
  require 'airbrake-ruby/timed_trace'
57
+ require 'airbrake-ruby/queue'
55
58
 
56
59
  # Airbrake is a thin wrapper around instances of the notifier classes (such as
57
60
  # notice, performance & deploy notifiers). It creates a way to access them via a
@@ -69,6 +72,7 @@ require 'airbrake-ruby/timed_trace'
69
72
  #
70
73
  # @since v1.0.0
71
74
  # @api public
75
+ # rubocop:disable Metrics/ModuleLength
72
76
  module Airbrake
73
77
  # The general error that this library uses when it wants to raise.
74
78
  Error = Class.new(StandardError)
@@ -423,6 +427,31 @@ module Airbrake
423
427
  performance_notifier.notify(performance_breakdown)
424
428
  end
425
429
 
430
+ # Increments statistics of a certain queue (worker).
431
+ #
432
+ # @example
433
+ # Airbrake.notify_queue(
434
+ # queue: 'emails',
435
+ # error_count: 1,
436
+ # groups: { redis: 24.0, sql: 0.4 } # ms
437
+ # )
438
+ #
439
+ # @param [Hash{Symbol=>Object}] queue_info
440
+ # @option queue_info [String] :queue The name of the queue/worker
441
+ # @option queue_info [Integer] :error_count How many times this worker
442
+ # failed
443
+ # @option queue_info [Array<Hash{Symbol=>Float}>] :groups Where the job
444
+ # spent its time
445
+ # @param [Hash] stash What needs to be appeneded to the stash, so it's
446
+ # available in filters
447
+ # @return [void]
448
+ # @since v4.9.0
449
+ def notify_queue(queue_info, stash = {})
450
+ queue = Queue.new(queue_info)
451
+ queue.stash.merge!(stash)
452
+ performance_notifier.notify(queue)
453
+ end
454
+
426
455
  # Runs a callback before {.notify_request} or {.notify_query} kicks in. This
427
456
  # is useful if you want to ignore specific resources or filter the data the
428
457
  # resource contains.
@@ -513,3 +542,4 @@ module Airbrake
513
542
  end
514
543
  end
515
544
  end
545
+ # rubocop:enable Metrics/ModuleLength
@@ -0,0 +1,12 @@
1
+ module Airbrake
2
+ # Grouppable adds the `#groups` method, so that we don't need to define it in
3
+ # all of performance models every time we add a model without groups.
4
+ #
5
+ # @since 4.9.0
6
+ # @api private
7
+ module Grouppable
8
+ def groups
9
+ {}
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Airbrake
2
+ # Mergeable adds the `#merge` method, so that we don't need to define it in
3
+ # all of performance models every time we add a model.
4
+ #
5
+ # @since 4.9.0
6
+ # @api private
7
+ module Mergeable
8
+ def merge(_other)
9
+ nil
10
+ end
11
+ end
12
+ end
@@ -12,6 +12,7 @@ module Airbrake
12
12
  include HashKeyable
13
13
  include Ignorable
14
14
  include Stashable
15
+ include Mergeable
15
16
 
16
17
  def initialize(
17
18
  method:,
@@ -61,7 +61,12 @@ module Airbrake
61
61
  private
62
62
 
63
63
  def update_payload(resource)
64
- @payload[resource] ||= { total: Airbrake::Stat.new }
64
+ if (total_stat = @payload[resource])
65
+ @payload.key(total_stat).merge(resource)
66
+ else
67
+ @payload[resource] = { total: Airbrake::Stat.new }
68
+ end
69
+
65
70
  @payload[resource][:total].increment(resource.start_time, resource.end_time)
66
71
 
67
72
  resource.groups.each do |name, ms|
@@ -11,6 +11,8 @@ module Airbrake
11
11
  include HashKeyable
12
12
  include Ignorable
13
13
  include Stashable
14
+ include Mergeable
15
+ include Grouppable
14
16
 
15
17
  def initialize(
16
18
  method:,
@@ -34,10 +36,6 @@ module Airbrake
34
36
  'queries'
35
37
  end
36
38
 
37
- def groups
38
- {}
39
- end
40
-
41
39
  def to_h
42
40
  {
43
41
  'method' => method,
@@ -0,0 +1,52 @@
1
+ module Airbrake
2
+ # Queue represents a queue (worker).
3
+ #
4
+ # @see Airbrake.notify_queue
5
+ # @api public
6
+ # @since v4.9.0
7
+ # rubocop:disable Metrics/BlockLength
8
+ Queue = Struct.new(:queue, :error_count, :groups, :start_time, :end_time) do
9
+ include HashKeyable
10
+ include Ignorable
11
+ include Stashable
12
+
13
+ def initialize(
14
+ queue:,
15
+ error_count:,
16
+ groups: {},
17
+ start_time: Time.now,
18
+ end_time: Time.now
19
+ )
20
+ @start_time_utc = TimeTruncate.utc_truncate_minutes(start_time)
21
+ super(queue, error_count, groups, start_time, end_time)
22
+ end
23
+
24
+ def destination
25
+ 'queues-stats'
26
+ end
27
+
28
+ def cargo
29
+ 'queues'
30
+ end
31
+
32
+ def to_h
33
+ {
34
+ 'queue' => queue,
35
+ 'errorCount' => error_count,
36
+ 'time' => @start_time_utc
37
+ }
38
+ end
39
+
40
+ def hash
41
+ {
42
+ 'queue' => queue,
43
+ 'time' => @start_time_utc
44
+ }.hash
45
+ end
46
+
47
+ def merge(other)
48
+ self.error_count += other.error_count
49
+ end
50
+ end
51
+ # rubocop:enable Metrics/BlockLength
52
+ end
@@ -9,6 +9,8 @@ module Airbrake
9
9
  include HashKeyable
10
10
  include Ignorable
11
11
  include Stashable
12
+ include Mergeable
13
+ include Grouppable
12
14
 
13
15
  def initialize(
14
16
  method:,
@@ -29,10 +31,6 @@ module Airbrake
29
31
  'routes'
30
32
  end
31
33
 
32
- def groups
33
- {}
34
- end
35
-
36
34
  def to_h
37
35
  {
38
36
  'method' => method,
@@ -2,5 +2,5 @@
2
2
  # More information: http://semver.org/
3
3
  module Airbrake
4
4
  # @return [String] the library version
5
- AIRBRAKE_RUBY_VERSION = '4.8.0'.freeze
5
+ AIRBRAKE_RUBY_VERSION = '4.9.0'.freeze
6
6
  end
@@ -303,6 +303,37 @@ RSpec.describe Airbrake do
303
303
  end
304
304
  end
305
305
 
306
+ describe "#notify_queue" do
307
+ context "when :stash key is not provided" do
308
+ it "doesn't add anything to the stash of the queue" do
309
+ expect(described_class.performance_notifier).to receive(:notify) do |queue|
310
+ expect(queue.stash).to be_empty
311
+ end
312
+
313
+ described_class.notify_queue(
314
+ queue: 'bananas',
315
+ error_count: 10
316
+ )
317
+ end
318
+ end
319
+
320
+ context "when :stash key is provided" do
321
+ it "adds the value as the stash of the queue" do
322
+ expect(described_class.performance_notifier).to receive(:notify) do |queue|
323
+ expect(queue.stash).to eq(request_id: 1)
324
+ end
325
+
326
+ described_class.notify_queue(
327
+ {
328
+ queue: 'bananas',
329
+ error_count: 10
330
+ },
331
+ request_id: 1
332
+ )
333
+ end
334
+ end
335
+ end
336
+
306
337
  describe ".performance_notifier" do
307
338
  it "returns a performance notifier" do
308
339
  expect(described_class.performance_notifier)
@@ -2,11 +2,13 @@ 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,
@@ -120,6 +122,46 @@ RSpec.describe Airbrake::PerformanceNotifier do
120
122
  ).to have_been_made
121
123
  end
122
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)
162
+ ).to have_been_made
163
+ end
164
+
123
165
  it "rounds time to the floor minute" do
124
166
  subject.notify(
125
167
  Airbrake::Request.new(
@@ -284,6 +326,55 @@ RSpec.describe Airbrake::PerformanceNotifier do
284
326
  ).to have_been_made
285
327
  end
286
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)
375
+ ).to have_been_made
376
+ end
377
+
287
378
  it "returns a promise" do
288
379
  promise = subject.notify(
289
380
  Airbrake::Request.new(
@@ -0,0 +1,11 @@
1
+ RSpec.describe Airbrake::Queue do
2
+ subject { described_class.new(queue: 'bananas', error_count: 0) }
3
+
4
+ describe "#ignore" do
5
+ it { is_expected.to respond_to(:ignore!) }
6
+ end
7
+
8
+ describe "#stash" do
9
+ it { is_expected.to respond_to(:stash) }
10
+ end
11
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: airbrake-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.8.0
4
+ version: 4.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Airbrake Technologies, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-23 00:00:00.000000000 Z
11
+ date: 2019-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rbtree3
@@ -24,6 +24,124 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-its
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: benchmark-ips
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '2'
111
+ - !ruby/object:Gem::Dependency
112
+ name: yard
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.9'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.9'
125
+ - !ruby/object:Gem::Dependency
126
+ name: public_suffix
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '2.0'
132
+ - - "<"
133
+ - !ruby/object:Gem::Version
134
+ version: '3.0'
135
+ type: :development
136
+ prerelease: false
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - "~>"
140
+ - !ruby/object:Gem::Version
141
+ version: '2.0'
142
+ - - "<"
143
+ - !ruby/object:Gem::Version
144
+ version: '3.0'
27
145
  description: |
28
146
  Airbrake Ruby is a plain Ruby notifier for Airbrake (https://airbrake.io), the
29
147
  leading exception reporting service. Airbrake Ruby provides minimalist API that
@@ -62,10 +180,12 @@ files:
62
180
  - lib/airbrake-ruby/filters/sql_filter.rb
63
181
  - lib/airbrake-ruby/filters/system_exit_filter.rb
64
182
  - lib/airbrake-ruby/filters/thread_filter.rb
183
+ - lib/airbrake-ruby/grouppable.rb
65
184
  - lib/airbrake-ruby/hash_keyable.rb
66
185
  - lib/airbrake-ruby/ignorable.rb
67
186
  - lib/airbrake-ruby/inspectable.rb
68
187
  - lib/airbrake-ruby/loggable.rb
188
+ - lib/airbrake-ruby/mergeable.rb
69
189
  - lib/airbrake-ruby/monotonic_time.rb
70
190
  - lib/airbrake-ruby/nested_exception.rb
71
191
  - lib/airbrake-ruby/notice.rb
@@ -74,6 +194,7 @@ files:
74
194
  - lib/airbrake-ruby/performance_notifier.rb
75
195
  - lib/airbrake-ruby/promise.rb
76
196
  - lib/airbrake-ruby/query.rb
197
+ - lib/airbrake-ruby/queue.rb
77
198
  - lib/airbrake-ruby/request.rb
78
199
  - lib/airbrake-ruby/response.rb
79
200
  - lib/airbrake-ruby/stashable.rb
@@ -126,6 +247,7 @@ files:
126
247
  - spec/performance_notifier_spec.rb
127
248
  - spec/promise_spec.rb
128
249
  - spec/query_spec.rb
250
+ - spec/queue_spec.rb
129
251
  - spec/request_spec.rb
130
252
  - spec/response_spec.rb
131
253
  - spec/spec_helper.rb
@@ -203,6 +325,7 @@ test_files:
203
325
  - spec/notice_notifier/options_spec.rb
204
326
  - spec/filter_chain_spec.rb
205
327
  - spec/response_spec.rb
328
+ - spec/queue_spec.rb
206
329
  - spec/code_hunk_spec.rb
207
330
  - spec/fixtures/notroot.txt
208
331
  - spec/fixtures/project_root/long_line.txt