airbrake-ruby 4.9.0 → 4.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby.rb +55 -6
  3. data/lib/airbrake-ruby/async_sender.rb +3 -3
  4. data/lib/airbrake-ruby/backtrace.rb +2 -2
  5. data/lib/airbrake-ruby/code_hunk.rb +1 -1
  6. data/lib/airbrake-ruby/config.rb +1 -1
  7. data/lib/airbrake-ruby/config/validator.rb +3 -3
  8. data/lib/airbrake-ruby/deploy_notifier.rb +1 -1
  9. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +2 -2
  10. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +2 -2
  11. data/lib/airbrake-ruby/filters/keys_filter.rb +1 -1
  12. data/lib/airbrake-ruby/filters/sql_filter.rb +3 -3
  13. data/lib/airbrake-ruby/filters/thread_filter.rb +1 -1
  14. data/lib/airbrake-ruby/inspectable.rb +2 -2
  15. data/lib/airbrake-ruby/notice.rb +7 -7
  16. data/lib/airbrake-ruby/notice_notifier.rb +1 -1
  17. data/lib/airbrake-ruby/performance_breakdown.rb +1 -1
  18. data/lib/airbrake-ruby/performance_notifier.rb +36 -20
  19. data/lib/airbrake-ruby/query.rb +1 -1
  20. data/lib/airbrake-ruby/queue.rb +2 -2
  21. data/lib/airbrake-ruby/request.rb +1 -1
  22. data/lib/airbrake-ruby/stat.rb +1 -1
  23. data/lib/airbrake-ruby/version.rb +1 -1
  24. data/spec/airbrake_spec.rb +107 -48
  25. data/spec/async_sender_spec.rb +4 -4
  26. data/spec/backtrace_spec.rb +18 -18
  27. data/spec/code_hunk_spec.rb +9 -9
  28. data/spec/config/validator_spec.rb +5 -5
  29. data/spec/config_spec.rb +5 -5
  30. data/spec/deploy_notifier_spec.rb +2 -2
  31. data/spec/filter_chain_spec.rb +1 -1
  32. data/spec/filters/dependency_filter_spec.rb +1 -1
  33. data/spec/filters/gem_root_filter_spec.rb +5 -5
  34. data/spec/filters/git_last_checkout_filter_spec.rb +1 -1
  35. data/spec/filters/git_repository_filter.rb +1 -1
  36. data/spec/filters/git_revision_filter_spec.rb +10 -10
  37. data/spec/filters/keys_blacklist_spec.rb +22 -22
  38. data/spec/filters/keys_whitelist_spec.rb +21 -21
  39. data/spec/filters/root_directory_filter_spec.rb +5 -5
  40. data/spec/filters/sql_filter_spec.rb +53 -53
  41. data/spec/filters/system_exit_filter_spec.rb +1 -1
  42. data/spec/filters/thread_filter_spec.rb +28 -28
  43. data/spec/fixtures/project_root/code.rb +9 -9
  44. data/spec/notice_notifier/options_spec.rb +12 -12
  45. data/spec/notice_notifier_spec.rb +17 -17
  46. data/spec/notice_spec.rb +5 -5
  47. data/spec/performance_notifier_spec.rb +88 -68
  48. data/spec/query_spec.rb +1 -1
  49. data/spec/request_spec.rb +1 -1
  50. data/spec/response_spec.rb +8 -8
  51. data/spec/spec_helper.rb +2 -2
  52. data/spec/stat_spec.rb +2 -2
  53. data/spec/sync_sender_spec.rb +12 -12
  54. data/spec/tdigest_spec.rb +6 -6
  55. data/spec/thread_pool_spec.rb +5 -5
  56. data/spec/timed_trace_spec.rb +1 -1
  57. data/spec/truncator_spec.rb +12 -12
  58. metadata +2 -120
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2358d01604e7b5362fe0a714c0137a0a95053741e5856acd58daf889da2810cb
4
- data.tar.gz: b76c2b4282f77e94d0879df60bd5283f472ef92377d0190166cadba682ade87c
3
+ metadata.gz: f7d68430114320da9313ddd9a3dbfd05d3610701bcbb4156c5a36afba295de5f
4
+ data.tar.gz: ecd8983a7b37efb831faa440f294db56c8a59de9b9a05c1d014493d3c3da1e94
5
5
  SHA512:
6
- metadata.gz: 0f389a12166d80085b35551a29a6ec6b963b79b55da50a29c37894f0fa8d5f3bcc87c61bddca113c3ada2f250c1541be13337981da98254bb59d73c4c60aa801
7
- data.tar.gz: d8f0db0d2f682421f75d2fd7647be688decb19d2aadfadfa3c39a8d7ba198c07b7d1a6dd6a1924f2aedd7c28c482ccb63cf77ff5376cb815cade58e146a007f5
6
+ metadata.gz: 95a4ae5fe3b38a8aaaaec88a65524144f509058091d414ec1d10e7c741767c00a59c5d046ba9c4861c960bde14555d58d937e3aafee983f9fdfeb1476b5a1f77
7
+ data.tar.gz: adba5aecb6240193c212a6f7a4011adde53fcaf87238060d4474eaca012daf66b59d0361b4c982fa5b103a28ea7f4fcc70a5bd75f57f908a08ccdbc2062a56b3
@@ -135,7 +135,7 @@ module Airbrake
135
135
  # @return [Boolean] true if the notifier was configured, false otherwise
136
136
  # @since v2.3.0
137
137
  def configured?
138
- notice_notifier.configured?
138
+ @notice_notifier && @notice_notifier.configured?
139
139
  end
140
140
 
141
141
  # Sends an exception to Airbrake asynchronously.
@@ -255,10 +255,17 @@ module Airbrake
255
255
  # Airbrake.notify('App crashed!') #=> raises Airbrake::Error
256
256
  #
257
257
  # @return [void]
258
+ # rubocop:disable Style/GuardClause, Style/IfUnlessModifier
258
259
  def close
259
- notice_notifier.close
260
- performance_notifier.close
260
+ if defined?(@notice_notifier) && @notice_notifier
261
+ @notice_notifier.close
262
+ end
263
+
264
+ if defined?(@performance_notifier) && @performance_notifier
265
+ @performance_notifier.close
266
+ end
261
267
  end
268
+ # rubocop:enable Style/GuardClause, Style/IfUnlessModifier
262
269
 
263
270
  # Pings the Airbrake Deploy API endpoint about the occurred deploy.
264
271
  #
@@ -364,6 +371,17 @@ module Airbrake
364
371
  performance_notifier.notify(request)
365
372
  end
366
373
 
374
+ # Synchronously Increments request statistics of a certain +route+ that was
375
+ # invoked on +start_time+ and ended on +end_time+ with +method+, and
376
+ # returned +status_code+.
377
+ # @since v4.10.0
378
+ # @see .notify_request
379
+ def notify_request_sync(request_info, stash = {})
380
+ request = Request.new(request_info)
381
+ request.stash.merge!(stash)
382
+ performance_notifier.notify_sync(request)
383
+ end
384
+
367
385
  # Increments SQL statistics of a certain +query+ that was invoked on
368
386
  # +start_time+ and finished on +end_time+. When +method+ and +route+ are
369
387
  # provided, the query is grouped by these parameters.
@@ -399,6 +417,17 @@ module Airbrake
399
417
  performance_notifier.notify(query)
400
418
  end
401
419
 
420
+ # Synchronously increments SQL statistics of a certain +query+ that was
421
+ # invoked on +start_time+ and finished on +end_time+. When +method+ and
422
+ # +route+ are provided, the query is grouped by these parameters.
423
+ # @since v4.10.0
424
+ # @see .notify_query
425
+ def notify_query_sync(query_info, stash = {})
426
+ query = Query.new(query_info)
427
+ query.stash.merge!(stash)
428
+ performance_notifier.notify_sync(query)
429
+ end
430
+
402
431
  # Increments performance breakdown statistics of a certain route.
403
432
  #
404
433
  # @example
@@ -427,6 +456,16 @@ module Airbrake
427
456
  performance_notifier.notify(performance_breakdown)
428
457
  end
429
458
 
459
+ # Increments performance breakdown statistics of a certain route
460
+ # synchronously.
461
+ # @since v4.10.0
462
+ # @see .notify_performance_breakdown
463
+ def notify_performance_breakdown_sync(breakdown_info, stash = {})
464
+ performance_breakdown = PerformanceBreakdown.new(breakdown_info)
465
+ performance_breakdown.stash.merge!(stash)
466
+ performance_notifier.notify_sync(performance_breakdown)
467
+ end
468
+
430
469
  # Increments statistics of a certain queue (worker).
431
470
  #
432
471
  # @example
@@ -442,16 +481,26 @@ module Airbrake
442
481
  # failed
443
482
  # @option queue_info [Array<Hash{Symbol=>Float}>] :groups Where the job
444
483
  # spent its time
445
- # @param [Hash] stash What needs to be appeneded to the stash, so it's
484
+ # @param [Hash] stash What needs to be appended to the stash, so it's
446
485
  # available in filters
447
486
  # @return [void]
448
487
  # @since v4.9.0
488
+ # @see .notify_queue_sync
449
489
  def notify_queue(queue_info, stash = {})
450
490
  queue = Queue.new(queue_info)
451
491
  queue.stash.merge!(stash)
452
492
  performance_notifier.notify(queue)
453
493
  end
454
494
 
495
+ # Increments statistics of a certain queue (worker) synchronously.
496
+ # @since v4.10.0
497
+ # @see .notify_queue
498
+ def notify_queue_sync(queue_info, stash = {})
499
+ queue = Queue.new(queue_info)
500
+ queue.stash.merge!(stash)
501
+ performance_notifier.notify_sync(queue)
502
+ end
503
+
455
504
  # Runs a callback before {.notify_request} or {.notify_query} kicks in. This
456
505
  # is useful if you want to ignore specific resources or filter the data the
457
506
  # resource contains.
@@ -509,7 +558,7 @@ module Airbrake
509
558
  # @return [void]
510
559
  # @since v4.2.2
511
560
  def reset
512
- close if notice_notifier && configured?
561
+ close
513
562
 
514
563
  self.performance_notifier = PerformanceNotifier.new
515
564
  self.notice_notifier = NoticeNotifier.new
@@ -535,7 +584,7 @@ module Airbrake
535
584
  Airbrake::Filters::RootDirectoryFilter,
536
585
  Airbrake::Filters::GitRevisionFilter,
537
586
  Airbrake::Filters::GitRepositoryFilter,
538
- Airbrake::Filters::GitLastCheckoutFilter
587
+ Airbrake::Filters::GitLastCheckoutFilter,
539
588
  ].each do |filter|
540
589
  notice_notifier.add_filter(filter.new(config.root_directory))
541
590
  end
@@ -54,7 +54,7 @@ module Airbrake
54
54
  ThreadPool.new(
55
55
  worker_size: @config.workers,
56
56
  queue_size: @config.queue_size,
57
- block: proc { |args| sender.send(*args) }
57
+ block: proc { |args| sender.send(*args) },
58
58
  )
59
59
  end
60
60
  end
@@ -71,8 +71,8 @@ module Airbrake
71
71
  message: error[:message],
72
72
  backtrace: error[:backtrace].map do |line|
73
73
  "#{line[:file]}:#{line[:line]} in `#{line[:function]}'"
74
- end.join("\n")
75
- )
74
+ end.join("\n"),
75
+ ),
76
76
  )
77
77
  promise.reject("AsyncSender has reached its capacity of #{@config.queue_size}")
78
78
  end
@@ -147,13 +147,13 @@ module Airbrake
147
147
  return {
148
148
  file: match[:file],
149
149
  line: (Integer(match[:line]) if match[:line]),
150
- function: match[:function]
150
+ function: match[:function],
151
151
  }
152
152
  end
153
153
 
154
154
  logger.error(
155
155
  "can't parse '#{stackframe}' (please file an issue so we can fix " \
156
- "it: https://github.com/airbrake/airbrake-ruby/issues/new)"
156
+ "it: https://github.com/airbrake/airbrake-ruby/issues/new)",
157
157
  )
158
158
  { file: nil, line: nil, function: stackframe }
159
159
  end
@@ -30,7 +30,7 @@ module Airbrake
30
30
  Airbrake::FileCache[file] ||= File.foreach(file)
31
31
  rescue StandardError => ex
32
32
  logger.error(
33
- "#{self.class.name}: can't read code hunk for #{file}: #{ex}"
33
+ "#{self.class.name}: can't read code hunk for #{file}: #{ex}",
34
34
  )
35
35
  nil
36
36
  end
@@ -132,7 +132,7 @@ module Airbrake
132
132
 
133
133
  self.root_directory = File.realpath(
134
134
  (defined?(Bundler) && Bundler.root) ||
135
- Dir.pwd
135
+ Dir.pwd,
136
136
  )
137
137
 
138
138
  self.versions = {}
@@ -29,7 +29,7 @@ module Airbrake
29
29
  return promise.reject(
30
30
  "the 'environment' option must be configured " \
31
31
  "with a Symbol (or String), but '#{config.environment.class}' was " \
32
- "provided: #{config.environment}"
32
+ "provided: #{config.environment}",
33
33
  )
34
34
  end
35
35
 
@@ -46,7 +46,7 @@ module Airbrake
46
46
 
47
47
  if ignored_environment?(config)
48
48
  return promise.reject(
49
- "current environment '#{config.environment}' is ignored"
49
+ "current environment '#{config.environment}' is ignored",
50
50
  )
51
51
  end
52
52
 
@@ -74,7 +74,7 @@ module Airbrake
74
74
  if config.ignore_environments.any? && config.environment.nil?
75
75
  config.logger.warn(
76
76
  "#{LOG_LABEL} the 'environment' option is not set, " \
77
- "'ignore_environments' has no effect"
77
+ "'ignore_environments' has no effect",
78
78
  )
79
79
  end
80
80
 
@@ -27,7 +27,7 @@ module Airbrake
27
27
  @sender.send(
28
28
  deploy_info,
29
29
  promise,
30
- URI.join(@config.host, "api/v4/projects/#{@config.project_id}/deploys")
30
+ URI.join(@config.host, "api/v4/projects/#{@config.project_id}/deploys"),
31
31
  )
32
32
 
33
33
  promise
@@ -22,13 +22,13 @@ module Airbrake
22
22
  attributes = exception.to_airbrake
23
23
  rescue StandardError => ex
24
24
  logger.error(
25
- "#{LOG_LABEL} #{exception.class}#to_airbrake failed. #{ex.class}: #{ex}"
25
+ "#{LOG_LABEL} #{exception.class}#to_airbrake failed. #{ex.class}: #{ex}",
26
26
  )
27
27
  end
28
28
 
29
29
  unless attributes.is_a?(Hash)
30
30
  logger.error(
31
- "#{LOG_LABEL} #{self.class}: wanted Hash, got #{attributes.class}"
31
+ "#{LOG_LABEL} #{self.class}: wanted Hash, got #{attributes.class}",
32
32
  )
33
33
  return
34
34
  end
@@ -52,7 +52,7 @@ module Airbrake
52
52
  parts = line.chomp.split("\t").first.split(' ')
53
53
  if parts.size < MIN_HEAD_COLS
54
54
  logger.error(
55
- "#{LOG_LABEL} Airbrake::#{self.class.name}: can't parse line: #{line}"
55
+ "#{LOG_LABEL} Airbrake::#{self.class.name}: can't parse line: #{line}",
56
56
  )
57
57
  return
58
58
  end
@@ -62,7 +62,7 @@ module Airbrake
62
62
  username: author[0..1].join(' '),
63
63
  email: parts[-3][1..-2],
64
64
  revision: parts[1],
65
- time: timestamp(parts[-2].to_i)
65
+ time: timestamp(parts[-2].to_i),
66
66
  }
67
67
  end
68
68
  # rubocop:enable Metrics/AbcSize
@@ -124,7 +124,7 @@ module Airbrake
124
124
 
125
125
  logger.error(
126
126
  "#{LOG_LABEL} one of the patterns in #{self.class} is invalid. " \
127
- "Known patterns: #{@patterns}"
127
+ "Known patterns: #{@patterns}",
128
128
  )
129
129
  end
130
130
 
@@ -64,7 +64,7 @@ module Airbrake
64
64
  cassandra: %i[
65
65
  single_quotes uuids numeric_literals boolean_literals
66
66
  hexadecimal_literals comments multi_line_comments
67
- ].freeze
67
+ ].freeze,
68
68
  }.freeze
69
69
 
70
70
  # @return [Hash{Symbol=>Regexp}] a set of regexps to check for unmatches
@@ -76,7 +76,7 @@ module Airbrake
76
76
  sqlite: %r{'|/\*|\*/},
77
77
  cassandra: %r{'|/\*|\*/},
78
78
  oracle: %r{'|/\*|\*/},
79
- oracle_enhanced: %r{'|/\*|\*/}
79
+ oracle_enhanced: %r{'|/\*|\*/},
80
80
  }.freeze
81
81
 
82
82
  # @return [Array<Regexp>] the list of queries to be ignored
@@ -89,7 +89,7 @@ module Airbrake
89
89
  /FROM pg_attribute/i,
90
90
  /FROM pg_index/i,
91
91
  /FROM pg_class/i,
92
- /FROM pg_type/i
92
+ /FROM pg_type/i,
93
93
  ].freeze
94
94
 
95
95
  def initialize(dialect)
@@ -16,7 +16,7 @@ module Airbrake
16
16
  String,
17
17
  Symbol,
18
18
  Regexp,
19
- Numeric
19
+ Numeric,
20
20
  ].freeze
21
21
 
22
22
  # Variables starting with this prefix are not attached to a notice.
@@ -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('>')
@@ -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
 
@@ -116,7 +116,7 @@ module Airbrake
116
116
 
117
117
  logger.warn(
118
118
  "#{LOG_LABEL} falling back to sync delivery because there are no " \
119
- "running async workers"
119
+ "running async workers",
120
120
  )
121
121
  @sync_sender
122
122
  end
@@ -39,7 +39,7 @@ module Airbrake
39
39
  'method' => method,
40
40
  'route' => route,
41
41
  'responseType' => response_type,
42
- 'time' => @start_time_utc
42
+ 'time' => @start_time_utc,
43
43
  }.delete_if { |_key, val| val.nil? }
44
44
  end
45
45
  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
- @sender = AsyncSender.new(:put)
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
- promise = @config.check_configuration
27
- return promise if promise.rejected?
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
- promise.resolve(:success)
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
- @sender.close
51
+ @async_sender.close
57
52
  logger.debug("#{LOG_LABEL} performance notifier closed")
58
53
  end
59
54
  end
@@ -106,12 +101,32 @@ module Airbrake
106
101
  @payload = {}
107
102
  end
108
103
 
109
- send(payload, Airbrake::Promise.new)
104
+ send(@async_sender, payload, Airbrake::Promise.new)
105
+ end
106
+ end
107
+ end
108
+
109
+ def send_resource(resource, sync:)
110
+ promise = @config.check_configuration
111
+ return promise if promise.rejected?
112
+
113
+ promise = @config.check_performance_options(resource)
114
+ return promise if promise.rejected?
115
+
116
+ @filter_chain.refine(resource)
117
+ return if resource.ignored?
118
+
119
+ @mutex.synchronize do
120
+ update_payload(resource)
121
+ if sync || @flush_period == 0
122
+ send(@sync_sender, @payload, promise)
123
+ else
124
+ schedule_flush
110
125
  end
111
126
  end
112
127
  end
113
128
 
114
- def send(payload, promise)
129
+ def send(sender, payload, promise)
115
130
  signature = "#{self.class.name}##{__method__}"
116
131
  raise "#{signature}: payload (#{payload}) cannot be empty. Race?" if payload.none?
117
132
 
@@ -120,9 +135,9 @@ module Airbrake
120
135
  with_grouped_payload(payload) do |resource_hash, destination|
121
136
  url = URI.join(
122
137
  @config.host,
123
- "api/v5/projects/#{@config.project_id}/#{destination}"
138
+ "api/v5/projects/#{@config.project_id}/#{destination}",
124
139
  )
125
- @sender.send(resource_hash, promise, url)
140
+ sender.send(resource_hash, promise, url)
126
141
  end
127
142
 
128
143
  promise
@@ -157,4 +172,5 @@ module Airbrake
157
172
  end
158
173
  end
159
174
  end
175
+ # rubocop:enable Metrics/ClassLength
160
176
  end