airbrake-ruby 4.8.0 → 4.11.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.rb +101 -25
- data/lib/airbrake-ruby/async_sender.rb +3 -3
- data/lib/airbrake-ruby/backtrace.rb +2 -2
- data/lib/airbrake-ruby/benchmark.rb +1 -1
- data/lib/airbrake-ruby/code_hunk.rb +1 -1
- data/lib/airbrake-ruby/config.rb +1 -1
- data/lib/airbrake-ruby/config/validator.rb +3 -3
- data/lib/airbrake-ruby/deploy_notifier.rb +1 -1
- data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +2 -2
- data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +2 -2
- data/lib/airbrake-ruby/filters/keys_filter.rb +1 -1
- data/lib/airbrake-ruby/filters/sql_filter.rb +3 -3
- data/lib/airbrake-ruby/filters/thread_filter.rb +1 -1
- data/lib/airbrake-ruby/grouppable.rb +12 -0
- data/lib/airbrake-ruby/inspectable.rb +2 -2
- data/lib/airbrake-ruby/mergeable.rb +12 -0
- data/lib/airbrake-ruby/notice.rb +7 -7
- data/lib/airbrake-ruby/notice_notifier.rb +3 -2
- data/lib/airbrake-ruby/performance_breakdown.rb +12 -6
- data/lib/airbrake-ruby/performance_notifier.rb +69 -22
- data/lib/airbrake-ruby/query.rb +15 -11
- data/lib/airbrake-ruby/queue.rb +56 -0
- data/lib/airbrake-ruby/request.rb +14 -12
- data/lib/airbrake-ruby/stat.rb +1 -1
- data/lib/airbrake-ruby/version.rb +1 -1
- data/spec/airbrake_spec.rb +135 -45
- data/spec/async_sender_spec.rb +4 -4
- data/spec/backtrace_spec.rb +18 -18
- data/spec/code_hunk_spec.rb +9 -9
- data/spec/config/validator_spec.rb +5 -5
- data/spec/config_spec.rb +5 -9
- data/spec/deploy_notifier_spec.rb +2 -2
- data/spec/filter_chain_spec.rb +1 -1
- data/spec/filters/dependency_filter_spec.rb +1 -1
- data/spec/filters/gem_root_filter_spec.rb +5 -5
- data/spec/filters/git_last_checkout_filter_spec.rb +1 -1
- data/spec/filters/git_repository_filter.rb +1 -1
- data/spec/filters/git_revision_filter_spec.rb +10 -10
- data/spec/filters/keys_blacklist_spec.rb +22 -22
- data/spec/filters/keys_whitelist_spec.rb +21 -21
- data/spec/filters/root_directory_filter_spec.rb +5 -5
- data/spec/filters/sql_filter_spec.rb +53 -55
- data/spec/filters/system_exit_filter_spec.rb +1 -1
- data/spec/filters/thread_filter_spec.rb +28 -28
- data/spec/fixtures/project_root/code.rb +9 -9
- data/spec/notice_notifier/options_spec.rb +12 -12
- data/spec/notice_notifier_spec.rb +18 -18
- data/spec/notice_spec.rb +5 -5
- data/spec/performance_breakdown_spec.rb +11 -0
- data/spec/performance_notifier_spec.rb +243 -72
- data/spec/query_spec.rb +11 -1
- data/spec/queue_spec.rb +21 -0
- data/spec/request_spec.rb +11 -1
- data/spec/response_spec.rb +8 -8
- data/spec/spec_helper.rb +2 -2
- data/spec/stat_spec.rb +2 -2
- data/spec/sync_sender_spec.rb +12 -12
- data/spec/tdigest_spec.rb +6 -6
- data/spec/thread_pool_spec.rb +5 -5
- data/spec/timed_trace_spec.rb +1 -1
- data/spec/truncator_spec.rb +12 -12
- metadata +7 -2
@@ -21,7 +21,7 @@ module Airbrake
|
|
21
21
|
project_id: @config.project_id,
|
22
22
|
project_key: @config.project_key,
|
23
23
|
host: @config.host,
|
24
|
-
filter_chain: @filter_chain.inspect
|
24
|
+
filter_chain: @filter_chain.inspect,
|
25
25
|
)
|
26
26
|
end
|
27
27
|
|
@@ -30,7 +30,7 @@ module Airbrake
|
|
30
30
|
q.text("#<#{self.class}:0x#{(object_id << 1).to_s(16).rjust(16, '0')} ")
|
31
31
|
q.text(
|
32
32
|
"project_id=\"#{@config.project_id}\" project_key=\"#{@config.project_key}\" " \
|
33
|
-
"host=\"#{@config.host}\" filter_chain="
|
33
|
+
"host=\"#{@config.host}\" filter_chain=",
|
34
34
|
)
|
35
35
|
q.pp(@filter_chain)
|
36
36
|
q.text('>')
|
data/lib/airbrake-ruby/notice.rb
CHANGED
@@ -8,7 +8,7 @@ module Airbrake
|
|
8
8
|
NOTIFIER = {
|
9
9
|
name: 'airbrake-ruby'.freeze,
|
10
10
|
version: Airbrake::AIRBRAKE_RUBY_VERSION,
|
11
|
-
url: 'https://github.com/airbrake/airbrake-ruby'.freeze
|
11
|
+
url: 'https://github.com/airbrake/airbrake-ruby'.freeze,
|
12
12
|
}.freeze
|
13
13
|
|
14
14
|
# @return [Hash{Symbol=>String,Hash}] the information to be displayed in the
|
@@ -16,7 +16,7 @@ module Airbrake
|
|
16
16
|
CONTEXT = {
|
17
17
|
os: RUBY_PLATFORM,
|
18
18
|
language: "#{RUBY_ENGINE}/#{RUBY_VERSION}".freeze,
|
19
|
-
notifier: NOTIFIER
|
19
|
+
notifier: NOTIFIER,
|
20
20
|
}.freeze
|
21
21
|
|
22
22
|
# @return [Integer] the maxium size of the JSON payload in bytes
|
@@ -32,7 +32,7 @@ module Airbrake
|
|
32
32
|
IOError,
|
33
33
|
NotImplementedError,
|
34
34
|
JSON::GeneratorError,
|
35
|
-
Encoding::UndefinedConversionError
|
35
|
+
Encoding::UndefinedConversionError,
|
36
36
|
].freeze
|
37
37
|
|
38
38
|
# @return [Array<Symbol>] the list of keys that can be be overwritten with
|
@@ -60,10 +60,10 @@ module Airbrake
|
|
60
60
|
errors: NestedException.new(exception).as_json,
|
61
61
|
context: context,
|
62
62
|
environment: {
|
63
|
-
program_name: $PROGRAM_NAME
|
63
|
+
program_name: $PROGRAM_NAME,
|
64
64
|
},
|
65
65
|
session: {},
|
66
|
-
params: params
|
66
|
+
params: params,
|
67
67
|
}
|
68
68
|
@truncator = Airbrake::Truncator.new(PAYLOAD_MAX_SIZE)
|
69
69
|
|
@@ -138,7 +138,7 @@ module Airbrake
|
|
138
138
|
# Make sure we always send hostname.
|
139
139
|
hostname: HOSTNAME,
|
140
140
|
|
141
|
-
severity: DEFAULT_SEVERITY
|
141
|
+
severity: DEFAULT_SEVERITY,
|
142
142
|
}.merge(CONTEXT).delete_if { |_key, val| val.nil? || val.empty? }
|
143
143
|
end
|
144
144
|
|
@@ -152,7 +152,7 @@ module Airbrake
|
|
152
152
|
logger.error(
|
153
153
|
"#{LOG_LABEL} truncation failed. File an issue at " \
|
154
154
|
"https://github.com/airbrake/airbrake-ruby " \
|
155
|
-
"and attach the following payload: #{@payload}"
|
155
|
+
"and attach the following payload: #{@payload}",
|
156
156
|
)
|
157
157
|
end
|
158
158
|
|
@@ -55,7 +55,8 @@ module Airbrake
|
|
55
55
|
def build_notice(exception, params = {})
|
56
56
|
if @async_sender.closed?
|
57
57
|
raise Airbrake::Error,
|
58
|
-
"
|
58
|
+
"Airbrake is closed; can't build exception: " \
|
59
|
+
"#{exception.class}: #{exception}"
|
59
60
|
end
|
60
61
|
|
61
62
|
if exception.is_a?(Airbrake::Notice)
|
@@ -116,7 +117,7 @@ module Airbrake
|
|
116
117
|
|
117
118
|
logger.warn(
|
118
119
|
"#{LOG_LABEL} falling back to sync delivery because there are no " \
|
119
|
-
"running async workers"
|
120
|
+
"running async workers",
|
120
121
|
)
|
121
122
|
@sync_sender
|
122
123
|
end
|
@@ -7,22 +7,28 @@ module Airbrake
|
|
7
7
|
# @since v4.2.0
|
8
8
|
# rubocop:disable Metrics/BlockLength, Metrics/ParameterLists
|
9
9
|
PerformanceBreakdown = Struct.new(
|
10
|
-
:method, :route, :response_type, :groups, :start_time, :end_time
|
10
|
+
:method, :route, :response_type, :groups, :start_time, :end_time, :timing,
|
11
|
+
:time
|
11
12
|
) do
|
12
13
|
include HashKeyable
|
13
14
|
include Ignorable
|
14
15
|
include Stashable
|
16
|
+
include Mergeable
|
15
17
|
|
16
18
|
def initialize(
|
17
19
|
method:,
|
18
20
|
route:,
|
19
21
|
response_type:,
|
20
22
|
groups:,
|
21
|
-
start_time
|
22
|
-
end_time:
|
23
|
+
start_time: Time.now,
|
24
|
+
end_time: start_time + 1,
|
25
|
+
timing: nil,
|
26
|
+
time: Time.now
|
23
27
|
)
|
24
|
-
@
|
25
|
-
super(
|
28
|
+
@time_utc = TimeTruncate.utc_truncate_minutes(time)
|
29
|
+
super(
|
30
|
+
method, route, response_type, groups, start_time, end_time, timing, time
|
31
|
+
)
|
26
32
|
end
|
27
33
|
|
28
34
|
def destination
|
@@ -38,7 +44,7 @@ module Airbrake
|
|
38
44
|
'method' => method,
|
39
45
|
'route' => route,
|
40
46
|
'responseType' => response_type,
|
41
|
-
'time' => @
|
47
|
+
'time' => @time_utc,
|
42
48
|
}.delete_if { |_key, val| val.nil? }
|
43
49
|
end
|
44
50
|
end
|
@@ -4,6 +4,7 @@ module Airbrake
|
|
4
4
|
#
|
5
5
|
# @api public
|
6
6
|
# @since v3.2.0
|
7
|
+
# rubocop:disable Metrics/ClassLength
|
7
8
|
class PerformanceNotifier
|
8
9
|
include Inspectable
|
9
10
|
include Loggable
|
@@ -11,7 +12,8 @@ module Airbrake
|
|
11
12
|
def initialize
|
12
13
|
@config = Airbrake::Config.instance
|
13
14
|
@flush_period = Airbrake::Config.instance.performance_stats_flush_period
|
14
|
-
@
|
15
|
+
@async_sender = AsyncSender.new(:put)
|
16
|
+
@sync_sender = SyncSender.new(:put)
|
15
17
|
@payload = {}
|
16
18
|
@schedule_flush = nil
|
17
19
|
@mutex = Mutex.new
|
@@ -23,21 +25,14 @@ module Airbrake
|
|
23
25
|
# @see Airbrake.notify_query
|
24
26
|
# @see Airbrake.notify_request
|
25
27
|
def notify(resource)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
promise = @config.check_performance_options(resource)
|
30
|
-
return promise if promise.rejected?
|
31
|
-
|
32
|
-
@filter_chain.refine(resource)
|
33
|
-
return if resource.ignored?
|
34
|
-
|
35
|
-
@mutex.synchronize do
|
36
|
-
update_payload(resource)
|
37
|
-
@flush_period > 0 ? schedule_flush : send(@payload, promise)
|
38
|
-
end
|
28
|
+
send_resource(resource, sync: false)
|
29
|
+
end
|
39
30
|
|
40
|
-
|
31
|
+
# @param [Hash] resource
|
32
|
+
# @since v4.10.0
|
33
|
+
# @see Airbrake.notify_queue_sync
|
34
|
+
def notify_sync(resource)
|
35
|
+
send_resource(resource, sync: true).value
|
41
36
|
end
|
42
37
|
|
43
38
|
# @see Airbrake.add_performance_filter
|
@@ -53,7 +48,7 @@ module Airbrake
|
|
53
48
|
def close
|
54
49
|
@mutex.synchronize do
|
55
50
|
@schedule_flush.kill if @schedule_flush
|
56
|
-
@
|
51
|
+
@async_sender.close
|
57
52
|
logger.debug("#{LOG_LABEL} performance notifier closed")
|
58
53
|
end
|
59
54
|
end
|
@@ -61,8 +56,13 @@ module Airbrake
|
|
61
56
|
private
|
62
57
|
|
63
58
|
def update_payload(resource)
|
64
|
-
@payload[resource]
|
65
|
-
|
59
|
+
if (total_stat = @payload[resource])
|
60
|
+
@payload.key(total_stat).merge(resource)
|
61
|
+
else
|
62
|
+
@payload[resource] = { total: Airbrake::Stat.new }
|
63
|
+
end
|
64
|
+
|
65
|
+
update_total(resource, @payload[resource][:total])
|
66
66
|
|
67
67
|
resource.groups.each do |name, ms|
|
68
68
|
@payload[resource][name] ||= Airbrake::Stat.new
|
@@ -70,6 +70,19 @@ module Airbrake
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
+
def update_total(resource, total)
|
74
|
+
if resource.timing
|
75
|
+
total.increment_ms(resource.timing)
|
76
|
+
else
|
77
|
+
loc = caller_locations(6..6).first
|
78
|
+
Kernel.warn(
|
79
|
+
"#{loc.path}:#{loc.lineno}: warning: :start_time and :end_time are " \
|
80
|
+
"deprecated. Use :timing & :time instead",
|
81
|
+
)
|
82
|
+
total.increment(resource.start_time, resource.end_time)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
73
86
|
def schedule_flush
|
74
87
|
return if @payload.empty?
|
75
88
|
|
@@ -101,12 +114,45 @@ module Airbrake
|
|
101
114
|
@payload = {}
|
102
115
|
end
|
103
116
|
|
104
|
-
send(payload, Airbrake::Promise.new)
|
117
|
+
send(@async_sender, payload, Airbrake::Promise.new)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def send_resource(resource, sync:)
|
123
|
+
promise = check_configuration(resource)
|
124
|
+
return promise if promise.rejected?
|
125
|
+
|
126
|
+
@filter_chain.refine(resource)
|
127
|
+
if resource.ignored?
|
128
|
+
return Promise.new.reject("#{resource.class} was ignored by a filter")
|
129
|
+
end
|
130
|
+
|
131
|
+
@mutex.synchronize do
|
132
|
+
update_payload(resource)
|
133
|
+
if sync || @flush_period == 0
|
134
|
+
send(@sync_sender, @payload, promise)
|
135
|
+
else
|
136
|
+
schedule_flush
|
105
137
|
end
|
106
138
|
end
|
107
139
|
end
|
108
140
|
|
109
|
-
def
|
141
|
+
def check_configuration(resource)
|
142
|
+
promise = @config.check_configuration
|
143
|
+
return promise if promise.rejected?
|
144
|
+
|
145
|
+
promise = @config.check_performance_options(resource)
|
146
|
+
return promise if promise.rejected?
|
147
|
+
|
148
|
+
if resource.timing && resource.timing == 0
|
149
|
+
return Promise.new.reject(':timing cannot be zero')
|
150
|
+
end
|
151
|
+
|
152
|
+
Promise.new
|
153
|
+
end
|
154
|
+
|
155
|
+
def send(sender, payload, promise)
|
110
156
|
signature = "#{self.class.name}##{__method__}"
|
111
157
|
raise "#{signature}: payload (#{payload}) cannot be empty. Race?" if payload.none?
|
112
158
|
|
@@ -115,9 +161,9 @@ module Airbrake
|
|
115
161
|
with_grouped_payload(payload) do |resource_hash, destination|
|
116
162
|
url = URI.join(
|
117
163
|
@config.host,
|
118
|
-
"api/v5/projects/#{@config.project_id}/#{destination}"
|
164
|
+
"api/v5/projects/#{@config.project_id}/#{destination}",
|
119
165
|
)
|
120
|
-
|
166
|
+
sender.send(resource_hash, promise, url)
|
121
167
|
end
|
122
168
|
|
123
169
|
promise
|
@@ -152,4 +198,5 @@ module Airbrake
|
|
152
198
|
end
|
153
199
|
end
|
154
200
|
end
|
201
|
+
# rubocop:enable Metrics/ClassLength
|
155
202
|
end
|
data/lib/airbrake-ruby/query.rb
CHANGED
@@ -6,11 +6,14 @@ module Airbrake
|
|
6
6
|
# @since v3.2.0
|
7
7
|
# rubocop:disable Metrics/ParameterLists, Metrics/BlockLength
|
8
8
|
Query = Struct.new(
|
9
|
-
:method, :route, :query, :func, :file, :line, :start_time, :end_time
|
9
|
+
:method, :route, :query, :func, :file, :line, :start_time, :end_time,
|
10
|
+
:timing, :time
|
10
11
|
) do
|
11
12
|
include HashKeyable
|
12
13
|
include Ignorable
|
13
14
|
include Stashable
|
15
|
+
include Mergeable
|
16
|
+
include Grouppable
|
14
17
|
|
15
18
|
def initialize(
|
16
19
|
method:,
|
@@ -19,11 +22,16 @@ module Airbrake
|
|
19
22
|
func: nil,
|
20
23
|
file: nil,
|
21
24
|
line: nil,
|
22
|
-
start_time
|
23
|
-
end_time:
|
25
|
+
start_time: Time.now,
|
26
|
+
end_time: start_time + 1,
|
27
|
+
timing: nil,
|
28
|
+
time: Time.now
|
24
29
|
)
|
25
|
-
@
|
26
|
-
super(
|
30
|
+
@time_utc = TimeTruncate.utc_truncate_minutes(time)
|
31
|
+
super(
|
32
|
+
method, route, query, func, file, line, start_time, end_time, timing,
|
33
|
+
time
|
34
|
+
)
|
27
35
|
end
|
28
36
|
|
29
37
|
def destination
|
@@ -34,19 +42,15 @@ module Airbrake
|
|
34
42
|
'queries'
|
35
43
|
end
|
36
44
|
|
37
|
-
def groups
|
38
|
-
{}
|
39
|
-
end
|
40
|
-
|
41
45
|
def to_h
|
42
46
|
{
|
43
47
|
'method' => method,
|
44
48
|
'route' => route,
|
45
49
|
'query' => query,
|
46
|
-
'time' => @
|
50
|
+
'time' => @time_utc,
|
47
51
|
'function' => func,
|
48
52
|
'file' => file,
|
49
|
-
'line' => line
|
53
|
+
'line' => line,
|
50
54
|
}.delete_if { |_key, val| val.nil? }
|
51
55
|
end
|
52
56
|
# rubocop:enable Metrics/ParameterLists, Metrics/BlockLength
|
@@ -0,0 +1,56 @@
|
|
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, Metrics/ParameterLists
|
8
|
+
Queue = Struct.new(
|
9
|
+
:queue, :error_count, :groups, :start_time, :end_time, :timing, :time
|
10
|
+
) do
|
11
|
+
include HashKeyable
|
12
|
+
include Ignorable
|
13
|
+
include Stashable
|
14
|
+
|
15
|
+
def initialize(
|
16
|
+
queue:,
|
17
|
+
error_count:,
|
18
|
+
groups: {},
|
19
|
+
start_time: Time.now,
|
20
|
+
end_time: start_time + 1,
|
21
|
+
timing: nil,
|
22
|
+
time: Time.now
|
23
|
+
)
|
24
|
+
@time_utc = TimeTruncate.utc_truncate_minutes(time)
|
25
|
+
super(queue, error_count, groups, start_time, end_time, timing, time)
|
26
|
+
end
|
27
|
+
|
28
|
+
def destination
|
29
|
+
'queues-stats'
|
30
|
+
end
|
31
|
+
|
32
|
+
def cargo
|
33
|
+
'queues'
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_h
|
37
|
+
{
|
38
|
+
'queue' => queue,
|
39
|
+
'errorCount' => error_count,
|
40
|
+
'time' => @time_utc,
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def hash
|
45
|
+
{
|
46
|
+
'queue' => queue,
|
47
|
+
'time' => @time_utc,
|
48
|
+
}.hash
|
49
|
+
end
|
50
|
+
|
51
|
+
def merge(other)
|
52
|
+
self.error_count += other.error_count
|
53
|
+
end
|
54
|
+
end
|
55
|
+
# rubocop:enable Metrics/BlockLength, Metrics/ParameterLists
|
56
|
+
end
|
@@ -4,21 +4,27 @@ module Airbrake
|
|
4
4
|
# @see Airbrake.notify_request
|
5
5
|
# @api public
|
6
6
|
# @since v3.2.0
|
7
|
-
# rubocop:disable Metrics/BlockLength
|
8
|
-
Request = Struct.new(
|
7
|
+
# rubocop:disable Metrics/BlockLength, Metrics/ParameterLists
|
8
|
+
Request = Struct.new(
|
9
|
+
:method, :route, :status_code, :start_time, :end_time, :timing, :time
|
10
|
+
) do
|
9
11
|
include HashKeyable
|
10
12
|
include Ignorable
|
11
13
|
include Stashable
|
14
|
+
include Mergeable
|
15
|
+
include Grouppable
|
12
16
|
|
13
17
|
def initialize(
|
14
18
|
method:,
|
15
19
|
route:,
|
16
20
|
status_code:,
|
17
|
-
start_time
|
18
|
-
end_time:
|
21
|
+
start_time: Time.now,
|
22
|
+
end_time: start_time + 1,
|
23
|
+
timing: nil,
|
24
|
+
time: Time.now
|
19
25
|
)
|
20
|
-
@
|
21
|
-
super(method, route, status_code, start_time, end_time)
|
26
|
+
@time_utc = TimeTruncate.utc_truncate_minutes(time)
|
27
|
+
super(method, route, status_code, start_time, end_time, timing, time)
|
22
28
|
end
|
23
29
|
|
24
30
|
def destination
|
@@ -29,18 +35,14 @@ module Airbrake
|
|
29
35
|
'routes'
|
30
36
|
end
|
31
37
|
|
32
|
-
def groups
|
33
|
-
{}
|
34
|
-
end
|
35
|
-
|
36
38
|
def to_h
|
37
39
|
{
|
38
40
|
'method' => method,
|
39
41
|
'route' => route,
|
40
42
|
'statusCode' => status_code,
|
41
|
-
'time' => @
|
43
|
+
'time' => @time_utc,
|
42
44
|
}.delete_if { |_key, val| val.nil? }
|
43
45
|
end
|
44
46
|
end
|
45
|
-
# rubocop:enable Metrics/BlockLength
|
47
|
+
# rubocop:enable Metrics/BlockLength, Metrics/ParameterLists
|
46
48
|
end
|