airbrake-ruby 2.13.0.pre.1 → 3.0.0.rc.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/airbrake-ruby.rb +1 -5
- data/lib/airbrake-ruby/backtrace.rb +1 -16
- data/lib/airbrake-ruby/config.rb +1 -1
- data/lib/airbrake-ruby/filter_chain.rb +56 -1
- data/lib/airbrake-ruby/notifier.rb +30 -39
- data/lib/airbrake-ruby/response.rb +1 -1
- data/lib/airbrake-ruby/route_sender.rb +63 -8
- data/lib/airbrake-ruby/version.rb +1 -1
- data/spec/backtrace_spec.rb +5 -25
- data/spec/filter_chain_spec.rb +5 -1
- data/spec/notifier_spec.rb +30 -0
- data/spec/response_spec.rb +1 -1
- data/spec/route_sender_spec.rb +4 -4
- data/spec/spec_helper.rb +1 -0
- metadata +22 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0aef633a546613b3d46a1669505df654fcc4348b
|
4
|
+
data.tar.gz: c35b98d3ffa46c7ee3fe976546585f27ca6d1bd1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 820453ab4d27d72c7f356958af555bc361507ca48cde546e6bc15f77cb32077c2cc1623d6a0afe882c37b9ade81888b58ddaadbd2c7f17b820a6011ab666877d
|
7
|
+
data.tar.gz: fc7078da83d4cd3d2e4e4c2602e5441e9ebd6751410cfdf8ffb6c4998f1832790a3ccfb41aa97a39470315b60a965b0b91059bc4914c366f923e986842f8adf1
|
data/lib/airbrake-ruby.rb
CHANGED
@@ -76,10 +76,6 @@ module Airbrake
|
|
76
76
|
# @return [String] the label to be prepended to the log output
|
77
77
|
LOG_LABEL = '**Airbrake:'.freeze
|
78
78
|
|
79
|
-
# @return [Boolean] true if current Ruby is Ruby 2.0.*. The result is used
|
80
|
-
# for special cases where we need to work around older implementations
|
81
|
-
RUBY_20 = RUBY_VERSION.start_with?('2.0')
|
82
|
-
|
83
79
|
# @return [Boolean] true if current Ruby is JRuby. The result is used for
|
84
80
|
# special cases where we need to work around older implementations
|
85
81
|
JRUBY = (RUBY_ENGINE == 'jruby')
|
@@ -366,7 +362,7 @@ module Airbrake
|
|
366
362
|
# milliseconds
|
367
363
|
# @param [Time] time When the request happened
|
368
364
|
# @return [void]
|
369
|
-
# @since
|
365
|
+
# @since v3.0.0
|
370
366
|
def inc_request(method, route, status_code, dur, time)
|
371
367
|
@notifiers[:default].inc_request(method, route, status_code, dur, time)
|
372
368
|
end
|
@@ -83,10 +83,6 @@ module Airbrake
|
|
83
83
|
#{RUBY}
|
84
84
|
)
|
85
85
|
\z/x
|
86
|
-
|
87
|
-
# @return [Regexp] +EXECJS+ pattern without named captures and
|
88
|
-
# uncommon frames
|
89
|
-
EXECJS_SIMPLIFIED = /\A.+ \(.+:\d+:\d+\)\z/
|
90
86
|
end
|
91
87
|
|
92
88
|
# @return [Integer] how many first frames should include code hunks
|
@@ -136,24 +132,13 @@ module Airbrake
|
|
136
132
|
defined?(OCIError) && exception.is_a?(OCIError)
|
137
133
|
end
|
138
134
|
|
139
|
-
# rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
140
135
|
def execjs_exception?(exception)
|
141
136
|
return false unless defined?(ExecJS::RuntimeError)
|
142
137
|
return true if exception.is_a?(ExecJS::RuntimeError)
|
143
|
-
|
144
|
-
if Airbrake::RUBY_20
|
145
|
-
# Ruby <2.1 doesn't support Exception#cause. We work around this by
|
146
|
-
# parsing backtraces. It's slow, so we check only a few first frames.
|
147
|
-
exception.backtrace[0..2].each do |frame|
|
148
|
-
return true if frame =~ Patterns::EXECJS_SIMPLIFIED
|
149
|
-
end
|
150
|
-
elsif exception.cause && exception.cause.is_a?(ExecJS::RuntimeError)
|
151
|
-
return true
|
152
|
-
end
|
138
|
+
return true if exception.cause && exception.cause.is_a?(ExecJS::RuntimeError)
|
153
139
|
|
154
140
|
false
|
155
141
|
end
|
156
|
-
# rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
157
142
|
|
158
143
|
def stack_frame(config, regexp, stackframe)
|
159
144
|
if (match = match_frame(regexp, stackframe))
|
data/lib/airbrake-ruby/config.rb
CHANGED
@@ -86,7 +86,7 @@ module Airbrake
|
|
86
86
|
# @return [Integer] how many seconds to wait before sending collected route
|
87
87
|
# stats
|
88
88
|
# @api public
|
89
|
-
# @since
|
89
|
+
# @since v3.0.0
|
90
90
|
attr_accessor :route_stats_flush_period
|
91
91
|
|
92
92
|
# @param [Hash{Symbol=>Object}] user_config the hash to be used to build the
|
@@ -18,9 +18,12 @@ module Airbrake
|
|
18
18
|
# @return [Integer]
|
19
19
|
DEFAULT_WEIGHT = 0
|
20
20
|
|
21
|
-
def initialize
|
21
|
+
def initialize(config, context)
|
22
|
+
@config = config
|
23
|
+
@context = context
|
22
24
|
@filters = []
|
23
25
|
DEFAULT_FILTERS.each { |f| add_filter(f.new) }
|
26
|
+
add_default_filters
|
24
27
|
end
|
25
28
|
|
26
29
|
# Adds a filter to the filter chain. Sorts filters by weight.
|
@@ -44,5 +47,57 @@ module Airbrake
|
|
44
47
|
filter.call(notice)
|
45
48
|
end
|
46
49
|
end
|
50
|
+
|
51
|
+
# @return [String] customized inspect to lessen the amount of clutter
|
52
|
+
def inspect
|
53
|
+
@filters.map(&:class)
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [String] {#inspect} for PrettyPrint
|
57
|
+
def pretty_print(q)
|
58
|
+
q.text('[')
|
59
|
+
|
60
|
+
# Make nesting of the first element consistent on JRuby and MRI.
|
61
|
+
q.nest(2) { q.breakable }
|
62
|
+
|
63
|
+
q.nest(2) do
|
64
|
+
q.seplist(@filters) { |f| q.pp(f.class) }
|
65
|
+
end
|
66
|
+
q.text(']')
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# rubocop:disable Metrics/AbcSize
|
72
|
+
def add_default_filters
|
73
|
+
if (whitelist_keys = @config.whitelist_keys).any?
|
74
|
+
add_filter(
|
75
|
+
Airbrake::Filters::KeysWhitelist.new(@config.logger, whitelist_keys)
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
if (blacklist_keys = @config.blacklist_keys).any?
|
80
|
+
add_filter(
|
81
|
+
Airbrake::Filters::KeysBlacklist.new(@config.logger, blacklist_keys)
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
add_filter(Airbrake::Filters::ContextFilter.new(@context))
|
86
|
+
add_filter(Airbrake::Filters::ExceptionAttributesFilter.new(@config.logger))
|
87
|
+
|
88
|
+
return unless (root_directory = @config.root_directory)
|
89
|
+
[
|
90
|
+
Airbrake::Filters::RootDirectoryFilter,
|
91
|
+
Airbrake::Filters::GitRevisionFilter,
|
92
|
+
Airbrake::Filters::GitRepositoryFilter
|
93
|
+
].each do |filter|
|
94
|
+
add_filter(filter.new(root_directory))
|
95
|
+
end
|
96
|
+
|
97
|
+
add_filter(
|
98
|
+
Airbrake::Filters::GitLastCheckoutFilter.new(@config.logger, root_directory)
|
99
|
+
)
|
100
|
+
end
|
101
|
+
# rubocop:enable Metrics/AbcSize
|
47
102
|
end
|
48
103
|
end
|
@@ -9,6 +9,12 @@ module Airbrake
|
|
9
9
|
# @return [String] the label to be prepended to the log output
|
10
10
|
LOG_LABEL = '**Airbrake:'.freeze
|
11
11
|
|
12
|
+
# @return [String] inspect output template
|
13
|
+
INSPECT_TEMPLATE =
|
14
|
+
"#<#{self}:0x%<id>s project_id=\"%<project_id>s\" " \
|
15
|
+
"project_key=\"%<project_key>s\" " \
|
16
|
+
"host=\"%<host>s\" filter_chain=%<filter_chain>s>".freeze
|
17
|
+
|
12
18
|
# Creates a new Airbrake notifier with the given config options.
|
13
19
|
#
|
14
20
|
# @example Configuring with a Hash
|
@@ -30,13 +36,9 @@ module Airbrake
|
|
30
36
|
raise Airbrake::Error, @config.validation_error_message unless @config.valid?
|
31
37
|
|
32
38
|
@context = {}
|
33
|
-
|
34
|
-
@filter_chain = FilterChain.new
|
35
|
-
add_default_filters
|
36
|
-
|
39
|
+
@filter_chain = FilterChain.new(@config, @context)
|
37
40
|
@async_sender = AsyncSender.new(@config)
|
38
41
|
@sync_sender = SyncSender.new(@config)
|
39
|
-
|
40
42
|
@route_sender = RouteSender.new(@config)
|
41
43
|
end
|
42
44
|
|
@@ -102,6 +104,29 @@ module Airbrake
|
|
102
104
|
@route_sender.inc_request(*args)
|
103
105
|
end
|
104
106
|
|
107
|
+
# @return [String] customized inspect to lessen the amount of clutter
|
108
|
+
def inspect
|
109
|
+
format(
|
110
|
+
INSPECT_TEMPLATE,
|
111
|
+
id: (object_id << 1).to_s(16).rjust(16, '0'),
|
112
|
+
project_id: @config.project_id,
|
113
|
+
project_key: @config.project_key,
|
114
|
+
host: @config.host,
|
115
|
+
filter_chain: @filter_chain.inspect
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
# @return [String] {#inspect} for PrettyPrint
|
120
|
+
def pretty_print(q)
|
121
|
+
q.text("#<#{self.class}:0x#{(object_id << 1).to_s(16).rjust(16, '0')} ")
|
122
|
+
q.text(
|
123
|
+
"project_id=\"#{@config.project_id}\" project_key=\"#{@config.project_key}\" " \
|
124
|
+
"host=\"#{@config.host}\" filter_chain="
|
125
|
+
)
|
126
|
+
q.pp(@filter_chain)
|
127
|
+
q.text('>')
|
128
|
+
end
|
129
|
+
|
105
130
|
private
|
106
131
|
|
107
132
|
def convert_to_exception(ex)
|
@@ -152,39 +177,5 @@ module Airbrake
|
|
152
177
|
return caller_copy if clean_bt.empty?
|
153
178
|
clean_bt
|
154
179
|
end
|
155
|
-
|
156
|
-
# rubocop:disable Metrics/AbcSize
|
157
|
-
def add_default_filters
|
158
|
-
if (whitelist_keys = @config.whitelist_keys).any?
|
159
|
-
@filter_chain.add_filter(
|
160
|
-
Airbrake::Filters::KeysWhitelist.new(@config.logger, whitelist_keys)
|
161
|
-
)
|
162
|
-
end
|
163
|
-
|
164
|
-
if (blacklist_keys = @config.blacklist_keys).any?
|
165
|
-
@filter_chain.add_filter(
|
166
|
-
Airbrake::Filters::KeysBlacklist.new(@config.logger, blacklist_keys)
|
167
|
-
)
|
168
|
-
end
|
169
|
-
|
170
|
-
@filter_chain.add_filter(Airbrake::Filters::ContextFilter.new(@context))
|
171
|
-
@filter_chain.add_filter(
|
172
|
-
Airbrake::Filters::ExceptionAttributesFilter.new(@config.logger)
|
173
|
-
)
|
174
|
-
|
175
|
-
return unless (root_directory = @config.root_directory)
|
176
|
-
[
|
177
|
-
Airbrake::Filters::RootDirectoryFilter,
|
178
|
-
Airbrake::Filters::GitRevisionFilter,
|
179
|
-
Airbrake::Filters::GitRepositoryFilter
|
180
|
-
].each do |filter|
|
181
|
-
@filter_chain.add_filter(filter.new(root_directory))
|
182
|
-
end
|
183
|
-
|
184
|
-
@filter_chain.add_filter(
|
185
|
-
Airbrake::Filters::GitLastCheckoutFilter.new(@config.logger, root_directory)
|
186
|
-
)
|
187
|
-
end
|
188
|
-
# rubocop:enable Metrics/AbcSize
|
189
180
|
end
|
190
181
|
end
|
@@ -1,20 +1,76 @@
|
|
1
|
+
require 'tdigest'
|
2
|
+
require 'base64'
|
3
|
+
|
1
4
|
module Airbrake
|
2
5
|
# RouteSender aggregates information about requests and periodically sends
|
3
6
|
# collected data to Airbrake.
|
4
|
-
# @since
|
7
|
+
# @since v3.0.0
|
5
8
|
class RouteSender
|
9
|
+
# Monkey-patch https://github.com/castle/tdigest to pack with Big Endian
|
10
|
+
# (instead of Little Endian) since our backend wants it.
|
11
|
+
#
|
12
|
+
# @see https://github.com/castle/tdigest/blob/master/lib/tdigest/tdigest.rb
|
13
|
+
# @since v3.0.0
|
14
|
+
# @api private
|
15
|
+
module TDigestBigEndianness
|
16
|
+
refine TDigest::TDigest do
|
17
|
+
# rubocop:disable Metrics/AbcSize
|
18
|
+
def as_small_bytes
|
19
|
+
size = @centroids.size
|
20
|
+
output = [self.class::SMALL_ENCODING, compression, size]
|
21
|
+
x = 0
|
22
|
+
# delta encoding allows saving 4-bytes floats
|
23
|
+
mean_arr = @centroids.map do |_, c|
|
24
|
+
val = c.mean - x
|
25
|
+
x = c.mean
|
26
|
+
val
|
27
|
+
end
|
28
|
+
output += mean_arr
|
29
|
+
# Variable length encoding of numbers
|
30
|
+
c_arr = @centroids.each_with_object([]) do |(_, c), arr|
|
31
|
+
k = 0
|
32
|
+
n = c.n
|
33
|
+
while n < 0 || n > 0x7f
|
34
|
+
b = 0x80 | (0x7f & n)
|
35
|
+
arr << b
|
36
|
+
n = n >> 7
|
37
|
+
k += 1
|
38
|
+
raise 'Unreasonable large number' if k > 6
|
39
|
+
end
|
40
|
+
arr << n
|
41
|
+
end
|
42
|
+
output += c_arr
|
43
|
+
output.pack("NGNg#{size}C#{size}")
|
44
|
+
end
|
45
|
+
# rubocop:enable Metrics/AbcSize
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
using TDigestBigEndianness
|
50
|
+
|
6
51
|
# The key that represents a route.
|
7
52
|
RouteKey = Struct.new(:method, :route, :statusCode, :time)
|
8
53
|
|
9
54
|
# RouteStat holds data that describes a route's performance.
|
10
|
-
RouteStat = Struct.new(:count, :sum, :sumsq, :
|
55
|
+
RouteStat = Struct.new(:count, :sum, :sumsq, :tdigest) do
|
11
56
|
# @param [Integer] count The number of requests
|
12
57
|
# @param [Float] sum The sum of request duration in milliseconds
|
13
58
|
# @param [Float] sumsq The squared sum of request duration in milliseconds
|
14
|
-
# @param [
|
15
|
-
|
16
|
-
|
17
|
-
|
59
|
+
# @param [TDigest::TDigest] tdigest
|
60
|
+
def initialize(count: 0, sum: 0.0, sumsq: 0.0, tdigest: TDigest::TDigest.new)
|
61
|
+
super(count, sum, sumsq, tdigest)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Hash{String=>Object}] the route stat as a hash with compressed
|
65
|
+
# and serialized as binary base64 tdigest
|
66
|
+
def to_h
|
67
|
+
tdigest.compress!
|
68
|
+
{
|
69
|
+
'count' => count,
|
70
|
+
'sum' => sum,
|
71
|
+
'sumsq' => sumsq,
|
72
|
+
'tDigest' => Base64.strict_encode64(tdigest.as_small_bytes)
|
73
|
+
}
|
18
74
|
end
|
19
75
|
end
|
20
76
|
|
@@ -66,8 +122,7 @@ module Airbrake
|
|
66
122
|
stat.sum += ms
|
67
123
|
stat.sumsq += ms * ms
|
68
124
|
|
69
|
-
stat.
|
70
|
-
stat.max = ms if ms > stat.max
|
125
|
+
stat.tdigest.push(ms)
|
71
126
|
end
|
72
127
|
|
73
128
|
def schedule_flush(promise)
|
data/spec/backtrace_spec.rb
CHANGED
@@ -248,6 +248,8 @@ RSpec.describe Airbrake::Backtrace do
|
|
248
248
|
"/opt/rubies/ruby-2.3.1/lib/ruby/2.3.0/benchmark.rb:308:in `realtime'"]
|
249
249
|
end
|
250
250
|
|
251
|
+
let(:ex) { ExecJS::RuntimeError.new.tap { |e| e.set_backtrace(bt) } }
|
252
|
+
|
251
253
|
let(:parsed_backtrace) do
|
252
254
|
[{ file: '(execjs)', line: 6692, function: 'compile' },
|
253
255
|
{ file: '<anonymous>', line: 1, function: 'eval' },
|
@@ -261,31 +263,9 @@ RSpec.describe Airbrake::Backtrace do
|
|
261
263
|
function: 'realtime' }]
|
262
264
|
end
|
263
265
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
it "returns a properly formatted array of hashes" do
|
268
|
-
stub_const('ExecJS::RuntimeError', AirbrakeTestError)
|
269
|
-
stub_const('Airbrake::RUBY_20', false)
|
270
|
-
|
271
|
-
expect(described_class.parse(config, ex)).to eq(parsed_backtrace)
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
context "when on Ruby 2.0" do
|
276
|
-
context "and when exception's class isn't ExecJS" do
|
277
|
-
let(:ex) do
|
278
|
-
ActionView::Template::Error.new.tap { |e| e.set_backtrace(bt) }
|
279
|
-
end
|
280
|
-
|
281
|
-
it "returns a properly formatted array of hashes" do
|
282
|
-
stub_const('ActionView::Template::Error', AirbrakeTestError)
|
283
|
-
stub_const('ExecJS::RuntimeError', NameError)
|
284
|
-
stub_const('Airbrake::RUBY_20', true)
|
285
|
-
|
286
|
-
expect(described_class.parse(config, ex)).to eq(parsed_backtrace)
|
287
|
-
end
|
288
|
-
end
|
266
|
+
it "returns a properly formatted array of hashes" do
|
267
|
+
stub_const('ExecJS::RuntimeError', AirbrakeTestError)
|
268
|
+
expect(described_class.parse(config, ex)).to eq(parsed_backtrace)
|
289
269
|
end
|
290
270
|
end
|
291
271
|
|
data/spec/filter_chain_spec.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe Airbrake::FilterChain do
|
4
|
+
subject { described_class.new(config, {}) }
|
5
|
+
|
6
|
+
let(:config) { Airbrake::Config.new }
|
7
|
+
|
4
8
|
let(:notice) do
|
5
|
-
Airbrake::Notice.new(
|
9
|
+
Airbrake::Notice.new(config, AirbrakeTestError.new)
|
6
10
|
end
|
7
11
|
|
8
12
|
describe "#refine" do
|
data/spec/notifier_spec.rb
CHANGED
@@ -459,5 +459,35 @@ RSpec.describe Airbrake::Notifier do
|
|
459
459
|
subject.inc_request('GET', '/foo', 200, 1000, t)
|
460
460
|
end
|
461
461
|
end
|
462
|
+
|
463
|
+
describe "#inspect" do
|
464
|
+
it "displays object information" do
|
465
|
+
expect(subject.inspect).to match(/
|
466
|
+
#<Airbrake::Notifier:0x\w+\s
|
467
|
+
project_id="\d+"\s
|
468
|
+
project_key=".+"\s
|
469
|
+
host="http.+"\s
|
470
|
+
filter_chain=\[.+\]>
|
471
|
+
/x)
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
describe "#pretty_print" do
|
476
|
+
it "displays object information in a beautiful way" do
|
477
|
+
q = PP.new
|
478
|
+
|
479
|
+
# Guarding is needed to fix JRuby failure:
|
480
|
+
# NoMethodError: undefined method `[]' for nil:NilClass
|
481
|
+
q.guard_inspect_key { subject.pretty_print(q) }
|
482
|
+
|
483
|
+
expect(q.output).to match(/
|
484
|
+
#<Airbrake::Notifier:0x\w+\s
|
485
|
+
project_id="\d+"\s
|
486
|
+
project_key=".+"\s
|
487
|
+
host="http.+"\s
|
488
|
+
filter_chain=\[\n\s\s
|
489
|
+
/x)
|
490
|
+
end
|
491
|
+
end
|
462
492
|
end
|
463
493
|
# rubocop:enable Layout/DotPosition
|
data/spec/response_spec.rb
CHANGED
@@ -5,7 +5,7 @@ RSpec.describe Airbrake::Response do
|
|
5
5
|
let(:out) { StringIO.new }
|
6
6
|
let(:logger) { Logger.new(out) }
|
7
7
|
|
8
|
-
[200, 201].each do |code|
|
8
|
+
[200, 201, 204].each do |code|
|
9
9
|
context "when response code is #{code}" do
|
10
10
|
it "logs response body" do
|
11
11
|
described_class.parse(OpenStruct.new(code: code, body: '{}'), logger)
|
data/spec/route_sender_spec.rb
CHANGED
@@ -48,10 +48,10 @@ RSpec.describe Airbrake::RouteSender do
|
|
48
48
|
{"routes":\[
|
49
49
|
{"method":"GET","route":"/foo","statusCode":200,
|
50
50
|
"time":"2018-01-01T00:00:00\+00:00","count":1,"sum":24.0,
|
51
|
-
"sumsq":576.0,"
|
51
|
+
"sumsq":576.0,"tDigest":"AAAAAkBZAAAAAAAAAAAAAUHAAAAB"},
|
52
52
|
{"method":"GET","route":"/foo","statusCode":200,
|
53
53
|
"time":"2018-01-01T00:01:00\+00:00","count":1,"sum":10.0,
|
54
|
-
"sumsq":100.0,"
|
54
|
+
"sumsq":100.0,"tDigest":"AAAAAkBZAAAAAAAAAAAAAUEgAAAB"}\]}
|
55
55
|
\z|x
|
56
56
|
)
|
57
57
|
).to have_been_made
|
@@ -67,10 +67,10 @@ RSpec.describe Airbrake::RouteSender do
|
|
67
67
|
{"routes":\[
|
68
68
|
{"method":"GET","route":"/foo","statusCode":200,
|
69
69
|
"time":"2018-01-01T00:00:00\+00:00","count":1,"sum":24.0,
|
70
|
-
"sumsq":576.0,"
|
70
|
+
"sumsq":576.0,"tDigest":"AAAAAkBZAAAAAAAAAAAAAUHAAAAB"},
|
71
71
|
{"method":"POST","route":"/foo","statusCode":200,
|
72
72
|
"time":"2018-01-01T00:00:00\+00:00","count":1,"sum":10.0,
|
73
|
-
"sumsq":100.0,"
|
73
|
+
"sumsq":100.0,"tDigest":"AAAAAkBZAAAAAAAAAAAAAUEgAAAB"}\]}
|
74
74
|
\z|x
|
75
75
|
)
|
76
76
|
).to have_been_made
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: airbrake-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0.rc.2
|
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: 2018-10-
|
11
|
+
date: 2018-10-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: tdigest
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.1.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.1.1
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rspec
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -104,12 +118,11 @@ description: |
|
|
104
118
|
Airbrake Ruby is a plain Ruby notifier for Airbrake (https://airbrake.io), the
|
105
119
|
leading exception reporting service. Airbrake Ruby provides minimalist API that
|
106
120
|
enables the ability to send any Ruby exception to the Airbrake dashboard. The
|
107
|
-
library is extremely lightweight
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
Sidekiq, Delayed Job and many more.
|
121
|
+
library is extremely lightweight and it perfectly suits plain Ruby applications.
|
122
|
+
For apps that are built with Rails, Sinatra or any other Rack-compliant web
|
123
|
+
framework we offer the airbrake gem (https://github.com/airbrake/airbrake). It
|
124
|
+
has additional features such as reporting of any unhandled exceptions
|
125
|
+
automatically, integrations with Resque, Sidekiq, Delayed Job and many more.
|
113
126
|
email: support@airbrake.io
|
114
127
|
executables: []
|
115
128
|
extensions: []
|
@@ -194,7 +207,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
194
207
|
requirements:
|
195
208
|
- - ">="
|
196
209
|
- !ruby/object:Gem::Version
|
197
|
-
version: '2.
|
210
|
+
version: '2.1'
|
198
211
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
199
212
|
requirements:
|
200
213
|
- - ">"
|