airbrake-ruby 4.1.0-java → 4.2.0-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.
- checksums.yaml +4 -4
- data/lib/airbrake-ruby.rb +25 -1
- data/lib/airbrake-ruby/performance_breakdown.rb +44 -0
- data/lib/airbrake-ruby/performance_notifier.rb +48 -21
- data/lib/airbrake-ruby/query.rb +9 -1
- data/lib/airbrake-ruby/request.rb +11 -1
- data/lib/airbrake-ruby/stat.rb +7 -1
- data/lib/airbrake-ruby/version.rb +1 -1
- data/spec/notice_notifier_spec.rb +0 -4
- data/spec/notice_notifier_spec/options_spec.rb +0 -4
- data/spec/performance_notifier_spec.rb +95 -3
- data/spec/spec_helper.rb +3 -0
- data/spec/stat_spec.rb +8 -10
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15e4e6fd4395f0687d8acfe52f59e71090d8be9a
|
4
|
+
data.tar.gz: 3db1f8a4dc6f496c1067d7d0e295054ee6c8fce2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c42a855a144bd9d07d57d9fdb52f4d961c46269d0ad1e6ffdaed614e40c8ce4bb1856438087e54e844f747f46dcf4217be7b49035605d2aa39ab8fc23dcb9a36
|
7
|
+
data.tar.gz: 3055d714f7d8263efd11e9de8748c04bb84031382fb1e377eef2333c2712636b48d774186a5f0b6d092de9d1fa73d43446dbf06f5b41e4587acf94a7cb05a92a
|
data/lib/airbrake-ruby.rb
CHANGED
@@ -46,6 +46,7 @@ require 'airbrake-ruby/time_truncate'
|
|
46
46
|
require 'airbrake-ruby/tdigest'
|
47
47
|
require 'airbrake-ruby/query'
|
48
48
|
require 'airbrake-ruby/request'
|
49
|
+
require 'airbrake-ruby/performance_breakdown'
|
49
50
|
|
50
51
|
# Airbrake is a thin wrapper around instances of the notifier classes (such as
|
51
52
|
# notice, performance & deploy notifiers). It creates a way to access them via a
|
@@ -352,7 +353,6 @@ module Airbrake
|
|
352
353
|
# )
|
353
354
|
#
|
354
355
|
# @param [Hash{Symbol=>Object}] query_info
|
355
|
-
# @option request_info [String] :environment (optional)
|
356
356
|
# @option request_info [String] :method The HTTP method that triggered this
|
357
357
|
# SQL query (optional)
|
358
358
|
# @option request_info [String] :route The route that triggered this SQL
|
@@ -367,6 +367,30 @@ module Airbrake
|
|
367
367
|
@performance_notifier.notify(Query.new(query_info))
|
368
368
|
end
|
369
369
|
|
370
|
+
# Increments performance breakdown statistics of a certain route.
|
371
|
+
#
|
372
|
+
# @example
|
373
|
+
# Airbrake.notify_request(
|
374
|
+
# method: 'POST',
|
375
|
+
# route: '/thing/:id/create',
|
376
|
+
# response_type: 'json',
|
377
|
+
# groups: { db: 24.0, view: 0.4 }, # ms
|
378
|
+
# start_time: timestamp,
|
379
|
+
# end_time: Time.now
|
380
|
+
# )
|
381
|
+
#
|
382
|
+
# @param [Hash{Symbol=>Object}] breakdown_info
|
383
|
+
# @option breakdown_info [String] :method HTTP method
|
384
|
+
# @option breakdown_info [String] :route
|
385
|
+
# @option breakdown_info [String] :response_type
|
386
|
+
# @option breakdown_info [Array<Hash{Symbol=>Float}>] :groups
|
387
|
+
# @option breakdown_info [Date] :start_time
|
388
|
+
# @return [void]
|
389
|
+
# @since v4.2.0
|
390
|
+
def notify_performance_breakdown(breakdown_info)
|
391
|
+
@performance_notifier.notify(PerformanceBreakdown.new(breakdown_info))
|
392
|
+
end
|
393
|
+
|
370
394
|
# Runs a callback before {.notify_request} or {.notify_query} kicks in. This
|
371
395
|
# is useful if you want to ignore specific resources or filter the data the
|
372
396
|
# resource contains.
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# PerformanceBreakdown holds data that shows how much time a request spent
|
3
|
+
# doing certaing subtasks such as (DB querying, view rendering, etc). request
|
4
|
+
#
|
5
|
+
# @see Airbrake.notify_breakdown
|
6
|
+
# @api public
|
7
|
+
# @since v4.3.0
|
8
|
+
# rubocop:disable Metrics/BlockLength, Metrics/ParameterLists
|
9
|
+
PerformanceBreakdown = Struct.new(
|
10
|
+
:method, :route, :response_type, :groups, :start_time, :end_time
|
11
|
+
) do
|
12
|
+
include HashKeyable
|
13
|
+
include Ignorable
|
14
|
+
|
15
|
+
def initialize(
|
16
|
+
method:,
|
17
|
+
route:,
|
18
|
+
response_type:,
|
19
|
+
groups:,
|
20
|
+
start_time:,
|
21
|
+
end_time: Time.now
|
22
|
+
)
|
23
|
+
super(method, route, response_type, groups, start_time, end_time)
|
24
|
+
end
|
25
|
+
|
26
|
+
def destination
|
27
|
+
'routes-breakdowns'
|
28
|
+
end
|
29
|
+
|
30
|
+
def cargo
|
31
|
+
'routes'
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_h
|
35
|
+
{
|
36
|
+
'method' => method,
|
37
|
+
'route' => route,
|
38
|
+
'responseType' => response_type,
|
39
|
+
'time' => TimeTruncate.utc_truncate_minutes(start_time)
|
40
|
+
}.delete_if { |_key, val| val.nil? }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
# rubocop:enable Metrics/BlockLength, Metrics/ParameterLists
|
44
|
+
end
|
@@ -34,14 +34,8 @@ module Airbrake
|
|
34
34
|
return if resource.ignored?
|
35
35
|
|
36
36
|
@mutex.synchronize do
|
37
|
-
|
38
|
-
@payload
|
39
|
-
|
40
|
-
if @flush_period > 0
|
41
|
-
schedule_flush(promise)
|
42
|
-
else
|
43
|
-
send(@payload, promise)
|
44
|
-
end
|
37
|
+
update_payload(resource)
|
38
|
+
@flush_period > 0 ? schedule_flush(promise) : send(@payload, promise)
|
45
39
|
end
|
46
40
|
|
47
41
|
promise
|
@@ -59,6 +53,16 @@ module Airbrake
|
|
59
53
|
|
60
54
|
private
|
61
55
|
|
56
|
+
def update_payload(resource)
|
57
|
+
@payload[resource] ||= { total: Airbrake::Stat.new }
|
58
|
+
@payload[resource][:total].increment(resource.start_time, resource.end_time)
|
59
|
+
|
60
|
+
resource.groups.each do |name, ms|
|
61
|
+
@payload[resource][name] ||= Airbrake::Stat.new
|
62
|
+
@payload[resource][name].increment_ms(ms)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
62
66
|
def schedule_flush(promise)
|
63
67
|
@schedule_flush ||= Thread.new do
|
64
68
|
sleep(@flush_period)
|
@@ -74,27 +78,50 @@ module Airbrake
|
|
74
78
|
end
|
75
79
|
end
|
76
80
|
|
77
|
-
# rubocop:disable Metrics/AbcSize
|
78
81
|
def send(payload, promise)
|
79
82
|
signature = "#{self.class.name}##{__method__}"
|
80
83
|
raise "#{signature}: payload (#{payload}) cannot be empty. Race?" if payload.none?
|
81
84
|
|
82
85
|
logger.debug("#{LOG_LABEL} #{signature}: #{payload}")
|
83
86
|
|
84
|
-
payload
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
@sender.send(
|
89
|
-
data,
|
90
|
-
promise,
|
91
|
-
URI.join(
|
92
|
-
@config.host,
|
93
|
-
"api/v5/projects/#{@config.project_id}/#{resource_name}-stats"
|
94
|
-
)
|
87
|
+
with_grouped_payload(payload) do |resource_hash, destination|
|
88
|
+
url = URI.join(
|
89
|
+
@config.host,
|
90
|
+
"api/v5/projects/#{@config.project_id}/#{destination}"
|
95
91
|
)
|
92
|
+
@sender.send(resource_hash, promise, url)
|
93
|
+
end
|
94
|
+
|
95
|
+
promise
|
96
|
+
end
|
97
|
+
|
98
|
+
def with_grouped_payload(raw_payload)
|
99
|
+
grouped_payload = raw_payload.group_by do |resource, _stats|
|
100
|
+
[resource.cargo, resource.destination]
|
101
|
+
end
|
102
|
+
|
103
|
+
grouped_payload.each do |(cargo, destination), resources|
|
104
|
+
payload = {}
|
105
|
+
payload[cargo] = serialize_resources(resources)
|
106
|
+
payload['environment'] = @config.environment if @config.environment
|
107
|
+
|
108
|
+
yield(payload, destination)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def serialize_resources(resources)
|
113
|
+
resources.map do |resource, stats|
|
114
|
+
resource_hash = resource.to_h.merge!(stats[:total].to_h)
|
115
|
+
|
116
|
+
if resource.groups.any?
|
117
|
+
group_stats = stats.reject { |name, _stat| name == :total }
|
118
|
+
resource_hash['groups'] = group_stats.merge(group_stats) do |_name, stat|
|
119
|
+
stat.to_h
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
resource_hash
|
96
124
|
end
|
97
125
|
end
|
98
|
-
# rubocop:enable Metrics/AbcSize
|
99
126
|
end
|
100
127
|
end
|
data/lib/airbrake-ruby/query.rb
CHANGED
@@ -24,10 +24,18 @@ module Airbrake
|
|
24
24
|
super(method, route, query, func, file, line, start_time, end_time)
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
27
|
+
def destination
|
28
|
+
'queries-stats'
|
29
|
+
end
|
30
|
+
|
31
|
+
def cargo
|
28
32
|
'queries'
|
29
33
|
end
|
30
34
|
|
35
|
+
def groups
|
36
|
+
{}
|
37
|
+
end
|
38
|
+
|
31
39
|
def to_h
|
32
40
|
{
|
33
41
|
'method' => method,
|
@@ -4,6 +4,7 @@ module Airbrake
|
|
4
4
|
# @see Airbrake.notify_request
|
5
5
|
# @api public
|
6
6
|
# @since v3.2.0
|
7
|
+
# rubocop:disable Metrics/BlockLength
|
7
8
|
Request = Struct.new(:method, :route, :status_code, :start_time, :end_time) do
|
8
9
|
include HashKeyable
|
9
10
|
include Ignorable
|
@@ -18,10 +19,18 @@ module Airbrake
|
|
18
19
|
super(method, route, status_code, start_time, end_time)
|
19
20
|
end
|
20
21
|
|
21
|
-
def
|
22
|
+
def destination
|
23
|
+
'routes-stats'
|
24
|
+
end
|
25
|
+
|
26
|
+
def cargo
|
22
27
|
'routes'
|
23
28
|
end
|
24
29
|
|
30
|
+
def groups
|
31
|
+
{}
|
32
|
+
end
|
33
|
+
|
25
34
|
def to_h
|
26
35
|
{
|
27
36
|
'method' => method,
|
@@ -31,4 +40,5 @@ module Airbrake
|
|
31
40
|
}.delete_if { |_key, val| val.nil? }
|
32
41
|
end
|
33
42
|
end
|
43
|
+
# rubocop:enable Metrics/BlockLength
|
34
44
|
end
|
data/lib/airbrake-ruby/stat.rb
CHANGED
@@ -43,10 +43,16 @@ module Airbrake
|
|
43
43
|
# @return [void]
|
44
44
|
def increment(start_time, end_time = nil)
|
45
45
|
end_time ||= Time.new
|
46
|
+
increment_ms((end_time - start_time) * 1000)
|
47
|
+
end
|
46
48
|
|
49
|
+
# Increments count and updates performance with given +ms+ value.
|
50
|
+
#
|
51
|
+
# @param [Float] ms
|
52
|
+
# @return [void]
|
53
|
+
def increment_ms(ms)
|
47
54
|
self.count += 1
|
48
55
|
|
49
|
-
ms = (end_time - start_time) * 1000
|
50
56
|
self.sum += ms
|
51
57
|
self.sumsq += ms * ms
|
52
58
|
|
@@ -185,10 +185,6 @@ RSpec.describe Airbrake::NoticeNotifier do
|
|
185
185
|
describe "#notify_sync" do
|
186
186
|
let(:endpoint) { 'https://api.airbrake.io/api/v3/projects/1/notices' }
|
187
187
|
|
188
|
-
let(:user_params) do
|
189
|
-
{ project_id: 1, project_key: 'abc', logger: Logger.new('/dev/null') }
|
190
|
-
end
|
191
|
-
|
192
188
|
let(:body) do
|
193
189
|
{
|
194
190
|
'id' => '00054414-b147-6ffa-85d6-1524d83362a6',
|
@@ -1,8 +1,4 @@
|
|
1
1
|
RSpec.describe Airbrake::NoticeNotifier do
|
2
|
-
def expect_a_request_with_body(body)
|
3
|
-
expect(a_request(:post, endpoint).with(body: body)).to have_been_made.once
|
4
|
-
end
|
5
|
-
|
6
2
|
let(:project_id) { 105138 }
|
7
3
|
let(:project_key) { 'fd04e13d806a90f96614ad8e529b2822' }
|
8
4
|
let(:localhost) { 'http://localhost:8080' }
|
@@ -1,10 +1,12 @@
|
|
1
1
|
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
|
+
let(:breakdowns) { 'https://api.airbrake.io/api/v5/projects/1/routes-breakdowns' }
|
4
5
|
|
5
6
|
before do
|
6
7
|
stub_request(:put, routes).to_return(status: 200, body: '')
|
7
8
|
stub_request(:put, queries).to_return(status: 200, body: '')
|
9
|
+
stub_request(:put, breakdowns).to_return(status: 200, body: '')
|
8
10
|
|
9
11
|
Airbrake::Config.instance = Airbrake::Config.new(
|
10
12
|
project_id: 1,
|
@@ -73,6 +75,47 @@ RSpec.describe Airbrake::PerformanceNotifier do
|
|
73
75
|
).to have_been_made
|
74
76
|
end
|
75
77
|
|
78
|
+
it "sends full performance breakdown" do
|
79
|
+
subject.notify(
|
80
|
+
Airbrake::PerformanceBreakdown.new(
|
81
|
+
method: 'DELETE',
|
82
|
+
route: '/routes-breakdowns',
|
83
|
+
response_type: 'json',
|
84
|
+
start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
|
85
|
+
end_time: Time.new(2018, 1, 1, 0, 50, 0, 0),
|
86
|
+
groups: { db: 131, view: 421 }
|
87
|
+
)
|
88
|
+
)
|
89
|
+
|
90
|
+
expect(
|
91
|
+
a_request(:put, breakdowns).with(body: %r|
|
92
|
+
\A{"routes":\[{
|
93
|
+
"method":"DELETE",
|
94
|
+
"route":"/routes-breakdowns",
|
95
|
+
"responseType":"json",
|
96
|
+
"time":"2018-01-01T00:49:00\+00:00",
|
97
|
+
"count":1,
|
98
|
+
"sum":60000.0,
|
99
|
+
"sumsq":3600000000.0,
|
100
|
+
"tdigest":"AAAAAkA0AAAAAAAAAAAAAUdqYAAB",
|
101
|
+
"groups":{
|
102
|
+
"db":{
|
103
|
+
"count":1,
|
104
|
+
"sum":131.0,
|
105
|
+
"sumsq":17161.0,
|
106
|
+
"tdigest":"AAAAAkA0AAAAAAAAAAAAAUMDAAAB"
|
107
|
+
},
|
108
|
+
"view":{
|
109
|
+
"count":1,
|
110
|
+
"sum":421.0,
|
111
|
+
"sumsq":177241.0,
|
112
|
+
"tdigest":"AAAAAkA0AAAAAAAAAAAAAUPSgAAB"
|
113
|
+
}
|
114
|
+
}
|
115
|
+
}\]}\z|x)
|
116
|
+
).to have_been_made
|
117
|
+
end
|
118
|
+
|
76
119
|
it "rounds time to the floor minute" do
|
77
120
|
subject.notify(
|
78
121
|
Airbrake::Request.new(
|
@@ -177,6 +220,57 @@ RSpec.describe Airbrake::PerformanceNotifier do
|
|
177
220
|
).to have_been_made
|
178
221
|
end
|
179
222
|
|
223
|
+
it "groups performance breakdowns by route key" do
|
224
|
+
subject.notify(
|
225
|
+
Airbrake::PerformanceBreakdown.new(
|
226
|
+
method: 'DELETE',
|
227
|
+
route: '/routes-breakdowns',
|
228
|
+
response_type: 'json',
|
229
|
+
start_time: Time.new(2018, 1, 1, 0, 0, 20, 0),
|
230
|
+
end_time: Time.new(2018, 1, 1, 0, 0, 22, 0),
|
231
|
+
groups: { db: 131, view: 421 }
|
232
|
+
)
|
233
|
+
)
|
234
|
+
subject.notify(
|
235
|
+
Airbrake::PerformanceBreakdown.new(
|
236
|
+
method: 'DELETE',
|
237
|
+
route: '/routes-breakdowns',
|
238
|
+
response_type: 'json',
|
239
|
+
start_time: Time.new(2018, 1, 1, 0, 0, 30, 0),
|
240
|
+
end_time: Time.new(2018, 1, 1, 0, 0, 32, 0),
|
241
|
+
groups: { db: 55, view: 11 }
|
242
|
+
)
|
243
|
+
)
|
244
|
+
|
245
|
+
expect(
|
246
|
+
a_request(:put, breakdowns).with(body: %r|
|
247
|
+
\A{"routes":\[{
|
248
|
+
"method":"DELETE",
|
249
|
+
"route":"/routes-breakdowns",
|
250
|
+
"responseType":"json",
|
251
|
+
"time":"2018-01-01T00:00:00\+00:00",
|
252
|
+
"count":2,
|
253
|
+
"sum":4000.0,
|
254
|
+
"sumsq":8000000.0,
|
255
|
+
"tdigest":"AAAAAkA0AAAAAAAAAAAAAUT6AAAC",
|
256
|
+
"groups":{
|
257
|
+
"db":{
|
258
|
+
"count":2,
|
259
|
+
"sum":186.0,
|
260
|
+
"sumsq":20186.0,
|
261
|
+
"tdigest":"AAAAAkA0AAAAAAAAAAAAAkJcAABCmAAAAQE="
|
262
|
+
},
|
263
|
+
"view":{
|
264
|
+
"count":2,
|
265
|
+
"sum":432.0,
|
266
|
+
"sumsq":177362.0,
|
267
|
+
"tdigest":"AAAAAkA0AAAAAAAAAAAAAkEwAABDzQAAAQE="
|
268
|
+
}
|
269
|
+
}
|
270
|
+
}\]}\z|x)
|
271
|
+
).to have_been_made
|
272
|
+
end
|
273
|
+
|
180
274
|
it "returns a promise" do
|
181
275
|
promise = subject.notify(
|
182
276
|
Airbrake::Request.new(
|
@@ -339,9 +433,7 @@ RSpec.describe Airbrake::PerformanceNotifier do
|
|
339
433
|
describe "#delete_filter" do
|
340
434
|
let(:filter) do
|
341
435
|
Class.new do
|
342
|
-
def call(resource)
|
343
|
-
resource.ignore!
|
344
|
-
end
|
436
|
+
def call(resource); end
|
345
437
|
end
|
346
438
|
end
|
347
439
|
|
data/spec/spec_helper.rb
CHANGED
data/spec/stat_spec.rb
CHANGED
@@ -12,21 +12,19 @@ RSpec.describe Airbrake::Stat do
|
|
12
12
|
|
13
13
|
describe "#increment" do
|
14
14
|
let(:start_time) { Time.new(2018, 1, 1, 0, 0, 20, 0) }
|
15
|
-
let(:end_time) { Time.new(2018, 1, 1, 0, 0,
|
15
|
+
let(:end_time) { Time.new(2018, 1, 1, 0, 0, 22, 0) }
|
16
16
|
|
17
17
|
before { subject.increment(start_time, end_time) }
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
end
|
19
|
+
its(:sum) { is_expected.to eq(2000) }
|
20
|
+
end
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
22
|
+
describe "#increment_ms" do
|
23
|
+
before { subject.increment_ms(1000) }
|
26
24
|
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
its(:count) { is_expected.to eq(1) }
|
26
|
+
its(:sum) { is_expected.to eq(1000) }
|
27
|
+
its(:sumsq) { is_expected.to eq(1000000) }
|
30
28
|
|
31
29
|
it "updates tdigest" do
|
32
30
|
expect(subject.tdigest.size).to eq(1)
|
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.
|
4
|
+
version: 4.2.0
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Airbrake Technologies, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rbtree-jruby
|
@@ -68,6 +68,7 @@ files:
|
|
68
68
|
- lib/airbrake-ruby/nested_exception.rb
|
69
69
|
- lib/airbrake-ruby/notice.rb
|
70
70
|
- lib/airbrake-ruby/notice_notifier.rb
|
71
|
+
- lib/airbrake-ruby/performance_breakdown.rb
|
71
72
|
- lib/airbrake-ruby/performance_notifier.rb
|
72
73
|
- lib/airbrake-ruby/promise.rb
|
73
74
|
- lib/airbrake-ruby/query.rb
|