airbrake-ruby 4.8.0 → 4.11.1
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 +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
|