airbrake-ruby 3.2.6 → 4.0.0
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 +31 -138
- data/lib/airbrake-ruby/async_sender.rb +20 -8
- data/lib/airbrake-ruby/backtrace.rb +15 -13
- data/lib/airbrake-ruby/code_hunk.rb +2 -4
- data/lib/airbrake-ruby/config.rb +8 -38
- data/lib/airbrake-ruby/deploy_notifier.rb +4 -17
- data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +5 -4
- data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +6 -4
- data/lib/airbrake-ruby/filters/keys_blacklist.rb +0 -1
- data/lib/airbrake-ruby/filters/keys_filter.rb +4 -4
- data/lib/airbrake-ruby/filters/keys_whitelist.rb +0 -1
- data/lib/airbrake-ruby/loggable.rb +31 -0
- data/lib/airbrake-ruby/nested_exception.rb +2 -3
- data/lib/airbrake-ruby/notice.rb +6 -6
- data/lib/airbrake-ruby/notice_notifier.rb +11 -47
- data/lib/airbrake-ruby/performance_notifier.rb +6 -18
- data/lib/airbrake-ruby/response.rb +5 -2
- data/lib/airbrake-ruby/sync_sender.rb +8 -6
- data/lib/airbrake-ruby/version.rb +1 -1
- data/spec/airbrake_spec.rb +1 -143
- data/spec/async_sender_spec.rb +83 -90
- data/spec/backtrace_spec.rb +36 -47
- data/spec/code_hunk_spec.rb +12 -15
- data/spec/config_spec.rb +79 -96
- data/spec/deploy_notifier_spec.rb +3 -7
- data/spec/filter_chain_spec.rb +1 -3
- data/spec/filters/context_filter_spec.rb +1 -3
- data/spec/filters/dependency_filter_spec.rb +1 -3
- data/spec/filters/exception_attributes_filter_spec.rb +1 -14
- data/spec/filters/gem_root_filter_spec.rb +1 -4
- data/spec/filters/git_last_checkout_filter_spec.rb +3 -5
- data/spec/filters/git_revision_filter_spec.rb +1 -3
- data/spec/filters/keys_blacklist_spec.rb +14 -25
- data/spec/filters/keys_whitelist_spec.rb +14 -25
- data/spec/filters/root_directory_filter_spec.rb +1 -4
- data/spec/filters/system_exit_filter_spec.rb +2 -2
- data/spec/filters/thread_filter_spec.rb +1 -3
- data/spec/nested_exception_spec.rb +3 -5
- data/spec/notice_notifier_spec.rb +23 -20
- data/spec/notice_notifier_spec/options_spec.rb +20 -25
- data/spec/notice_spec.rb +13 -12
- data/spec/performance_notifier_spec.rb +19 -31
- data/spec/response_spec.rb +23 -17
- data/spec/sync_sender_spec.rb +26 -33
- metadata +2 -1
@@ -9,9 +9,7 @@ module Airbrake
|
|
9
9
|
# @return [Integer] how many lines should be read around the base line
|
10
10
|
NLINES = 2
|
11
11
|
|
12
|
-
|
13
|
-
@config = config
|
14
|
-
end
|
12
|
+
include Loggable
|
15
13
|
|
16
14
|
# @param [String] file The file to read
|
17
15
|
# @param [Integer] line The base line in the file
|
@@ -31,7 +29,7 @@ module Airbrake
|
|
31
29
|
def get_from_cache(file)
|
32
30
|
Airbrake::FileCache[file] ||= File.foreach(file)
|
33
31
|
rescue StandardError => ex
|
34
|
-
|
32
|
+
logger.error(
|
35
33
|
"#{self.class.name}: can't read code hunk for #{file}: #{ex}"
|
36
34
|
)
|
37
35
|
nil
|
data/lib/airbrake-ruby/config.rb
CHANGED
@@ -5,6 +5,13 @@ module Airbrake
|
|
5
5
|
# @api public
|
6
6
|
# @since v1.0.0
|
7
7
|
class Config
|
8
|
+
@instance = new
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# @return [Config]
|
12
|
+
attr_accessor :instance
|
13
|
+
end
|
14
|
+
|
8
15
|
# @return [Integer] the project identificator. This value *must* be set.
|
9
16
|
# @api public
|
10
17
|
attr_accessor :project_id
|
@@ -97,7 +104,6 @@ module Airbrake
|
|
97
104
|
|
98
105
|
# @param [Hash{Symbol=>Object}] user_config the hash to be used to build the
|
99
106
|
# config
|
100
|
-
# rubocop:disable Metrics/AbcSize
|
101
107
|
def initialize(user_config = {})
|
102
108
|
@validator = Config::Validator.new(self)
|
103
109
|
|
@@ -105,10 +111,7 @@ module Airbrake
|
|
105
111
|
self.queue_size = 100
|
106
112
|
self.workers = 1
|
107
113
|
self.code_hunks = true
|
108
|
-
|
109
|
-
self.logger = Logger.new(STDOUT)
|
110
|
-
logger.level = Logger::WARN
|
111
|
-
|
114
|
+
self.logger = ::Logger.new(File::NULL)
|
112
115
|
self.project_id = user_config[:project_id]
|
113
116
|
self.project_key = user_config[:project_key]
|
114
117
|
self.host = 'https://api.airbrake.io'
|
@@ -131,7 +134,6 @@ module Airbrake
|
|
131
134
|
|
132
135
|
merge(user_config)
|
133
136
|
end
|
134
|
-
# rubocop:enable Metrics/AbcSize
|
135
137
|
|
136
138
|
# The full URL to the Airbrake Notice API. Based on the +:host+ option.
|
137
139
|
# @return [URI] the endpoint address
|
@@ -195,38 +197,6 @@ module Airbrake
|
|
195
197
|
end
|
196
198
|
end
|
197
199
|
|
198
|
-
def route_stats
|
199
|
-
logger.warn(
|
200
|
-
"#{LOG_LABEL} the 'route_stats' option is deprecated. " \
|
201
|
-
"Use 'performance_stats' instead"
|
202
|
-
)
|
203
|
-
@performance_stats
|
204
|
-
end
|
205
|
-
|
206
|
-
def route_stats=(value)
|
207
|
-
logger.warn(
|
208
|
-
"#{LOG_LABEL} the 'route_stats' option is deprecated. " \
|
209
|
-
"Use 'performance_stats' instead"
|
210
|
-
)
|
211
|
-
@performance_stats = value
|
212
|
-
end
|
213
|
-
|
214
|
-
def route_stats_flush_period
|
215
|
-
logger.warn(
|
216
|
-
"#{LOG_LABEL} the 'route_stats_flush_period' option is deprecated. " \
|
217
|
-
"Use 'performance_stats_flush_period' instead"
|
218
|
-
)
|
219
|
-
@performance_stats_flush_period
|
220
|
-
end
|
221
|
-
|
222
|
-
def route_stats_flush_period=(value)
|
223
|
-
logger.warn(
|
224
|
-
"#{LOG_LABEL} the 'route_stats_flush_period' option is deprecated. " \
|
225
|
-
"Use 'performance_stats' instead"
|
226
|
-
)
|
227
|
-
@performance_stats_flush_period = value
|
228
|
-
end
|
229
|
-
|
230
200
|
private
|
231
201
|
|
232
202
|
def set_option(option, value)
|
@@ -12,25 +12,12 @@ module Airbrake
|
|
12
12
|
class DeployNotifier
|
13
13
|
include Inspectable
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
@
|
18
|
-
if config.is_a?(Config)
|
19
|
-
config
|
20
|
-
else
|
21
|
-
loc = caller_locations(1..1).first
|
22
|
-
signature = "#{self.class.name}##{__method__}"
|
23
|
-
warn(
|
24
|
-
"#{loc.path}:#{loc.lineno}: warning: passing a Hash to #{signature} " \
|
25
|
-
'is deprecated. Pass `Airbrake::Config` instead'
|
26
|
-
)
|
27
|
-
Config.new(config)
|
28
|
-
end
|
29
|
-
|
30
|
-
@sender = SyncSender.new(@config)
|
15
|
+
def initialize
|
16
|
+
@config = Airbrake::Config.instance
|
17
|
+
@sender = SyncSender.new
|
31
18
|
end
|
32
19
|
|
33
|
-
# @see Airbrake.
|
20
|
+
# @see Airbrake.notify_deploy
|
34
21
|
def notify(deploy_info, promise = Airbrake::Promise.new)
|
35
22
|
if @config.ignored_environment?
|
36
23
|
return promise.reject("The '#{@config.environment}' environment is ignored")
|
@@ -6,8 +6,9 @@ module Airbrake
|
|
6
6
|
# @api private
|
7
7
|
# @since v2.10.0
|
8
8
|
class ExceptionAttributesFilter
|
9
|
-
|
10
|
-
|
9
|
+
include Loggable
|
10
|
+
|
11
|
+
def initialize
|
11
12
|
@weight = 118
|
12
13
|
end
|
13
14
|
|
@@ -20,13 +21,13 @@ module Airbrake
|
|
20
21
|
begin
|
21
22
|
attributes = exception.to_airbrake
|
22
23
|
rescue StandardError => ex
|
23
|
-
|
24
|
+
logger.error(
|
24
25
|
"#{LOG_LABEL} #{exception.class}#to_airbrake failed. #{ex.class}: #{ex}"
|
25
26
|
)
|
26
27
|
end
|
27
28
|
|
28
29
|
unless attributes.is_a?(Hash)
|
29
|
-
|
30
|
+
logger.error(
|
30
31
|
"#{LOG_LABEL} #{self.class}: wanted Hash, got #{attributes.class}"
|
31
32
|
)
|
32
33
|
return
|
@@ -20,11 +20,11 @@ module Airbrake
|
|
20
20
|
# file (checkout information is omitted)
|
21
21
|
MIN_HEAD_COLS = 6
|
22
22
|
|
23
|
-
|
23
|
+
include Loggable
|
24
|
+
|
24
25
|
# @param [String] root_directory
|
25
|
-
def initialize(
|
26
|
+
def initialize(root_directory)
|
26
27
|
@git_path = File.join(root_directory, '.git')
|
27
|
-
@logger = logger
|
28
28
|
@weight = 116
|
29
29
|
@last_checkout = nil
|
30
30
|
end
|
@@ -45,12 +45,13 @@ module Airbrake
|
|
45
45
|
|
46
46
|
private
|
47
47
|
|
48
|
+
# rubocop:disable Metrics/AbcSize
|
48
49
|
def last_checkout
|
49
50
|
return unless (line = last_checkout_line)
|
50
51
|
|
51
52
|
parts = line.chomp.split("\t").first.split(' ')
|
52
53
|
if parts.size < MIN_HEAD_COLS
|
53
|
-
|
54
|
+
logger.error(
|
54
55
|
"#{LOG_LABEL} Airbrake::#{self.class.name}: can't parse line: #{line}"
|
55
56
|
)
|
56
57
|
return
|
@@ -64,6 +65,7 @@ module Airbrake
|
|
64
65
|
time: timestamp(parts[-2].to_i)
|
65
66
|
}
|
66
67
|
end
|
68
|
+
# rubocop:enable Metrics/AbcSize
|
67
69
|
|
68
70
|
def last_checkout_line
|
69
71
|
head_path = File.join(@git_path, 'logs', 'HEAD')
|
@@ -26,16 +26,16 @@ module Airbrake
|
|
26
26
|
# be modified by blacklist/whitelist filters
|
27
27
|
FILTERABLE_CONTEXT_KEYS = %i[user headers].freeze
|
28
28
|
|
29
|
+
include Loggable
|
30
|
+
|
29
31
|
# @return [Integer]
|
30
32
|
attr_reader :weight
|
31
33
|
|
32
34
|
# Creates a new KeysBlacklist or KeysWhitelist filter that uses the given
|
33
35
|
# +patterns+ for filtering a notice's payload.
|
34
36
|
#
|
35
|
-
# @param [Logger, #error] logger
|
36
37
|
# @param [Array<String,Regexp,Symbol>] patterns
|
37
|
-
def initialize(
|
38
|
-
@logger = logger
|
38
|
+
def initialize(patterns)
|
39
39
|
@patterns = patterns
|
40
40
|
@valid_patterns = false
|
41
41
|
end
|
@@ -122,7 +122,7 @@ module Airbrake
|
|
122
122
|
|
123
123
|
return if @valid_patterns
|
124
124
|
|
125
|
-
|
125
|
+
logger.error(
|
126
126
|
"#{LOG_LABEL} one of the patterns in #{self.class} is invalid. " \
|
127
127
|
"Known patterns: #{@patterns}"
|
128
128
|
)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# Loggable is included into any class that wants to be able to log.
|
3
|
+
#
|
4
|
+
# By default, Loggable defines a null logger that doesn't do anything. You are
|
5
|
+
# supposed to overwrite it via the {instance} method before calling {logger}.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# class A
|
9
|
+
# include Loggable
|
10
|
+
#
|
11
|
+
# def initialize
|
12
|
+
# logger.debug('Initialized A')
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# @since v4.0.0
|
17
|
+
# @api private
|
18
|
+
module Loggable
|
19
|
+
@instance = ::Logger.new(File::NULL)
|
20
|
+
|
21
|
+
class << self
|
22
|
+
# @return [Logger]
|
23
|
+
attr_accessor :instance
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Logger] standard Ruby logger object
|
27
|
+
def logger
|
28
|
+
Loggable.instance
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -9,8 +9,7 @@ module Airbrake
|
|
9
9
|
# can unwrap. Exceptions that have a longer cause chain will be ignored
|
10
10
|
MAX_NESTED_EXCEPTIONS = 3
|
11
11
|
|
12
|
-
def initialize(
|
13
|
-
@config = config
|
12
|
+
def initialize(exception)
|
14
13
|
@exception = exception
|
15
14
|
end
|
16
15
|
|
@@ -18,7 +17,7 @@ module Airbrake
|
|
18
17
|
unwind_exceptions.map do |exception|
|
19
18
|
{ type: exception.class.name,
|
20
19
|
message: exception.message,
|
21
|
-
backtrace: Backtrace.parse(
|
20
|
+
backtrace: Backtrace.parse(exception) }
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
data/lib/airbrake-ruby/notice.rb
CHANGED
@@ -50,6 +50,7 @@ module Airbrake
|
|
50
50
|
DEFAULT_SEVERITY = 'error'.freeze
|
51
51
|
|
52
52
|
include Ignorable
|
53
|
+
include Loggable
|
53
54
|
|
54
55
|
# @since v1.7.0
|
55
56
|
# @return [Hash{Symbol=>Object}] the hash with arbitrary objects to be used
|
@@ -57,11 +58,10 @@ module Airbrake
|
|
57
58
|
attr_reader :stash
|
58
59
|
|
59
60
|
# @api private
|
60
|
-
def initialize(
|
61
|
-
@config =
|
62
|
-
|
61
|
+
def initialize(exception, params = {})
|
62
|
+
@config = Airbrake::Config.instance
|
63
63
|
@payload = {
|
64
|
-
errors: NestedException.new(
|
64
|
+
errors: NestedException.new(exception).as_json,
|
65
65
|
context: context,
|
66
66
|
environment: {
|
67
67
|
program_name: $PROGRAM_NAME
|
@@ -84,7 +84,7 @@ module Airbrake
|
|
84
84
|
begin
|
85
85
|
json = @payload.to_json
|
86
86
|
rescue *JSON_EXCEPTIONS => ex
|
87
|
-
|
87
|
+
logger.debug("#{LOG_LABEL} `notice.to_json` failed: #{ex.class}: #{ex}")
|
88
88
|
else
|
89
89
|
return json if json && json.bytesize <= MAX_NOTICE_SIZE
|
90
90
|
end
|
@@ -152,7 +152,7 @@ module Airbrake
|
|
152
152
|
|
153
153
|
new_max_size = @truncator.reduce_max_size
|
154
154
|
if new_max_size == 0
|
155
|
-
|
155
|
+
logger.error(
|
156
156
|
"#{LOG_LABEL} truncation failed. File an issue at " \
|
157
157
|
"https://github.com/airbrake/airbrake-ruby " \
|
158
158
|
"and attach the following payload: #{@payload}"
|
@@ -5,7 +5,6 @@ module Airbrake
|
|
5
5
|
# @see Airbrake::Config The list of options
|
6
6
|
# @since v1.0.0
|
7
7
|
# @api public
|
8
|
-
# rubocop:disable Metrics/ClassLength
|
9
8
|
class NoticeNotifier
|
10
9
|
# @return [Array<Class>] filters to be executed first
|
11
10
|
DEFAULT_FILTERS = [
|
@@ -17,35 +16,14 @@ module Airbrake
|
|
17
16
|
].freeze
|
18
17
|
|
19
18
|
include Inspectable
|
19
|
+
include Loggable
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
# @example
|
24
|
-
# config = Airbrake::Config.new
|
25
|
-
# config.project_id = 123
|
26
|
-
# config.project_key = '321'
|
27
|
-
# notice_notifier = Airbrake::NoticeNotifier.new(config)
|
28
|
-
#
|
29
|
-
# @param [Airbrake::Config] config
|
30
|
-
def initialize(config, perf_notifier = nil)
|
31
|
-
@config =
|
32
|
-
if config.is_a?(Config)
|
33
|
-
config
|
34
|
-
else
|
35
|
-
loc = caller_locations(1..1).first
|
36
|
-
signature = "#{self.class.name}##{__method__}"
|
37
|
-
warn(
|
38
|
-
"#{loc.path}:#{loc.lineno}: warning: passing a Hash to #{signature} " \
|
39
|
-
'is deprecated. Pass `Airbrake::Config` instead'
|
40
|
-
)
|
41
|
-
Config.new(config)
|
42
|
-
end
|
43
|
-
|
21
|
+
def initialize
|
22
|
+
@config = Airbrake::Config.instance
|
44
23
|
@context = {}
|
45
24
|
@filter_chain = FilterChain.new
|
46
|
-
@async_sender = AsyncSender.new
|
47
|
-
@sync_sender = SyncSender.new
|
48
|
-
@perf_notifier = perf_notifier
|
25
|
+
@async_sender = AsyncSender.new
|
26
|
+
@sync_sender = SyncSender.new
|
49
27
|
|
50
28
|
add_default_filters
|
51
29
|
end
|
@@ -60,15 +38,6 @@ module Airbrake
|
|
60
38
|
send_notice(exception, params, @sync_sender, &block).value
|
61
39
|
end
|
62
40
|
|
63
|
-
# @deprecated Update the airbrake gem to v8.1.0 or higher
|
64
|
-
def notify_request(request_info, promise = Promise.new)
|
65
|
-
@config.logger.info(
|
66
|
-
"#{LOG_LABEL} #{self.class}##{__method__} is deprecated. Update " \
|
67
|
-
'the airbrake gem to v8.1.0 or higher'
|
68
|
-
)
|
69
|
-
@perf_notifier.notify(Request.new(request_info), promise)
|
70
|
-
end
|
71
|
-
|
72
41
|
# @macro see_public_api_method
|
73
42
|
def add_filter(filter = nil, &block)
|
74
43
|
@filter_chain.add_filter(block_given? ? block : filter)
|
@@ -90,7 +59,7 @@ module Airbrake
|
|
90
59
|
exception[:params].merge!(params)
|
91
60
|
exception
|
92
61
|
else
|
93
|
-
Notice.new(
|
62
|
+
Notice.new(convert_to_exception(exception), params.dup)
|
94
63
|
end
|
95
64
|
end
|
96
65
|
|
@@ -143,7 +112,7 @@ module Airbrake
|
|
143
112
|
def default_sender
|
144
113
|
return @async_sender if @async_sender.has_workers?
|
145
114
|
|
146
|
-
|
115
|
+
logger.warn(
|
147
116
|
"#{LOG_LABEL} falling back to sync delivery because there are no " \
|
148
117
|
"running async workers"
|
149
118
|
)
|
@@ -165,19 +134,15 @@ module Airbrake
|
|
165
134
|
DEFAULT_FILTERS.each { |f| add_filter(f.new) }
|
166
135
|
|
167
136
|
if (whitelist_keys = @config.whitelist_keys).any?
|
168
|
-
add_filter(
|
169
|
-
Airbrake::Filters::KeysWhitelist.new(@config.logger, whitelist_keys)
|
170
|
-
)
|
137
|
+
add_filter(Airbrake::Filters::KeysWhitelist.new(whitelist_keys))
|
171
138
|
end
|
172
139
|
|
173
140
|
if (blacklist_keys = @config.blacklist_keys).any?
|
174
|
-
add_filter(
|
175
|
-
Airbrake::Filters::KeysBlacklist.new(@config.logger, blacklist_keys)
|
176
|
-
)
|
141
|
+
add_filter(Airbrake::Filters::KeysBlacklist.new(blacklist_keys))
|
177
142
|
end
|
178
143
|
|
179
144
|
add_filter(Airbrake::Filters::ContextFilter.new(@context))
|
180
|
-
add_filter(Airbrake::Filters::ExceptionAttributesFilter.new
|
145
|
+
add_filter(Airbrake::Filters::ExceptionAttributesFilter.new)
|
181
146
|
|
182
147
|
return unless (root_directory = @config.root_directory)
|
183
148
|
[
|
@@ -189,10 +154,9 @@ module Airbrake
|
|
189
154
|
end
|
190
155
|
|
191
156
|
add_filter(
|
192
|
-
Airbrake::Filters::GitLastCheckoutFilter.new(
|
157
|
+
Airbrake::Filters::GitLastCheckoutFilter.new(root_directory)
|
193
158
|
)
|
194
159
|
end
|
195
160
|
# rubocop:enable Metrics/AbcSize
|
196
161
|
end
|
197
|
-
# rubocop:enable Metrics/ClassLength
|
198
162
|
end
|