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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby.rb +31 -138
  3. data/lib/airbrake-ruby/async_sender.rb +20 -8
  4. data/lib/airbrake-ruby/backtrace.rb +15 -13
  5. data/lib/airbrake-ruby/code_hunk.rb +2 -4
  6. data/lib/airbrake-ruby/config.rb +8 -38
  7. data/lib/airbrake-ruby/deploy_notifier.rb +4 -17
  8. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +5 -4
  9. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +6 -4
  10. data/lib/airbrake-ruby/filters/keys_blacklist.rb +0 -1
  11. data/lib/airbrake-ruby/filters/keys_filter.rb +4 -4
  12. data/lib/airbrake-ruby/filters/keys_whitelist.rb +0 -1
  13. data/lib/airbrake-ruby/loggable.rb +31 -0
  14. data/lib/airbrake-ruby/nested_exception.rb +2 -3
  15. data/lib/airbrake-ruby/notice.rb +6 -6
  16. data/lib/airbrake-ruby/notice_notifier.rb +11 -47
  17. data/lib/airbrake-ruby/performance_notifier.rb +6 -18
  18. data/lib/airbrake-ruby/response.rb +5 -2
  19. data/lib/airbrake-ruby/sync_sender.rb +8 -6
  20. data/lib/airbrake-ruby/version.rb +1 -1
  21. data/spec/airbrake_spec.rb +1 -143
  22. data/spec/async_sender_spec.rb +83 -90
  23. data/spec/backtrace_spec.rb +36 -47
  24. data/spec/code_hunk_spec.rb +12 -15
  25. data/spec/config_spec.rb +79 -96
  26. data/spec/deploy_notifier_spec.rb +3 -7
  27. data/spec/filter_chain_spec.rb +1 -3
  28. data/spec/filters/context_filter_spec.rb +1 -3
  29. data/spec/filters/dependency_filter_spec.rb +1 -3
  30. data/spec/filters/exception_attributes_filter_spec.rb +1 -14
  31. data/spec/filters/gem_root_filter_spec.rb +1 -4
  32. data/spec/filters/git_last_checkout_filter_spec.rb +3 -5
  33. data/spec/filters/git_revision_filter_spec.rb +1 -3
  34. data/spec/filters/keys_blacklist_spec.rb +14 -25
  35. data/spec/filters/keys_whitelist_spec.rb +14 -25
  36. data/spec/filters/root_directory_filter_spec.rb +1 -4
  37. data/spec/filters/system_exit_filter_spec.rb +2 -2
  38. data/spec/filters/thread_filter_spec.rb +1 -3
  39. data/spec/nested_exception_spec.rb +3 -5
  40. data/spec/notice_notifier_spec.rb +23 -20
  41. data/spec/notice_notifier_spec/options_spec.rb +20 -25
  42. data/spec/notice_spec.rb +13 -12
  43. data/spec/performance_notifier_spec.rb +19 -31
  44. data/spec/response_spec.rb +23 -17
  45. data/spec/sync_sender_spec.rb +26 -33
  46. metadata +2 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b8b74bf6222301b0773cbae98656cae5e7434dfe
4
- data.tar.gz: f0c09f8d665390398e8226e624d5fb407927ab09
3
+ metadata.gz: f02fa6e61f0c293426ab4b4c3347106dadba90a8
4
+ data.tar.gz: aab5dbba3ec59829fb95b4b3d6a24c67a38023d7
5
5
  SHA512:
6
- metadata.gz: b075a5d9a1d6b6d3d28fe5345140c0c5397a34e49e89d52cbd9ab4b99d9d4fc57e12641cbd354028b579263f8fa69a82e15f15938559f8e905f553d0092ce972
7
- data.tar.gz: 380596bb7dc0836185ac4a812fd309d5c3c5913b72177e4df660c620d9ae29520c9659bdcb01ccb52879e4946e37687231205b72a6191dbd1c177f2b3ad591aa
6
+ metadata.gz: 5d4c7e5134c8fb761a469bc082518b920b0de8a6e1845b3e1b574a71bc33fab56e1e51235434a65d2e68b1069e78e397a2438b0b48e885c33afba503962a929a
7
+ data.tar.gz: 01d60cea9501d88ca04b45c57a19d568e42a3bfb07e09ffbe23fb9a67e1dab02fc60c2bd1ab564aad164a40e28635a6fb47ec2d1baf5c8c2e13c4196e64eba99
data/lib/airbrake-ruby.rb CHANGED
@@ -7,6 +7,7 @@ require 'socket'
7
7
  require 'time'
8
8
 
9
9
  require 'airbrake-ruby/version'
10
+ require 'airbrake-ruby/loggable'
10
11
  require 'airbrake-ruby/config'
11
12
  require 'airbrake-ruby/config/validator'
12
13
  require 'airbrake-ruby/promise'
@@ -46,40 +47,22 @@ require 'airbrake-ruby/tdigest'
46
47
  require 'airbrake-ruby/query'
47
48
  require 'airbrake-ruby/request'
48
49
 
49
- # This module defines the Airbrake API. The user is meant to interact with
50
- # Airbrake via its public class methods. Before using the library, you must to
51
- # {configure} the default notifier.
50
+ # Airbrake is a thin wrapper around instances of the notifier classes (such as
51
+ # notice, performance & deploy notifiers). It creates a way to access them via a
52
+ # consolidated global interface.
52
53
  #
53
- # The module supports multiple notifiers, each of which can be configured
54
- # differently. By default, every method is invoked in context of the default
55
- # notifier. To use a different notifier, you need to {configure} it first and
56
- # pass the notifier's name as the last argument of the method you're calling.
54
+ # Prior to using it, you must {configure} it.
57
55
  #
58
- # You can have as many notifiers as you want, but they must have unique names.
59
- #
60
- # @example Configuring multiple notifiers and using them
61
- # # Configure the default notifier.
56
+ # @example
62
57
  # Airbrake.configure do |c|
63
58
  # c.project_id = 113743
64
59
  # c.project_key = 'fd04e13d806a90f96614ad8e529b2822'
65
60
  # end
66
61
  #
67
- # # Configure a named notifier.
68
- # Airbrake.configure(:my_other_project) do |c|
69
- # c.project_id = 224854
70
- # c.project_key = '91ac5e4a37496026c6837f63276ed2b6'
71
- # end
72
- #
73
- # # Send an exception via the default notifier.
74
62
  # Airbrake.notify('Oops!')
75
63
  #
76
- # # Send an exception via other configured notifier.
77
- # params = {}
78
- # Airbrake[:my_other_project].notify('Oops', params)
79
- #
80
- # @see Airbrake::NoticeNotifier
81
64
  # @since v1.0.0
82
- # rubocop:disable Metrics/ModuleLength
65
+ # @api public
83
66
  module Airbrake
84
67
  # The general error that this library uses when it wants to raise.
85
68
  Error = Class.new(StandardError)
@@ -127,14 +110,6 @@ module Airbrake
127
110
  def merge_context(_context); end
128
111
  end
129
112
 
130
- # @deprecated Use {Airbrake::NoticeNotifier} instead
131
- Notifier = NoticeNotifier
132
- deprecate_constant(:Notifier) if respond_to?(:deprecate_constant)
133
-
134
- # @deprecated Use {Airbrake::NilNoticeNotifier} instead
135
- NilNotifier = NilNoticeNotifier
136
- deprecate_constant(:NilNotifier) if respond_to?(:deprecate_constant)
137
-
138
113
  # NilPerformanceNotifier is a no-op notifier, which mimics
139
114
  # {Airbrake::PerformanceNotifier} and serves only the purpose of making the
140
115
  # library API easier to use.
@@ -160,75 +135,23 @@ module Airbrake
160
135
  #
161
136
  # @since v3.2.0
162
137
  class NilDeployNotifier
163
- # @see Airbrake.create_deploy
138
+ # @see Airbrake.notify_deploy
164
139
  def notify(_deploy_info); end
165
140
  end
166
141
 
167
- # A Hash that holds all notice notifiers. The keys of the Hash are notifier
168
- # names, the values are {Airbrake::NoticeNotifier} instances. If a notifier is
169
- # not assigned to the hash, then it returns a null object (NilNoticeNotifier).
170
- @notice_notifiers = Hash.new(NilNoticeNotifier.new)
171
-
172
- # A Hash that holds all performance notifiers. The keys of the Hash are
173
- # notifier names, the values are {Airbrake::PerformanceNotifier} instances. If
174
- # a notifier is not assigned to the hash, then it returns a null object
175
- # (NilPerformanceNotifier).
176
- @performance_notifiers = Hash.new(NilPerformanceNotifier.new)
177
-
178
- # A Hash that holds all deploy notifiers. The keys of the Hash are notifier
179
- # names, the values are {Airbrake::DeployNotifier} instances. If a deploy
180
- # notifier is not assigned to the hash, then it returns a null object
181
- # (NilDeployNotifier).
182
- @deploy_notifiers = Hash.new(NilDeployNotifier.new)
142
+ @notice_notifier = NilNoticeNotifier.new
143
+ @performance_notifier = NilPerformanceNotifier.new
144
+ @deploy_notifier = NilDeployNotifier.new
183
145
 
184
146
  class << self
185
- # Retrieves configured notifiers.
186
- #
187
- # @example
188
- # Airbrake[:my_notifier].notify('oops')
189
- #
190
- # @param [Symbol] notifier_name the name of the notice notifier you want to
191
- # use
192
- # @return [Airbrake::NoticeNotifier, NilClass]
193
- # @since v1.8.0
194
- def [](notifier_name)
195
- loc = caller_locations(1..1).first
196
- signature = "#{self}##{__method__}"
197
- warn(
198
- "#{loc.path}:#{loc.lineno}: warning: #{signature} is deprecated. It " \
199
- "will be removed from airbrake-ruby v4 altogether."
200
- )
201
-
202
- @notice_notifiers[notifier_name]
203
- end
204
-
205
- # @return [Hash{Symbol=>Array<Object>}] a Hash with all configured notifiers
206
- # (notice, performance, deploy)
207
- # @since v3.2.0
208
- def notifiers
209
- loc = caller_locations(1..1).first
210
- signature = "#{self}##{__method__}"
211
- warn(
212
- "#{loc.path}:#{loc.lineno}: warning: #{signature} is deprecated. It " \
213
- "will be removed from airbrake-ruby v4 altogether."
214
- )
215
-
216
- {
217
- notice: @notice_notifiers,
218
- performance: @performance_notifiers,
219
- deploy: @deploy_notifiers
220
- }
221
- end
222
-
223
147
  # Configures the Airbrake notifier.
224
148
  #
225
- # @example Configuring the default notifier
149
+ # @example
226
150
  # Airbrake.configure do |c|
227
151
  # c.project_id = 113743
228
152
  # c.project_key = 'fd04e13d806a90f96614ad8e529b2822'
229
153
  # end
230
154
  #
231
- # @param [Symbol] notifier_name the name to be associated with the notifier
232
155
  # @yield [config] The configuration object
233
156
  # @yieldparam config [Airbrake::Config]
234
157
  # @return [void]
@@ -236,41 +159,23 @@ module Airbrake
236
159
  # existing notifier
237
160
  # @raise [Airbrake::Error] when either +project_id+ or +project_key+
238
161
  # is missing (or both)
239
- # @note There's no way to reconfigure a notifier
240
162
  # @note There's no way to read config values outside of this library
241
- def configure(notifier_name = :default)
242
- unless notifier_name == :default
243
- loc = caller_locations(1..1).first
244
- warn(
245
- "#{loc.path}:#{loc.lineno}: warning: configuring a notifier with a " \
246
- "custom name is deprecated. This feature will be removed from " \
247
- "airbrake-ruby v4 altogether."
248
- )
249
- end
250
-
251
- yield config = Airbrake::Config.new
252
-
253
- if @notice_notifiers.key?(notifier_name)
254
- raise Airbrake::Error,
255
- "the '#{notifier_name}' notifier was already configured"
256
- end
163
+ def configure
164
+ yield config = Airbrake::Config.instance
257
165
 
258
166
  raise Airbrake::Error, config.validation_error_message unless config.valid?
259
167
 
260
- # TODO: Kludge to avoid
261
- # https://github.com/airbrake/airbrake-ruby/issues/406
262
- # Stop passing perf_notifier to NoticeNotifier as soon as possible.
263
- perf_notifier = PerformanceNotifier.new(config)
264
- @performance_notifiers[notifier_name] = perf_notifier
265
- @notice_notifiers[notifier_name] = NoticeNotifier.new(config, perf_notifier)
168
+ Airbrake::Loggable.instance = Airbrake::Config.instance
266
169
 
267
- @deploy_notifiers[notifier_name] = DeployNotifier.new(config)
170
+ @performance_notifier = PerformanceNotifier.new
171
+ @notice_notifier = NoticeNotifier.new
172
+ @deploy_notifier = DeployNotifier.new
268
173
  end
269
174
 
270
175
  # @return [Boolean] true if the notifier was configured, false otherwise
271
176
  # @since v2.3.0
272
177
  def configured?
273
- @notice_notifiers[:default].configured?
178
+ @notice_notifier.configured?
274
179
  end
275
180
 
276
181
  # Sends an exception to Airbrake asynchronously.
@@ -295,7 +200,7 @@ module Airbrake
295
200
  # @return [Airbrake::Promise]
296
201
  # @see .notify_sync
297
202
  def notify(exception, params = {}, &block)
298
- @notice_notifiers[:default].notify(exception, params, &block)
203
+ @notice_notifier.notify(exception, params, &block)
299
204
  end
300
205
 
301
206
  # Sends an exception to Airbrake synchronously.
@@ -315,7 +220,7 @@ module Airbrake
315
220
  # @return [Hash{String=>String}] the reponse from the server
316
221
  # @see .notify
317
222
  def notify_sync(exception, params = {}, &block)
318
- @notice_notifiers[:default].notify_sync(exception, params, &block)
223
+ @notice_notifier.notify_sync(exception, params, &block)
319
224
  end
320
225
 
321
226
  # Runs a callback before {.notify} or {.notify_sync} kicks in. This is
@@ -343,7 +248,7 @@ module Airbrake
343
248
  # @yieldreturn [void]
344
249
  # @return [void]
345
250
  def add_filter(filter = nil, &block)
346
- @notice_notifiers[:default].add_filter(filter, &block)
251
+ @notice_notifier.add_filter(filter, &block)
347
252
  end
348
253
 
349
254
  # Deletes a filter added via {Airbrake#add_filter}.
@@ -360,7 +265,7 @@ module Airbrake
360
265
  # @since v3.1.0
361
266
  # @note This method cannot delete filters assigned via the Proc form.
362
267
  def delete_filter(filter_class)
363
- @notice_notifiers[:default].delete_filter(filter_class)
268
+ @notice_notifier.delete_filter(filter_class)
364
269
  end
365
270
 
366
271
  # Builds an Airbrake notice. This is useful, if you want to add or modify a
@@ -378,7 +283,7 @@ module Airbrake
378
283
  # @return [Airbrake::Notice] the notice built with help of the given
379
284
  # arguments
380
285
  def build_notice(exception, params = {})
381
- @notice_notifiers[:default].build_notice(exception, params)
286
+ @notice_notifier.build_notice(exception, params)
382
287
  end
383
288
 
384
289
  # Makes the notice notifier a no-op, which means you cannot use the
@@ -391,7 +296,7 @@ module Airbrake
391
296
  #
392
297
  # @return [void]
393
298
  def close
394
- @notice_notifiers[:default].close
299
+ @notice_notifier.close
395
300
  end
396
301
 
397
302
  # Pings the Airbrake Deploy API endpoint about the occurred deploy.
@@ -404,18 +309,7 @@ module Airbrake
404
309
  # @option deploy_info [Symbol] :version
405
310
  # @return [void]
406
311
  def notify_deploy(deploy_info)
407
- @deploy_notifiers[:default].notify(deploy_info)
408
- end
409
-
410
- # @see notify_deploy
411
- def create_deploy(deploy_info)
412
- loc = caller_locations(1..1).first
413
- signature = "#{self}##{__method__}"
414
- warn(
415
- "#{loc.path}:#{loc.lineno}: warning: #{signature} is deprecated. Call " \
416
- "#{self}#notify_deploy instead"
417
- )
418
- notify_deploy(deploy_info)
312
+ @deploy_notifier.notify(deploy_info)
419
313
  end
420
314
 
421
315
  # Merges +context+ with the current context.
@@ -463,7 +357,7 @@ module Airbrake
463
357
  # @param [Hash{Symbol=>Object}] context
464
358
  # @return [void]
465
359
  def merge_context(context)
466
- @notice_notifiers[:default].merge_context(context)
360
+ @notice_notifier.merge_context(context)
467
361
  end
468
362
 
469
363
  # Increments request statistics of a certain +route+ that was invoked on
@@ -502,7 +396,7 @@ module Airbrake
502
396
  # @since v3.0.0
503
397
  # @see Airbrake::PerformanceNotifier#notify
504
398
  def notify_request(request_info)
505
- @performance_notifiers[:default].notify(Request.new(request_info))
399
+ @performance_notifier.notify(Request.new(request_info))
506
400
  end
507
401
 
508
402
  # Increments SQL statistics of a certain +query+ that was invoked on
@@ -534,7 +428,7 @@ module Airbrake
534
428
  # @since v3.2.0
535
429
  # @see Airbrake::PerformanceNotifier#notify
536
430
  def notify_query(query_info)
537
- @performance_notifiers[:default].notify(Query.new(query_info))
431
+ @performance_notifier.notify(Query.new(query_info))
538
432
  end
539
433
 
540
434
  # Runs a callback before {.notify_request} or {.notify_query} kicks in. This
@@ -569,7 +463,7 @@ module Airbrake
569
463
  # @since v3.2.0
570
464
  # @see Airbrake::PerformanceNotifier#add_filter
571
465
  def add_performance_filter(filter = nil, &block)
572
- @performance_notifiers[:default].add_filter(filter, &block)
466
+ @performance_notifier.add_filter(filter, &block)
573
467
  end
574
468
 
575
469
  # Deletes a filter added via {Airbrake#add_performance_filter}.
@@ -587,8 +481,7 @@ module Airbrake
587
481
  # @note This method cannot delete filters assigned via the Proc form.
588
482
  # @see Airbrake::PerformanceNotifier#delete_filter
589
483
  def delete_performance_filter(filter_class)
590
- @performance_notifiers[:default].delete_filter(filter_class)
484
+ @performance_notifier.delete_filter(filter_class)
591
485
  end
592
486
  end
593
487
  end
594
- # rubocop:enable Metrics/ModuleLength
@@ -7,11 +7,23 @@ module Airbrake
7
7
  # @api private
8
8
  # @since v1.0.0
9
9
  class AsyncSender
10
- # @param [Airbrake::Config] config
11
- def initialize(config)
12
- @config = config
13
- @unsent = SizedQueue.new(config.queue_size)
14
- @sender = SyncSender.new(config)
10
+ include Loggable
11
+
12
+ # @return [ThreadGroup] the list of workers
13
+ # @note This is exposed for eaiser unit testing
14
+ # @since v4.0.0
15
+ attr_reader :workers
16
+
17
+ # @return [Array<[Airbrake::Notice,Airbrake::Promise]>] the list of unsent
18
+ # payload
19
+ # @note This is exposed for eaiser unit testing
20
+ # @since v4.0.0
21
+ attr_reader :unsent
22
+
23
+ def initialize
24
+ @config = Airbrake::Config.instance
25
+ @unsent = SizedQueue.new(Airbrake::Config.instance.queue_size)
26
+ @sender = SyncSender.new
15
27
  @closed = false
16
28
  @workers = ThreadGroup.new
17
29
  @mutex = Mutex.new
@@ -41,7 +53,7 @@ module Airbrake
41
53
 
42
54
  unless @unsent.empty?
43
55
  msg = "#{LOG_LABEL} waiting to send #{@unsent.size} unsent notice(s)..."
44
- @config.logger.debug(msg + ' (Ctrl-C to abort)')
56
+ logger.debug(msg + ' (Ctrl-C to abort)')
45
57
  end
46
58
 
47
59
  @config.workers.times { @unsent << [:stop, Airbrake::Promise.new] }
@@ -50,7 +62,7 @@ module Airbrake
50
62
  end
51
63
 
52
64
  threads.each(&:join)
53
- @config.logger.debug("#{LOG_LABEL} closed")
65
+ logger.debug("#{LOG_LABEL} closed")
54
66
  end
55
67
 
56
68
  # Checks whether the sender is closed and thus usable.
@@ -107,7 +119,7 @@ module Airbrake
107
119
  backtrace = notice[:errors][0][:backtrace].map do |line|
108
120
  "#{line[:file]}:#{line[:line]} in `#{line[:function]}'"
109
121
  end
110
- @config.logger.error(
122
+ logger.error(
111
123
  "#{LOG_LABEL} AsyncSender has reached its capacity of " \
112
124
  "#{@unsent.max} and the following notice will not be delivered " \
113
125
  "Error: #{notice[:errors][0][:type]} - #{notice[:errors][0][:message]}\n" \
@@ -7,7 +7,7 @@ module Airbrake
7
7
  # begin
8
8
  # raise 'Oops!'
9
9
  # rescue
10
- # Backtrace.parse($!, Logger.new(STDOUT))
10
+ # Backtrace.parse($!)
11
11
  # end
12
12
  #
13
13
  # @api private
@@ -93,9 +93,9 @@ module Airbrake
93
93
  # @param [Exception] exception The exception, which contains a backtrace to
94
94
  # parse
95
95
  # @return [Array<Hash{Symbol=>String,Integer}>] the parsed backtrace
96
- def self.parse(config, exception)
96
+ def self.parse(exception)
97
97
  return [] if exception.backtrace.nil? || exception.backtrace.none?
98
- parse_backtrace(config, exception)
98
+ parse_backtrace(exception)
99
99
  end
100
100
 
101
101
  # Checks whether the given exception was generated by JRuby's VM.
@@ -114,6 +114,8 @@ module Airbrake
114
114
  end
115
115
 
116
116
  class << self
117
+ include Loggable
118
+
117
119
  private
118
120
 
119
121
  def best_regexp_for(exception)
@@ -140,7 +142,7 @@ module Airbrake
140
142
  false
141
143
  end
142
144
 
143
- def stack_frame(config, regexp, stackframe)
145
+ def stack_frame(regexp, stackframe)
144
146
  if (match = match_frame(regexp, stackframe))
145
147
  return {
146
148
  file: match[:file],
@@ -149,7 +151,7 @@ module Airbrake
149
151
  }
150
152
  end
151
153
 
152
- config.logger.error(
154
+ logger.error(
153
155
  "can't parse '#{stackframe}' (please file an issue so we can fix " \
154
156
  "it: https://github.com/airbrake/airbrake-ruby/issues/new)"
155
157
  )
@@ -163,26 +165,26 @@ module Airbrake
163
165
  Patterns::GENERIC.match(stackframe)
164
166
  end
165
167
 
166
- def parse_backtrace(config, exception)
168
+ def parse_backtrace(exception)
167
169
  regexp = best_regexp_for(exception)
168
- root_directory = config.root_directory.to_s
170
+ root_directory = Airbrake::Config.instance.root_directory.to_s
169
171
 
170
172
  exception.backtrace.map.with_index do |stackframe, i|
171
- frame = stack_frame(config, regexp, stackframe)
172
- next(frame) if !config.code_hunks || frame[:file].nil?
173
+ frame = stack_frame(regexp, stackframe)
174
+ next(frame) if !Airbrake::Config.instance.code_hunks || frame[:file].nil?
173
175
 
174
176
  if !root_directory.empty?
175
- populate_code(config, frame) if frame_in_root?(frame, root_directory)
177
+ populate_code(frame) if frame_in_root?(frame, root_directory)
176
178
  elsif i < CODE_FRAME_LIMIT
177
- populate_code(config, frame)
179
+ populate_code(frame)
178
180
  end
179
181
 
180
182
  frame
181
183
  end
182
184
  end
183
185
 
184
- def populate_code(config, frame)
185
- code = Airbrake::CodeHunk.new(config).get(frame[:file], frame[:line])
186
+ def populate_code(frame)
187
+ code = Airbrake::CodeHunk.new.get(frame[:file], frame[:line])
186
188
  frame[:code] = code if code
187
189
  end
188
190