airbrake-ruby 6.1.2 → 6.2.1
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/async_sender.rb +13 -15
- data/lib/airbrake-ruby/backlog.rb +123 -0
- data/lib/airbrake-ruby/config.rb +22 -7
- data/lib/airbrake-ruby/notice_notifier.rb +1 -0
- data/lib/airbrake-ruby/performance_notifier.rb +1 -0
- data/lib/airbrake-ruby/response.rb +56 -5
- data/lib/airbrake-ruby/sync_sender.rb +39 -8
- data/lib/airbrake-ruby/tdigest.rb +1 -1
- data/lib/airbrake-ruby/thread_pool.rb +0 -1
- data/lib/airbrake-ruby/version.rb +1 -1
- data/lib/airbrake-ruby.rb +2 -1
- metadata +104 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 868a2afd78cb0fe3fa7c94fdfcef19cc34c3355ebe9665945f37326410deaa95
|
4
|
+
data.tar.gz: 6e21e26d4999a8bcb1044f96952718835d9c49b79167593be0d4f2529e7b8153
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26d41719d656683977497104627ae82024868b75372db7886b18bef8bb654f6f5fe7617770b2d981063fb3dfa139e1011b65ad190ed93fa850118ca06bf0bb8f
|
7
|
+
data.tar.gz: 5d1fcffd4559b02a2e0a3c57561f6cec279e4561db5c28eb09dadfd4a174347ad7e8d62df9162248359f78b70fac89c363f9b673cc71d1d303faa28db2681c68
|
@@ -5,20 +5,20 @@ module Airbrake
|
|
5
5
|
# @api private
|
6
6
|
# @since v1.0.0
|
7
7
|
class AsyncSender
|
8
|
-
include Loggable
|
9
|
-
|
10
8
|
def initialize(method = :post, name = 'async-sender')
|
11
9
|
@config = Airbrake::Config.instance
|
12
|
-
@
|
10
|
+
@sync_sender = SyncSender.new(method)
|
13
11
|
@name = name
|
14
12
|
end
|
15
13
|
|
16
14
|
# Asynchronously sends a notice to Airbrake.
|
17
15
|
#
|
18
|
-
# @param [
|
16
|
+
# @param [Airbrake::Notice] data Whatever needs to be sent
|
17
|
+
# @param [Airbrake::Promise] promise
|
18
|
+
# @param [URI] endpoint Where to send +data+
|
19
19
|
# @return [Airbrake::Promise]
|
20
|
-
def send(
|
21
|
-
unless thread_pool << [
|
20
|
+
def send(data, promise, endpoint = @config.error_endpoint)
|
21
|
+
unless thread_pool << [data, promise, endpoint]
|
22
22
|
return promise.reject(
|
23
23
|
"AsyncSender has reached its capacity of #{@config.queue_size}",
|
24
24
|
)
|
@@ -29,6 +29,7 @@ module Airbrake
|
|
29
29
|
|
30
30
|
# @return [void]
|
31
31
|
def close
|
32
|
+
@sync_sender.close
|
32
33
|
thread_pool.close
|
33
34
|
end
|
34
35
|
|
@@ -45,15 +46,12 @@ module Airbrake
|
|
45
46
|
private
|
46
47
|
|
47
48
|
def thread_pool
|
48
|
-
@thread_pool ||=
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
block: proc { |args| sender.send(*args) },
|
55
|
-
)
|
56
|
-
end
|
49
|
+
@thread_pool ||= ThreadPool.new(
|
50
|
+
name: @name,
|
51
|
+
worker_size: @config.workers,
|
52
|
+
queue_size: @config.queue_size,
|
53
|
+
block: proc { |args| @sync_sender.send(*args) },
|
54
|
+
)
|
57
55
|
end
|
58
56
|
end
|
59
57
|
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# Backlog accepts notices and APM events and synchronously sends them in the
|
3
|
+
# background at regular intervals. The backlog is a queue of data that failed
|
4
|
+
# to be sent due to some error. In a nutshell, it's a retry mechanism.
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
# @since v6.2.0
|
8
|
+
class Backlog
|
9
|
+
include Loggable
|
10
|
+
|
11
|
+
# @return [Integer] how many records to keep in the backlog
|
12
|
+
BACKLOG_SIZE = 100
|
13
|
+
|
14
|
+
# @return [Integer] flush period in seconds
|
15
|
+
TWO_MINUTES = 60 * 2
|
16
|
+
|
17
|
+
def initialize(sync_sender, flush_period = TWO_MINUTES)
|
18
|
+
@sync_sender = sync_sender
|
19
|
+
@flush_period = flush_period
|
20
|
+
@queue = SizedQueue.new(BACKLOG_SIZE).extend(MonitorMixin)
|
21
|
+
@has_backlog_data = @queue.new_cond
|
22
|
+
@schedule_flush = nil
|
23
|
+
|
24
|
+
@seen = Set.new
|
25
|
+
end
|
26
|
+
|
27
|
+
# Appends data to the backlog. Once appended, the flush schedule will
|
28
|
+
# start. Chainable.
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# backlog << [{ 'data' => 1 }, 'https://airbrake.io/api']
|
32
|
+
#
|
33
|
+
# @param [Array<#to_json, String>] data An array of two elements, where the
|
34
|
+
# first element is the data we are sending and the second element is the
|
35
|
+
# URL that we are sending to
|
36
|
+
# @return [self]
|
37
|
+
def <<(data)
|
38
|
+
@queue.synchronize do
|
39
|
+
return self if @seen.include?(data)
|
40
|
+
|
41
|
+
@seen << data
|
42
|
+
|
43
|
+
begin
|
44
|
+
@queue.push(data, true)
|
45
|
+
rescue ThreadError
|
46
|
+
logger.error("#{LOG_LABEL} Airbrake::Backlog full")
|
47
|
+
return self
|
48
|
+
end
|
49
|
+
|
50
|
+
@has_backlog_data.signal
|
51
|
+
schedule_flush
|
52
|
+
|
53
|
+
self
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Closes all the resources that this sender has allocated.
|
58
|
+
#
|
59
|
+
# @return [void]
|
60
|
+
# @since v6.2.0
|
61
|
+
def close
|
62
|
+
@queue.synchronize do
|
63
|
+
if @schedule_flush
|
64
|
+
@schedule_flush.kill
|
65
|
+
logger.debug("#{LOG_LABEL} Airbrake::Backlog closed")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def schedule_flush
|
73
|
+
@schedule_flush ||= Thread.new do
|
74
|
+
loop do
|
75
|
+
@queue.synchronize do
|
76
|
+
wait
|
77
|
+
next if @queue.empty?
|
78
|
+
|
79
|
+
flush
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def wait
|
86
|
+
@has_backlog_data.wait(@flush_period) while time_elapsed < @flush_period
|
87
|
+
@last_flush = nil
|
88
|
+
end
|
89
|
+
|
90
|
+
def time_elapsed
|
91
|
+
MonotonicTime.time_in_s - last_flush
|
92
|
+
end
|
93
|
+
|
94
|
+
def last_flush
|
95
|
+
@last_flush ||= MonotonicTime.time_in_s
|
96
|
+
end
|
97
|
+
|
98
|
+
def flush
|
99
|
+
unless @queue.empty?
|
100
|
+
logger.debug(
|
101
|
+
"#{LOG_LABEL} Airbrake::Backlog flushing #{@queue.size} messages",
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
failed = 0
|
106
|
+
|
107
|
+
until @queue.empty?
|
108
|
+
data, endpoint = @queue.pop
|
109
|
+
promise = Airbrake::Promise.new
|
110
|
+
@sync_sender.send(data, promise, endpoint)
|
111
|
+
failed += 1 if promise.rejected?
|
112
|
+
end
|
113
|
+
|
114
|
+
if failed > 0
|
115
|
+
logger.debug(
|
116
|
+
"#{LOG_LABEL} Airbrake::Backlog #{failed} messages were not flushed",
|
117
|
+
)
|
118
|
+
end
|
119
|
+
|
120
|
+
@seen.clear
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/lib/airbrake-ruby/config.rb
CHANGED
@@ -44,12 +44,8 @@ module Airbrake
|
|
44
44
|
# @return [String] the host, which provides the API endpoint to which
|
45
45
|
# exceptions should be sent
|
46
46
|
# @api public
|
47
|
-
attr_accessor :host
|
48
|
-
|
49
|
-
# @since v5.0.0
|
50
|
-
alias error_host host
|
51
47
|
# @since v5.0.0
|
52
|
-
|
48
|
+
attr_accessor :error_host
|
53
49
|
|
54
50
|
# @return [String] the host, which provides the API endpoint to which
|
55
51
|
# APM data should be sent
|
@@ -136,6 +132,12 @@ module Airbrake
|
|
136
132
|
# @since v5.2.0
|
137
133
|
attr_accessor :remote_config
|
138
134
|
|
135
|
+
# @return [Boolean] true if the library should keep a backlog of failed
|
136
|
+
# notices or APM events and retry them after an interval, false otherwise
|
137
|
+
# @api public
|
138
|
+
# @since v6.2.0
|
139
|
+
attr_accessor :backlog
|
140
|
+
|
139
141
|
class << self
|
140
142
|
# @return [Config]
|
141
143
|
attr_writer :instance
|
@@ -157,8 +159,7 @@ module Airbrake
|
|
157
159
|
self.logger = ::Logger.new(File::NULL).tap { |l| l.level = Logger::WARN }
|
158
160
|
self.project_id = user_config[:project_id]
|
159
161
|
self.project_key = user_config[:project_key]
|
160
|
-
self.error_host = 'https://api.airbrake.io'
|
161
|
-
self.apm_host = 'https://api.airbrake.io'
|
162
|
+
self.error_host = self.apm_host = 'https://api.airbrake.io'
|
162
163
|
self.remote_config_host = 'https://notifier-configs.airbrake.io'
|
163
164
|
|
164
165
|
self.ignore_environments = []
|
@@ -180,6 +181,7 @@ module Airbrake
|
|
180
181
|
self.job_stats = true
|
181
182
|
self.error_notifications = true
|
182
183
|
self.remote_config = true
|
184
|
+
self.backlog = true
|
183
185
|
|
184
186
|
merge(user_config)
|
185
187
|
end
|
@@ -262,6 +264,19 @@ module Airbrake
|
|
262
264
|
end
|
263
265
|
end
|
264
266
|
|
267
|
+
HOST_DEPRECATION_MSG = "**Airbrake: the 'host' option is deprecated. Use " \
|
268
|
+
"'error_host' instead".freeze
|
269
|
+
|
270
|
+
def host
|
271
|
+
logger.warn(HOST_DEPRECATION_MSG)
|
272
|
+
@error_host
|
273
|
+
end
|
274
|
+
|
275
|
+
def host=(value)
|
276
|
+
logger.warn(HOST_DEPRECATION_MSG)
|
277
|
+
@error_host = value
|
278
|
+
end
|
279
|
+
|
265
280
|
private
|
266
281
|
|
267
282
|
def set_option(option, value)
|
@@ -8,9 +8,56 @@ module Airbrake
|
|
8
8
|
# @return [Integer] the limit of the response body
|
9
9
|
TRUNCATE_LIMIT = 100
|
10
10
|
|
11
|
+
# @return [Integer] HTTP code returned when the server cannot or will not
|
12
|
+
# process the request due to something that is perceived to be a client
|
13
|
+
# error
|
14
|
+
# @since v6.2.0
|
15
|
+
BAD_REQUEST = 400
|
16
|
+
|
17
|
+
# @return [Integer] HTTP code returned when client request has not been
|
18
|
+
# completed because it lacks valid authentication credentials for the
|
19
|
+
# requested resource
|
20
|
+
# @since v6.2.0
|
21
|
+
UNAUTHORIZED = 401
|
22
|
+
|
23
|
+
# @return [Integer] HTTP code returned when the server understands the
|
24
|
+
# request but refuses to authorize it
|
25
|
+
# @since v6.2.0
|
26
|
+
FORBIDDEN = 403
|
27
|
+
|
28
|
+
# @return [Integer] HTTP code returned when the server would like to shut
|
29
|
+
# down this unused connection
|
30
|
+
# @since v6.2.0
|
31
|
+
REQUEST_TIMEOUT = 408
|
32
|
+
|
33
|
+
# @return [Integer] HTTP code returned when there's a request conflict with
|
34
|
+
# the current state of the target resource
|
35
|
+
# @since v6.2.0
|
36
|
+
CONFLICT = 409
|
37
|
+
|
38
|
+
# @return [Integer]
|
39
|
+
# @since v6.2.0
|
40
|
+
ENHANCE_YOUR_CALM = 420
|
41
|
+
|
11
42
|
# @return [Integer] HTTP code returned when an IP sends over 10k/min notices
|
12
43
|
TOO_MANY_REQUESTS = 429
|
13
44
|
|
45
|
+
# @return [Integer] HTTP code returned when the server encountered an
|
46
|
+
# unexpected condition that prevented it from fulfilling the request
|
47
|
+
# @since v6.2.0
|
48
|
+
INTERNAL_SERVER_ERROR = 500
|
49
|
+
|
50
|
+
# @return [Integer] HTTP code returened when the server, while acting as a
|
51
|
+
# gateway or proxy, received an invalid response from the upstream server
|
52
|
+
# @since v6.2.0
|
53
|
+
BAD_GATEWAY = 502
|
54
|
+
|
55
|
+
# @return [Integer] HTTP code returened when the server, while acting as a
|
56
|
+
# gateway or proxy, did not get a response in time from the upstream
|
57
|
+
# server that it needed in order to complete the request
|
58
|
+
# @since v6.2.0
|
59
|
+
GATEWAY_TIMEOUT = 504
|
60
|
+
|
14
61
|
class << self
|
15
62
|
include Loggable
|
16
63
|
end
|
@@ -33,24 +80,28 @@ module Airbrake
|
|
33
80
|
parsed_body = JSON.parse(body)
|
34
81
|
logger.debug("#{LOG_LABEL} #{name} (#{code}): #{parsed_body}")
|
35
82
|
parsed_body
|
36
|
-
when
|
83
|
+
when BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, ENHANCE_YOUR_CALM
|
37
84
|
parsed_body = JSON.parse(body)
|
38
85
|
logger.error("#{LOG_LABEL} #{parsed_body['message']}")
|
39
|
-
parsed_body
|
86
|
+
parsed_body.merge('code' => code, 'error' => parsed_body['message'])
|
40
87
|
when TOO_MANY_REQUESTS
|
41
88
|
parsed_body = JSON.parse(body)
|
42
89
|
msg = "#{LOG_LABEL} #{parsed_body['message']}"
|
43
90
|
logger.error(msg)
|
44
|
-
{
|
91
|
+
{
|
92
|
+
'code' => code,
|
93
|
+
'error' => msg,
|
94
|
+
'rate_limit_reset' => rate_limit_reset(response),
|
95
|
+
}
|
45
96
|
else
|
46
97
|
body_msg = truncated_body(body)
|
47
98
|
logger.error("#{LOG_LABEL} unexpected code (#{code}). Body: #{body_msg}")
|
48
|
-
{ 'error' => body_msg }
|
99
|
+
{ 'code' => code, 'error' => body_msg }
|
49
100
|
end
|
50
101
|
rescue StandardError => ex
|
51
102
|
body_msg = truncated_body(body)
|
52
103
|
logger.error("#{LOG_LABEL} error while parsing body (#{ex}). Body: #{body_msg}")
|
53
|
-
{ 'error' => ex.inspect }
|
104
|
+
{ 'code' => code, 'error' => ex.inspect }
|
54
105
|
end
|
55
106
|
end
|
56
107
|
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
@@ -9,6 +9,20 @@ module Airbrake
|
|
9
9
|
# @return [String] body for HTTP requests
|
10
10
|
CONTENT_TYPE = 'application/json'.freeze
|
11
11
|
|
12
|
+
# @return [Array<Integer>] response codes that are good to be backlogged
|
13
|
+
# @since v6.2.0
|
14
|
+
BACKLOGGABLE_STATUS_CODES = [
|
15
|
+
Response::BAD_REQUEST,
|
16
|
+
Response::FORBIDDEN,
|
17
|
+
Response::ENHANCE_YOUR_CALM,
|
18
|
+
Response::REQUEST_TIMEOUT,
|
19
|
+
Response::CONFLICT,
|
20
|
+
Response::TOO_MANY_REQUESTS,
|
21
|
+
Response::INTERNAL_SERVER_ERROR,
|
22
|
+
Response::BAD_GATEWAY,
|
23
|
+
Response::GATEWAY_TIMEOUT,
|
24
|
+
].freeze
|
25
|
+
|
12
26
|
include Loggable
|
13
27
|
|
14
28
|
# @param [Symbol] method HTTP method to use to send payload
|
@@ -16,6 +30,7 @@ module Airbrake
|
|
16
30
|
@config = Airbrake::Config.instance
|
17
31
|
@method = method
|
18
32
|
@rate_limit_reset = Time.now
|
33
|
+
@backlog = Backlog.new(self) if @config.backlog
|
19
34
|
end
|
20
35
|
|
21
36
|
# Sends a POST or PUT request to the given +endpoint+ with the +data+ payload.
|
@@ -26,15 +41,11 @@ module Airbrake
|
|
26
41
|
def send(data, promise, endpoint = @config.error_endpoint)
|
27
42
|
return promise if rate_limited_ip?(promise)
|
28
43
|
|
29
|
-
response = nil
|
30
44
|
req = build_request(endpoint, data)
|
31
|
-
|
32
45
|
return promise if missing_body?(req, promise)
|
33
46
|
|
34
|
-
https = build_https(endpoint)
|
35
|
-
|
36
47
|
begin
|
37
|
-
response =
|
48
|
+
response = build_https(endpoint).request(req)
|
38
49
|
rescue StandardError => ex
|
39
50
|
reason = "#{LOG_LABEL} HTTP error: #{ex}"
|
40
51
|
logger.error(reason)
|
@@ -42,15 +53,22 @@ module Airbrake
|
|
42
53
|
end
|
43
54
|
|
44
55
|
parsed_resp = Response.parse(response)
|
45
|
-
|
46
|
-
|
47
|
-
end
|
56
|
+
handle_rate_limit(parsed_resp)
|
57
|
+
@backlog << [data, endpoint] if add_to_backlog?(parsed_resp)
|
48
58
|
|
49
59
|
return promise.reject(parsed_resp['error']) if parsed_resp.key?('error')
|
50
60
|
|
51
61
|
promise.resolve(parsed_resp)
|
52
62
|
end
|
53
63
|
|
64
|
+
# Closes all the resources that this sender has allocated.
|
65
|
+
#
|
66
|
+
# @return [void]
|
67
|
+
# @since v6.2.0
|
68
|
+
def close
|
69
|
+
@backlog.close
|
70
|
+
end
|
71
|
+
|
54
72
|
private
|
55
73
|
|
56
74
|
def build_https(uri)
|
@@ -86,6 +104,19 @@ module Airbrake
|
|
86
104
|
req
|
87
105
|
end
|
88
106
|
|
107
|
+
def handle_rate_limit(parsed_resp)
|
108
|
+
return unless parsed_resp.key?('rate_limit_reset')
|
109
|
+
|
110
|
+
@rate_limit_reset = parsed_resp['rate_limit_reset']
|
111
|
+
end
|
112
|
+
|
113
|
+
def add_to_backlog?(parsed_resp)
|
114
|
+
return unless @backlog
|
115
|
+
return unless parsed_resp.key?('code')
|
116
|
+
|
117
|
+
BACKLOGGABLE_STATUS_CODES.include?(parsed_resp['code'])
|
118
|
+
end
|
119
|
+
|
89
120
|
def proxy_params
|
90
121
|
return unless @config.proxy.key?(:host)
|
91
122
|
|
data/lib/airbrake-ruby.rb
CHANGED
@@ -18,9 +18,9 @@ require 'airbrake-ruby/remote_settings/settings_data'
|
|
18
18
|
require 'airbrake-ruby/remote_settings'
|
19
19
|
require 'airbrake-ruby/promise'
|
20
20
|
require 'airbrake-ruby/thread_pool'
|
21
|
+
require 'airbrake-ruby/response'
|
21
22
|
require 'airbrake-ruby/sync_sender'
|
22
23
|
require 'airbrake-ruby/async_sender'
|
23
|
-
require 'airbrake-ruby/response'
|
24
24
|
require 'airbrake-ruby/nested_exception'
|
25
25
|
require 'airbrake-ruby/ignorable'
|
26
26
|
require 'airbrake-ruby/inspectable'
|
@@ -59,6 +59,7 @@ require 'airbrake-ruby/monotonic_time'
|
|
59
59
|
require 'airbrake-ruby/timed_trace'
|
60
60
|
require 'airbrake-ruby/queue'
|
61
61
|
require 'airbrake-ruby/context'
|
62
|
+
require 'airbrake-ruby/backlog'
|
62
63
|
|
63
64
|
# Airbrake is a thin wrapper around instances of the notifier classes (such as
|
64
65
|
# notice, performance & deploy notifiers). It creates a way to access them via a
|
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: 6.1
|
4
|
+
version: 6.2.1
|
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:
|
11
|
+
date: 2023-03-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rbtree3
|
@@ -16,14 +16,112 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.6'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
26
|
+
version: '0.6'
|
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: '13'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '13'
|
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: '3.8'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.8'
|
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'
|
27
125
|
description: |
|
28
126
|
Airbrake Ruby is a plain Ruby notifier for Airbrake (https://airbrake.io), the
|
29
127
|
leading exception reporting service. Airbrake Ruby provides minimalist API that
|
@@ -40,6 +138,7 @@ extra_rdoc_files: []
|
|
40
138
|
files:
|
41
139
|
- lib/airbrake-ruby.rb
|
42
140
|
- lib/airbrake-ruby/async_sender.rb
|
141
|
+
- lib/airbrake-ruby/backlog.rb
|
43
142
|
- lib/airbrake-ruby/backtrace.rb
|
44
143
|
- lib/airbrake-ruby/benchmark.rb
|
45
144
|
- lib/airbrake-ruby/code_hunk.rb
|
@@ -113,7 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
212
|
- !ruby/object:Gem::Version
|
114
213
|
version: '0'
|
115
214
|
requirements: []
|
116
|
-
rubygems_version: 3.
|
215
|
+
rubygems_version: 3.4.6
|
117
216
|
signing_key:
|
118
217
|
specification_version: 4
|
119
218
|
summary: Ruby notifier for https://airbrake.io
|