airbrake-ruby 3.2.2-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 +7 -0
- data/lib/airbrake-ruby.rb +554 -0
- data/lib/airbrake-ruby/async_sender.rb +119 -0
- data/lib/airbrake-ruby/backtrace.rb +194 -0
- data/lib/airbrake-ruby/code_hunk.rb +53 -0
- data/lib/airbrake-ruby/config.rb +238 -0
- data/lib/airbrake-ruby/config/validator.rb +63 -0
- data/lib/airbrake-ruby/deploy_notifier.rb +47 -0
- data/lib/airbrake-ruby/file_cache.rb +48 -0
- data/lib/airbrake-ruby/filter_chain.rb +95 -0
- data/lib/airbrake-ruby/filters/context_filter.rb +29 -0
- data/lib/airbrake-ruby/filters/dependency_filter.rb +31 -0
- data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +45 -0
- data/lib/airbrake-ruby/filters/gem_root_filter.rb +33 -0
- data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +90 -0
- data/lib/airbrake-ruby/filters/git_repository_filter.rb +42 -0
- data/lib/airbrake-ruby/filters/git_revision_filter.rb +66 -0
- data/lib/airbrake-ruby/filters/keys_blacklist.rb +50 -0
- data/lib/airbrake-ruby/filters/keys_filter.rb +140 -0
- data/lib/airbrake-ruby/filters/keys_whitelist.rb +49 -0
- data/lib/airbrake-ruby/filters/root_directory_filter.rb +28 -0
- data/lib/airbrake-ruby/filters/sql_filter.rb +104 -0
- data/lib/airbrake-ruby/filters/system_exit_filter.rb +23 -0
- data/lib/airbrake-ruby/filters/thread_filter.rb +92 -0
- data/lib/airbrake-ruby/hash_keyable.rb +37 -0
- data/lib/airbrake-ruby/ignorable.rb +44 -0
- data/lib/airbrake-ruby/nested_exception.rb +39 -0
- data/lib/airbrake-ruby/notice.rb +165 -0
- data/lib/airbrake-ruby/notice_notifier.rb +228 -0
- data/lib/airbrake-ruby/performance_notifier.rb +161 -0
- data/lib/airbrake-ruby/promise.rb +99 -0
- data/lib/airbrake-ruby/response.rb +71 -0
- data/lib/airbrake-ruby/stat.rb +56 -0
- data/lib/airbrake-ruby/sync_sender.rb +111 -0
- data/lib/airbrake-ruby/tdigest.rb +393 -0
- data/lib/airbrake-ruby/time_truncate.rb +17 -0
- data/lib/airbrake-ruby/truncator.rb +115 -0
- data/lib/airbrake-ruby/version.rb +6 -0
- data/spec/airbrake_spec.rb +171 -0
- data/spec/async_sender_spec.rb +154 -0
- data/spec/backtrace_spec.rb +438 -0
- data/spec/code_hunk_spec.rb +118 -0
- data/spec/config/validator_spec.rb +189 -0
- data/spec/config_spec.rb +281 -0
- data/spec/deploy_notifier_spec.rb +41 -0
- data/spec/file_cache.rb +36 -0
- data/spec/filter_chain_spec.rb +83 -0
- data/spec/filters/context_filter_spec.rb +25 -0
- data/spec/filters/dependency_filter_spec.rb +14 -0
- data/spec/filters/exception_attributes_filter_spec.rb +63 -0
- data/spec/filters/gem_root_filter_spec.rb +44 -0
- data/spec/filters/git_last_checkout_filter_spec.rb +48 -0
- data/spec/filters/git_repository_filter.rb +53 -0
- data/spec/filters/git_revision_filter_spec.rb +126 -0
- data/spec/filters/keys_blacklist_spec.rb +236 -0
- data/spec/filters/keys_whitelist_spec.rb +205 -0
- data/spec/filters/root_directory_filter_spec.rb +42 -0
- data/spec/filters/sql_filter_spec.rb +219 -0
- data/spec/filters/system_exit_filter_spec.rb +14 -0
- data/spec/filters/thread_filter_spec.rb +279 -0
- data/spec/fixtures/notroot.txt +7 -0
- data/spec/fixtures/project_root/code.rb +221 -0
- data/spec/fixtures/project_root/empty_file.rb +0 -0
- data/spec/fixtures/project_root/long_line.txt +1 -0
- data/spec/fixtures/project_root/short_file.rb +3 -0
- data/spec/fixtures/project_root/vendor/bundle/ignored_file.rb +5 -0
- data/spec/helpers.rb +9 -0
- data/spec/ignorable_spec.rb +14 -0
- data/spec/nested_exception_spec.rb +75 -0
- data/spec/notice_notifier_spec.rb +436 -0
- data/spec/notice_notifier_spec/options_spec.rb +266 -0
- data/spec/notice_spec.rb +297 -0
- data/spec/performance_notifier_spec.rb +287 -0
- data/spec/promise_spec.rb +165 -0
- data/spec/response_spec.rb +82 -0
- data/spec/spec_helper.rb +102 -0
- data/spec/stat_spec.rb +35 -0
- data/spec/sync_sender_spec.rb +140 -0
- data/spec/tdigest_spec.rb +230 -0
- data/spec/time_truncate_spec.rb +13 -0
- data/spec/truncator_spec.rb +238 -0
- metadata +278 -0
@@ -0,0 +1,161 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# QueryNotifier aggregates information about SQL queries and periodically sends
|
3
|
+
# collected data to Airbrake.
|
4
|
+
#
|
5
|
+
# @api public
|
6
|
+
# @since v3.2.0
|
7
|
+
class PerformanceNotifier
|
8
|
+
# @param [Airbrake::Config] config
|
9
|
+
def initialize(config)
|
10
|
+
@config =
|
11
|
+
if config.is_a?(Config)
|
12
|
+
config
|
13
|
+
else
|
14
|
+
loc = caller_locations(1..1).first
|
15
|
+
signature = "#{self.class.name}##{__method__}"
|
16
|
+
warn(
|
17
|
+
"#{loc.path}:#{loc.lineno}: warning: passing a Hash to #{signature} " \
|
18
|
+
'is deprecated. Pass `Airbrake::Config` instead'
|
19
|
+
)
|
20
|
+
Config.new(config)
|
21
|
+
end
|
22
|
+
|
23
|
+
@flush_period = @config.performance_stats_flush_period
|
24
|
+
@sender = SyncSender.new(@config, :put)
|
25
|
+
@payload = {}
|
26
|
+
@schedule_flush = nil
|
27
|
+
@mutex = Mutex.new
|
28
|
+
@filter_chain = FilterChain.new
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param [Hash] resource
|
32
|
+
# @param [Airbrake::Promise] promise
|
33
|
+
# @see Airbrake.notify_query
|
34
|
+
# @see Airbrake.notify_request
|
35
|
+
def notify(resource, promise = Airbrake::Promise.new)
|
36
|
+
if @config.ignored_environment?
|
37
|
+
return promise.reject("The '#{@config.environment}' environment is ignored")
|
38
|
+
end
|
39
|
+
|
40
|
+
unless @config.performance_stats
|
41
|
+
return promise.reject("The Performance Stats feature is disabled")
|
42
|
+
end
|
43
|
+
|
44
|
+
@filter_chain.refine(resource)
|
45
|
+
return if resource.ignored?
|
46
|
+
|
47
|
+
@mutex.synchronize do
|
48
|
+
@payload[resource] ||= Airbrake::Stat.new
|
49
|
+
@payload[resource].increment(resource.start_time, resource.end_time)
|
50
|
+
|
51
|
+
if @flush_period > 0
|
52
|
+
schedule_flush(promise)
|
53
|
+
else
|
54
|
+
send(@payload, promise)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
promise
|
59
|
+
end
|
60
|
+
|
61
|
+
# @see Airbrake.add_performance_filter
|
62
|
+
def add_filter(filter = nil, &block)
|
63
|
+
@filter_chain.add_filter(block_given? ? block : filter)
|
64
|
+
end
|
65
|
+
|
66
|
+
# @see Airbrake.delete_performance_filter
|
67
|
+
def delete_filter(filter_class)
|
68
|
+
@filter_chain.delete_filter(filter_class)
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def schedule_flush(promise)
|
74
|
+
@schedule_flush ||= Thread.new do
|
75
|
+
sleep(@flush_period)
|
76
|
+
|
77
|
+
payload = nil
|
78
|
+
@mutex.synchronize do
|
79
|
+
payload = @payload
|
80
|
+
@payload = {}
|
81
|
+
@schedule_flush = nil
|
82
|
+
end
|
83
|
+
|
84
|
+
send(payload, promise)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def send(payload, promise)
|
89
|
+
signature = "#{self.class.name}##{__method__}"
|
90
|
+
raise "#{signature}: payload (#{payload}) cannot be empty. Race?" if payload.none?
|
91
|
+
|
92
|
+
@config.logger.debug("#{LOG_LABEL} #{signature}: #{payload}")
|
93
|
+
|
94
|
+
payload.group_by { |k, _v| k.name }.each do |resource_name, data|
|
95
|
+
@sender.send(
|
96
|
+
{ resource_name => data.map { |k, v| k.to_h.merge!(v.to_h) } },
|
97
|
+
promise,
|
98
|
+
URI.join(
|
99
|
+
@config.host,
|
100
|
+
"api/v5/projects/#{@config.project_id}/#{resource_name}-stats"
|
101
|
+
)
|
102
|
+
)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Request holds request data that powers route stats.
|
108
|
+
#
|
109
|
+
# @see Airbrake.notify_request
|
110
|
+
# @api public
|
111
|
+
# @since v3.2.0
|
112
|
+
Request = Struct.new(:method, :route, :status_code, :start_time, :end_time) do
|
113
|
+
include HashKeyable
|
114
|
+
include Ignorable
|
115
|
+
|
116
|
+
def initialize(method:, route:, status_code:, start_time:, end_time: Time.now)
|
117
|
+
@ignored = false
|
118
|
+
super(method, route, status_code, start_time, end_time)
|
119
|
+
end
|
120
|
+
|
121
|
+
def name
|
122
|
+
'routes'
|
123
|
+
end
|
124
|
+
|
125
|
+
def to_h
|
126
|
+
{
|
127
|
+
'method' => method,
|
128
|
+
'route' => route,
|
129
|
+
'statusCode' => status_code,
|
130
|
+
'time' => TimeTruncate.utc_truncate_minutes(start_time)
|
131
|
+
}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Query holds SQL query data that powers SQL query collection.
|
136
|
+
#
|
137
|
+
# @see Airbrake.notify_query
|
138
|
+
# @api public
|
139
|
+
# @since v3.2.0
|
140
|
+
Query = Struct.new(:method, :route, :query, :start_time, :end_time) do
|
141
|
+
include HashKeyable
|
142
|
+
include Ignorable
|
143
|
+
|
144
|
+
def initialize(method:, route:, query:, start_time:, end_time: Time.now)
|
145
|
+
super(method, route, query, start_time, end_time)
|
146
|
+
end
|
147
|
+
|
148
|
+
def name
|
149
|
+
'queries'
|
150
|
+
end
|
151
|
+
|
152
|
+
def to_h
|
153
|
+
{
|
154
|
+
'method' => method,
|
155
|
+
'route' => route,
|
156
|
+
'query' => query,
|
157
|
+
'time' => TimeTruncate.utc_truncate_minutes(start_time)
|
158
|
+
}
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# Represents a simplified promise object (similar to promises found in
|
3
|
+
# JavaScript), which allows chaining callbacks that are executed when the
|
4
|
+
# promise is either resolved or rejected.
|
5
|
+
#
|
6
|
+
# @see https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
|
7
|
+
# @see https://github.com/ruby-concurrency/concurrent-ruby/blob/master/lib/concurrent/promise.rb
|
8
|
+
# @since v1.7.0
|
9
|
+
class Promise
|
10
|
+
# @api private
|
11
|
+
# @return [Hash<String,String>] either successful response containing the
|
12
|
+
# +id+ key or unsuccessful response containing the +error+ key
|
13
|
+
# @note This is a non-blocking call!
|
14
|
+
attr_reader :value
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@on_resolved = []
|
18
|
+
@on_rejected = []
|
19
|
+
@value = {}
|
20
|
+
@mutex = Mutex.new
|
21
|
+
end
|
22
|
+
|
23
|
+
# Attaches a callback to be executed when the promise is resolved.
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# Airbrake::Promise.new.then { |response| puts response }
|
27
|
+
# #=> {"id"=>"00054415-8201-e9c6-65d6-fc4d231d2871",
|
28
|
+
# # "url"=>"http://localhost/locate/00054415-8201-e9c6-65d6-fc4d231d2871"}
|
29
|
+
#
|
30
|
+
# @yield [response]
|
31
|
+
# @yieldparam response [Hash<String,String>] Contains the `id` & `url` keys
|
32
|
+
# @return [self]
|
33
|
+
def then(&block)
|
34
|
+
@mutex.synchronize do
|
35
|
+
if @value.key?('id')
|
36
|
+
yield(@value)
|
37
|
+
return self
|
38
|
+
end
|
39
|
+
|
40
|
+
@on_resolved << block
|
41
|
+
end
|
42
|
+
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
# Attaches a callback to be executed when the promise is rejected.
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
# Airbrake::Promise.new.rescue { |error| raise error }
|
50
|
+
#
|
51
|
+
# @yield [error] The error message from the API
|
52
|
+
# @yieldparam error [String]
|
53
|
+
# @return [self]
|
54
|
+
def rescue(&block)
|
55
|
+
@mutex.synchronize do
|
56
|
+
if @value.key?('error')
|
57
|
+
yield(@value['error'])
|
58
|
+
return self
|
59
|
+
end
|
60
|
+
|
61
|
+
@on_rejected << block
|
62
|
+
end
|
63
|
+
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
# Resolves the promise.
|
68
|
+
#
|
69
|
+
# @example
|
70
|
+
# Airbrake::Promise.new.resolve('id' => '123')
|
71
|
+
#
|
72
|
+
# @param response [Hash<String,String>]
|
73
|
+
# @return [self]
|
74
|
+
def resolve(response)
|
75
|
+
@mutex.synchronize do
|
76
|
+
@value = response
|
77
|
+
@on_resolved.each { |callback| callback.call(response) }
|
78
|
+
end
|
79
|
+
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
# Rejects the promise.
|
84
|
+
#
|
85
|
+
# @example
|
86
|
+
# Airbrake::Promise.new.reject('Something went wrong')
|
87
|
+
#
|
88
|
+
# @param error [String]
|
89
|
+
# @return [self]
|
90
|
+
def reject(error)
|
91
|
+
@mutex.synchronize do
|
92
|
+
@value['error'] = error
|
93
|
+
@on_rejected.each { |callback| callback.call(error) }
|
94
|
+
end
|
95
|
+
|
96
|
+
self
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# Parses responses coming from the Airbrake API. Handles HTTP errors by
|
3
|
+
# logging them.
|
4
|
+
#
|
5
|
+
# @api private
|
6
|
+
# @since v1.0.0
|
7
|
+
module Response
|
8
|
+
# @return [Integer] the limit of the response body
|
9
|
+
TRUNCATE_LIMIT = 100
|
10
|
+
|
11
|
+
# @return [Integer] HTTP code returned when an IP sends over 10k/min notices
|
12
|
+
TOO_MANY_REQUESTS = 429
|
13
|
+
|
14
|
+
# Parses HTTP responses from the Airbrake API.
|
15
|
+
#
|
16
|
+
# @param [Net::HTTPResponse] response
|
17
|
+
# @param [Logger] logger
|
18
|
+
# @return [Hash{String=>String}] parsed response
|
19
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
20
|
+
def self.parse(response, logger)
|
21
|
+
code = response.code.to_i
|
22
|
+
body = response.body
|
23
|
+
|
24
|
+
begin
|
25
|
+
case code
|
26
|
+
when 200, 204
|
27
|
+
logger.debug("#{LOG_LABEL} #{name} (#{code}): #{body}")
|
28
|
+
{ response.msg => response.body }
|
29
|
+
when 201
|
30
|
+
parsed_body = JSON.parse(body)
|
31
|
+
logger.debug("#{LOG_LABEL} #{name} (#{code}): #{parsed_body}")
|
32
|
+
parsed_body
|
33
|
+
when 400, 401, 403, 420
|
34
|
+
parsed_body = JSON.parse(body)
|
35
|
+
logger.error("#{LOG_LABEL} #{parsed_body['message']}")
|
36
|
+
parsed_body
|
37
|
+
when TOO_MANY_REQUESTS
|
38
|
+
parsed_body = JSON.parse(body)
|
39
|
+
msg = "#{LOG_LABEL} #{parsed_body['message']}"
|
40
|
+
logger.error(msg)
|
41
|
+
{ 'error' => msg, 'rate_limit_reset' => rate_limit_reset(response) }
|
42
|
+
else
|
43
|
+
body_msg = truncated_body(body)
|
44
|
+
logger.error("#{LOG_LABEL} unexpected code (#{code}). Body: #{body_msg}")
|
45
|
+
{ 'error' => body_msg }
|
46
|
+
end
|
47
|
+
rescue StandardError => ex
|
48
|
+
body_msg = truncated_body(body)
|
49
|
+
logger.error("#{LOG_LABEL} error while parsing body (#{ex}). Body: #{body_msg}")
|
50
|
+
{ 'error' => ex.inspect }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
54
|
+
|
55
|
+
def self.truncated_body(body)
|
56
|
+
if body.nil?
|
57
|
+
'[EMPTY_BODY]'.freeze
|
58
|
+
elsif body.length > TRUNCATE_LIMIT
|
59
|
+
body[0..TRUNCATE_LIMIT] << '...'
|
60
|
+
else
|
61
|
+
body
|
62
|
+
end
|
63
|
+
end
|
64
|
+
private_class_method :truncated_body
|
65
|
+
|
66
|
+
def self.rate_limit_reset(response)
|
67
|
+
Time.now + response['X-RateLimit-Delay'].to_i
|
68
|
+
end
|
69
|
+
private_class_method :rate_limit_reset
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Airbrake
|
4
|
+
# Stat is a data structure that allows accumulating performance data (route
|
5
|
+
# performance, SQL query performance and such). It's powered by TDigests.
|
6
|
+
#
|
7
|
+
# Usually, one Stat corresponds to one resource (route or query,
|
8
|
+
# etc.). Incrementing a stat means pushing new performance statistics.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# stat = Airbrake::Stat.new
|
12
|
+
# stat.increment(Time.now - 200)
|
13
|
+
# stat.to_h # Pack and serialize data so it can be transmitted.
|
14
|
+
#
|
15
|
+
# @since v3.2.0
|
16
|
+
Stat = Struct.new(:count, :sum, :sumsq, :tdigest) do
|
17
|
+
# @param [Integer] count How many times this stat was incremented
|
18
|
+
# @param [Float] sum The sum of duration in milliseconds
|
19
|
+
# @param [Float] sumsq The squared sum of duration in milliseconds
|
20
|
+
# @param [TDigest::TDigest] tdigest Packed durations. By default,
|
21
|
+
# compression is 20
|
22
|
+
def initialize(count: 0, sum: 0.0, sumsq: 0.0, tdigest: TDigest.new(0.05))
|
23
|
+
super(count, sum, sumsq, tdigest)
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Hash{String=>Object}] stats as a hash with compressed TDigest
|
27
|
+
# (serialized as base64)
|
28
|
+
def to_h
|
29
|
+
tdigest.compress!
|
30
|
+
{
|
31
|
+
'count' => count,
|
32
|
+
'sum' => sum,
|
33
|
+
'sumsq' => sumsq,
|
34
|
+
'tdigest' => Base64.strict_encode64(tdigest.as_small_bytes)
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
# Increments count and updates performance with the difference of +end_time+
|
39
|
+
# and +start_time+.
|
40
|
+
#
|
41
|
+
# @param [Date] start_time
|
42
|
+
# @param [Date] end_time
|
43
|
+
# @return [void]
|
44
|
+
def increment(start_time, end_time = nil)
|
45
|
+
end_time ||= Time.new
|
46
|
+
|
47
|
+
self.count += 1
|
48
|
+
|
49
|
+
ms = (end_time - start_time) * 1000
|
50
|
+
self.sum += ms
|
51
|
+
self.sumsq += ms * ms
|
52
|
+
|
53
|
+
tdigest.push(ms)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# Responsible for sending data to Airbrake synchronously via PUT or POST
|
3
|
+
# methods. Supports proxies.
|
4
|
+
#
|
5
|
+
# @see AsyncSender
|
6
|
+
# @api private
|
7
|
+
# @since v1.0.0
|
8
|
+
class SyncSender
|
9
|
+
# @return [String] body for HTTP requests
|
10
|
+
CONTENT_TYPE = 'application/json'.freeze
|
11
|
+
|
12
|
+
# @param [Airbrake::Config] config
|
13
|
+
def initialize(config, method = :post)
|
14
|
+
@config = config
|
15
|
+
@method = method
|
16
|
+
@rate_limit_reset = Time.now
|
17
|
+
end
|
18
|
+
|
19
|
+
# Sends a POST or PUT request to the given +endpoint+ with the +data+ payload.
|
20
|
+
#
|
21
|
+
# @param [#to_json] data
|
22
|
+
# @param [URI::HTTPS] endpoint
|
23
|
+
# @return [Hash{String=>String}] the parsed HTTP response
|
24
|
+
def send(data, promise, endpoint = @config.endpoint)
|
25
|
+
return promise if rate_limited_ip?(promise)
|
26
|
+
|
27
|
+
response = nil
|
28
|
+
req = build_request(endpoint, data)
|
29
|
+
|
30
|
+
return promise if missing_body?(req, promise)
|
31
|
+
|
32
|
+
https = build_https(endpoint)
|
33
|
+
|
34
|
+
begin
|
35
|
+
response = https.request(req)
|
36
|
+
rescue StandardError => ex
|
37
|
+
reason = "#{LOG_LABEL} HTTP error: #{ex}"
|
38
|
+
@config.logger.error(reason)
|
39
|
+
return promise.reject(reason)
|
40
|
+
end
|
41
|
+
|
42
|
+
parsed_resp = Response.parse(response, @config.logger)
|
43
|
+
if parsed_resp.key?('rate_limit_reset')
|
44
|
+
@rate_limit_reset = parsed_resp['rate_limit_reset']
|
45
|
+
end
|
46
|
+
|
47
|
+
return promise.reject(parsed_resp['error']) if parsed_resp.key?('error')
|
48
|
+
promise.resolve(parsed_resp)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def build_https(uri)
|
54
|
+
Net::HTTP.new(uri.host, uri.port, *proxy_params).tap do |https|
|
55
|
+
https.use_ssl = uri.is_a?(URI::HTTPS)
|
56
|
+
if @config.timeout
|
57
|
+
https.open_timeout = @config.timeout
|
58
|
+
https.read_timeout = @config.timeout
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def build_request(uri, data)
|
64
|
+
req =
|
65
|
+
if @method == :put
|
66
|
+
Net::HTTP::Put.new(uri.request_uri)
|
67
|
+
else
|
68
|
+
Net::HTTP::Post.new(uri.request_uri)
|
69
|
+
end
|
70
|
+
|
71
|
+
build_request_body(req, data)
|
72
|
+
end
|
73
|
+
|
74
|
+
def build_request_body(req, data)
|
75
|
+
req.body = data.to_json
|
76
|
+
|
77
|
+
req['Authorization'] = "Bearer #{@config.project_key}"
|
78
|
+
req['Content-Type'] = CONTENT_TYPE
|
79
|
+
req['User-Agent'] =
|
80
|
+
"#{Airbrake::Notice::NOTIFIER[:name]}/#{Airbrake::AIRBRAKE_RUBY_VERSION}" \
|
81
|
+
" Ruby/#{RUBY_VERSION}"
|
82
|
+
|
83
|
+
req
|
84
|
+
end
|
85
|
+
|
86
|
+
def proxy_params
|
87
|
+
return unless @config.proxy.key?(:host)
|
88
|
+
|
89
|
+
[@config.proxy[:host], @config.proxy[:port], @config.proxy[:user],
|
90
|
+
@config.proxy[:password]]
|
91
|
+
end
|
92
|
+
|
93
|
+
def rate_limited_ip?(promise)
|
94
|
+
rate_limited = Time.now < @rate_limit_reset
|
95
|
+
promise.reject("#{LOG_LABEL} IP is rate limited") if rate_limited
|
96
|
+
rate_limited
|
97
|
+
end
|
98
|
+
|
99
|
+
def missing_body?(req, promise)
|
100
|
+
missing = req.body.nil?
|
101
|
+
|
102
|
+
if missing
|
103
|
+
reason = "#{LOG_LABEL} data was not sent because of missing body"
|
104
|
+
@config.logger.error(reason)
|
105
|
+
promise.reject(reason)
|
106
|
+
end
|
107
|
+
|
108
|
+
missing
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|