airbrake 9.3.0 → 13.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +5 -5
  2. data/lib/airbrake/capistrano/capistrano2.rb +3 -1
  3. data/lib/airbrake/capistrano/capistrano3.rb +3 -1
  4. data/lib/airbrake/capistrano.rb +2 -0
  5. data/lib/airbrake/delayed_job.rb +31 -15
  6. data/lib/airbrake/logger.rb +5 -1
  7. data/lib/airbrake/rack/context_filter.rb +13 -2
  8. data/lib/airbrake/rack/http_headers_filter.rb +7 -5
  9. data/lib/airbrake/rack/http_params_filter.rb +2 -0
  10. data/lib/airbrake/rack/instrumentable.rb +116 -4
  11. data/lib/airbrake/rack/middleware.rb +3 -1
  12. data/lib/airbrake/rack/request_body_filter.rb +2 -0
  13. data/lib/airbrake/rack/request_store.rb +2 -0
  14. data/lib/airbrake/rack/route_filter.rb +9 -11
  15. data/lib/airbrake/rack/session_filter.rb +2 -0
  16. data/lib/airbrake/rack/user.rb +9 -1
  17. data/lib/airbrake/rack/user_filter.rb +2 -0
  18. data/lib/airbrake/rack.rb +4 -0
  19. data/lib/airbrake/rails/action_cable/notify_callback.rb +2 -0
  20. data/lib/airbrake/rails/action_cable.rb +21 -17
  21. data/lib/airbrake/rails/action_controller.rb +9 -4
  22. data/lib/airbrake/rails/action_controller_notify_subscriber.rb +6 -2
  23. data/lib/airbrake/rails/action_controller_performance_breakdown_subscriber.rb +6 -1
  24. data/lib/airbrake/rails/action_controller_route_subscriber.rb +8 -15
  25. data/lib/airbrake/rails/active_job.rb +25 -8
  26. data/lib/airbrake/rails/active_record.rb +2 -0
  27. data/lib/airbrake/rails/active_record_subscriber.rb +7 -3
  28. data/lib/airbrake/rails/app.rb +64 -19
  29. data/lib/airbrake/rails/backtrace_cleaner.rb +13 -0
  30. data/lib/airbrake/rails/curb.rb +19 -22
  31. data/lib/airbrake/rails/event.rb +26 -8
  32. data/lib/airbrake/rails/excon_subscriber.rb +4 -0
  33. data/lib/airbrake/rails/http.rb +13 -7
  34. data/lib/airbrake/rails/http_client.rb +12 -6
  35. data/lib/airbrake/rails/net_http.rb +14 -6
  36. data/lib/airbrake/rails/railtie.rb +54 -0
  37. data/lib/airbrake/rails/railties/action_controller_tie.rb +90 -0
  38. data/lib/airbrake/rails/railties/active_record_tie.rb +74 -0
  39. data/lib/airbrake/rails/railties/middleware_tie.rb +62 -0
  40. data/lib/airbrake/rails/typhoeus.rb +11 -7
  41. data/lib/airbrake/rails.rb +14 -135
  42. data/lib/airbrake/rake/tasks.rb +15 -13
  43. data/lib/airbrake/rake.rb +49 -46
  44. data/lib/airbrake/resque.rb +33 -0
  45. data/lib/airbrake/shoryuken.rb +16 -4
  46. data/lib/airbrake/sidekiq/retryable_jobs_filter.rb +9 -1
  47. data/lib/airbrake/sidekiq.rb +22 -16
  48. data/lib/airbrake/sneakers.rb +39 -1
  49. data/lib/airbrake/version.rb +3 -1
  50. data/lib/airbrake.rb +2 -0
  51. data/lib/generators/airbrake_generator.rb +5 -6
  52. data/lib/generators/airbrake_initializer.rb.erb +67 -65
  53. metadata +53 -97
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Airbrake
4
+ module Rails
5
+ module Railties
6
+ # Ties Airbrake Rails Middleware with Rails (error sending).
7
+ #
8
+ # Since Rails 3.2 the ActionDispatch::DebugExceptions middleware is
9
+ # responsible for logging exceptions and showing a debugging page in case
10
+ # the request is local. We want to insert our middleware after
11
+ # DebugExceptions, so we don't notify Airbrake about local requests.
12
+ #
13
+ # @api private
14
+ # @since v13.0.1
15
+ class MiddlewareTie
16
+ def initialize(app)
17
+ @app = app
18
+ @middleware = app.config.middleware
19
+ end
20
+
21
+ def call
22
+ return tie_rails_5_or_above if ::Rails.version.to_i >= 5
23
+
24
+ if defined?(::ActiveRecord::ConnectionAdapters::ConnectionManagement)
25
+ return tie_rails_4_or_below_with_active_record
26
+ end
27
+
28
+ tie_rails_4_or_below_without_active_record
29
+ end
30
+
31
+ private
32
+
33
+ # Avoid the warning about deprecated strings.
34
+ # Insert after DebugExceptions, since ConnectionManagement doesn't
35
+ # exist in Rails 5 anymore.
36
+ def tie_rails_5_or_above
37
+ @middleware.insert_after(
38
+ ActionDispatch::DebugExceptions,
39
+ Airbrake::Rack::Middleware,
40
+ )
41
+ end
42
+
43
+ # Insert after ConnectionManagement to avoid DB connection leakage:
44
+ # https://github.com/airbrake/airbrake/pull/568
45
+ def tie_rails_4_or_below_with_active_record
46
+ @middleware.insert_after(
47
+ ::ActiveRecord::ConnectionAdapters::ConnectionManagement,
48
+ 'Airbrake::Rack::Middleware',
49
+ )
50
+ end
51
+
52
+ # Insert after DebugExceptions for apps without ActiveRecord.
53
+ def tie_rails_4_or_below_without_active_record
54
+ @middleware.insert_after(
55
+ ActionDispatch::DebugExceptions,
56
+ 'Airbrake::Rack::Middleware',
57
+ )
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,12 +1,16 @@
1
- module Typhoeus
2
- # Monkey-patch to measure request timing.
3
- class Request
4
- alias run_without_airbrake run
1
+ # frozen_string_literal: true
5
2
 
6
- def run
7
- Airbrake::Rack.capture_timing(:http) do
8
- run_without_airbrake
3
+ module Airbrake
4
+ module Rails
5
+ # Allow measuring request timing.
6
+ module TyphoeusRequest
7
+ def run
8
+ Airbrake::Rack.capture_timing(:http) do
9
+ super
10
+ end
9
11
  end
10
12
  end
11
13
  end
12
14
  end
15
+
16
+ Typhoeus::Request.prepend(Airbrake::Rails::TyphoeusRequest)
@@ -1,140 +1,19 @@
1
- module Airbrake
2
- module Rails
3
- # This railtie works for any Rails application that supports railties (Rails
4
- # 3.2+ apps). It makes Airbrake Ruby work with Rails and report errors
5
- # occurring in the application automatically.
6
- class Railtie < ::Rails::Railtie
7
- initializer('airbrake.middleware') do |app|
8
- # Since Rails 3.2 the ActionDispatch::DebugExceptions middleware is
9
- # responsible for logging exceptions and showing a debugging page in
10
- # case the request is local. We want to insert our middleware after
11
- # DebugExceptions, so we don't notify Airbrake about local requests.
12
-
13
- if ::Rails.version.to_i >= 5
14
- # Avoid the warning about deprecated strings.
15
- # Insert after DebugExceptions, since ConnectionManagement doesn't
16
- # exist in Rails 5 anymore.
17
- app.config.middleware.insert_after(
18
- ActionDispatch::DebugExceptions,
19
- Airbrake::Rack::Middleware
20
- )
21
- elsif defined?(::ActiveRecord::ConnectionAdapters::ConnectionManagement)
22
- # Insert after ConnectionManagement to avoid DB connection leakage:
23
- # https://github.com/airbrake/airbrake/pull/568
24
- app.config.middleware.insert_after(
25
- ::ActiveRecord::ConnectionAdapters::ConnectionManagement,
26
- 'Airbrake::Rack::Middleware'
27
- )
28
- else
29
- # Insert after DebugExceptions for apps without ActiveRecord.
30
- app.config.middleware.insert_after(
31
- ActionDispatch::DebugExceptions,
32
- 'Airbrake::Rack::Middleware'
33
- )
34
- end
35
- end
36
-
37
- rake_tasks do
38
- # Report exceptions occurring in Rake tasks.
39
- require 'airbrake/rake'
40
-
41
- # Defines tasks such as `airbrake:test` & `airbrake:deploy`.
42
- require 'airbrake/rake/tasks'
43
- end
44
-
45
- # rubocop:disable Metrics/BlockLength
46
- initializer('airbrake.action_controller') do
47
- ActiveSupport.on_load(:action_controller) do
48
- # Patches ActionController with methods that allow us to retrieve
49
- # interesting request data. Appends that information to notices.
50
- require 'airbrake/rails/action_controller'
51
- include Airbrake::Rails::ActionController
52
-
53
- if Airbrake::Config.instance.performance_stats
54
- # Cache route information for the duration of the request.
55
- require 'airbrake/rails/action_controller_route_subscriber'
56
- ActiveSupport::Notifications.subscribe(
57
- 'start_processing.action_controller',
58
- Airbrake::Rails::ActionControllerRouteSubscriber.new
59
- )
60
-
61
- # Send route stats.
62
- require 'airbrake/rails/action_controller_notify_subscriber'
63
- ActiveSupport::Notifications.subscribe(
64
- 'process_action.action_controller',
65
- Airbrake::Rails::ActionControllerNotifySubscriber.new
66
- )
67
-
68
- # Send performance breakdown: where a request spends its time.
69
- require 'airbrake/rails/action_controller_performance_breakdown_subscriber'
70
- ActiveSupport::Notifications.subscribe(
71
- 'process_action.action_controller',
72
- Airbrake::Rails::ActionControllerPerformanceBreakdownSubscriber.new
73
- )
74
-
75
- require 'airbrake/rails/net_http' if defined?(Net) && defined?(Net::HTTP)
76
-
77
- if defined?(Curl) && defined?(Curl::CURB_VERSION)
78
- require 'airbrake/rails/curb'
79
- end
1
+ # frozen_string_literal: true
80
2
 
81
- require 'airbrake/rails/http' if defined?(HTTP) && defined?(HTTP::Client)
82
- require 'airbrake/rails/http_client' if defined?(HTTPClient)
83
- require 'airbrake/rails/typhoeus' if defined?(Typhoeus)
3
+ require 'airbrake/rails/railtie'
84
4
 
85
- if defined?(Excon)
86
- require 'airbrake/rails/excon_subscriber'
87
- ActiveSupport::Notifications.subscribe(/excon/, Airbrake::Rails::Excon.new)
88
- ::Excon.defaults[:instrumentor] = ActiveSupport::Notifications
89
- end
90
- end
91
- end
92
- end
93
- # rubocop:enable Metrics/BlockLength
94
-
95
- initializer('airbrake.active_record') do
96
- ActiveSupport.on_load(:active_record) do
97
- # Reports exceptions occurring in some bugged ActiveRecord callbacks.
98
- # Applicable only to the versions of Rails lower than 4.2.
99
- require 'airbrake/rails/active_record'
100
- include Airbrake::Rails::ActiveRecord
101
-
102
- if defined?(ActiveRecord) && Airbrake::Config.instance.performance_stats
103
- # Send SQL queries.
104
- require 'airbrake/rails/active_record_subscriber'
105
- ActiveSupport::Notifications.subscribe(
106
- 'sql.active_record', Airbrake::Rails::ActiveRecordSubscriber.new
107
- )
108
-
109
- # Filter out parameters from SQL body.
110
- Airbrake.add_performance_filter(
111
- Airbrake::Filters::SqlFilter.new(
112
- ::ActiveRecord::Base.connection_config[:adapter]
113
- )
114
- )
115
- end
116
- end
117
- end
118
-
119
- initializer('airbrake.active_job') do
120
- ActiveSupport.on_load(:active_job) do
121
- # Reports exceptions occurring in ActiveJob jobs.
122
- require 'airbrake/rails/active_job'
123
- include Airbrake::Rails::ActiveJob
124
- end
125
- end
126
-
127
- initializer('airbrake.action_cable') do
128
- ActiveSupport.on_load(:action_cable) do
129
- # Reports exceptions occurring in ActionCable connections.
130
- require 'airbrake/rails/action_cable'
131
- end
132
- end
133
-
134
- runner do
135
- at_exit do
136
- Airbrake.notify_sync($ERROR_INFO) if $ERROR_INFO
137
- end
5
+ module Airbrake
6
+ # Rails namespace holds all Rails-related functionality.
7
+ module Rails
8
+ def self.logger
9
+ # Rails.logger is not set in some Rake tasks such as
10
+ # 'airbrake:deploy'. In this case we use a sensible fallback.
11
+ level = (::Rails.logger ? ::Rails.logger.level : Logger::ERROR)
12
+
13
+ if ENV['RAILS_LOG_TO_STDOUT'].present?
14
+ Logger.new($stdout, level: level)
15
+ else
16
+ Logger.new(::Rails.root.join('log', 'airbrake.log'), level: level)
138
17
  end
139
18
  end
140
19
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'airbrake-ruby'
2
4
 
3
5
  namespace :airbrake do
@@ -51,11 +53,11 @@ namespace :airbrake do
51
53
  raise Airbrake::Error, 'airbrake-ruby is not configured' unless Airbrake.configured?
52
54
 
53
55
  deploy_params = {
54
- environment: ENV['ENVIRONMENT'],
55
- username: ENV['USERNAME'],
56
- revision: ENV['REVISION'],
57
- repository: ENV['REPOSITORY'],
58
- version: ENV['VERSION']
56
+ environment: ENV.fetch('ENVIRONMENT', nil),
57
+ username: ENV.fetch('USERNAME', nil),
58
+ revision: ENV.fetch('REVISION', nil),
59
+ repository: ENV.fetch('REPOSITORY', nil),
60
+ version: ENV.fetch('VERSION', nil),
59
61
  }
60
62
  promise = Airbrake.notify_deploy(deploy_params)
61
63
  promise.then do
@@ -66,10 +68,10 @@ namespace :airbrake do
66
68
 
67
69
  desc 'Install a Heroku deploy hook to notify Airbrake of deploys'
68
70
  task :install_heroku_deploy_hook do
69
- app = ENV['HEROKU_APP']
71
+ app = ENV.fetch('HEROKU_APP', nil)
70
72
 
71
73
  config = Bundler.with_clean_env do
72
- `heroku config --shell#{ " --app #{app}" if app }`
74
+ `heroku config --shell#{" --app #{app}" if app}`
73
75
  end
74
76
 
75
77
  heroku_env = config.each_line.with_object({}) do |line, h|
@@ -83,11 +85,11 @@ namespace :airbrake do
83
85
 
84
86
  unless (env = heroku_env['RAILS_ENV'])
85
87
  env = 'production'
86
- puts "Airbrake couldn't identify your app's environment, so the '#{env}'" \
87
- " environment will be used."
88
+ puts "Airbrake couldn't identify your app's environment, " \
89
+ "so the '#{env}' environment will be used."
88
90
  end
89
91
 
90
- unless (repo = ENV['REPOSITORY_URL'])
92
+ unless (repo = ENV.fetch('REPOSITORY_URL', nil))
91
93
  repo = `git remote get-url origin 2>/dev/null`.chomp
92
94
  if repo.empty?
93
95
  puts "Airbrake couldn't identify your app's repository."
@@ -97,14 +99,14 @@ namespace :airbrake do
97
99
  end
98
100
  end
99
101
 
100
- url = "https://airbrake.io/api/v3/projects/#{id}/heroku-deploys?key=#{key}"
102
+ url = ["https://airbrake.io/api/v3/projects/#{id}/heroku-deploys?key=#{key}"]
101
103
  url << "&environment=#{env}"
102
104
  url << "&repository=#{repo}" unless repo.empty?
103
105
 
104
- command = %(heroku addons:create deployhooks:http --url="#{url}")
106
+ command = [%(heroku addons:create deployhooks:http --url="#{url.join}")]
105
107
  command << " --app #{app}" if app
106
108
 
107
- puts "$ #{command}"
109
+ puts "$ #{command.join}"
108
110
  Bundler.with_clean_env { puts `#{command}` }
109
111
  end
110
112
  end
data/lib/airbrake/rake.rb CHANGED
@@ -1,63 +1,66 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This is not bulletproof, but if this file is executed before a task
2
4
  # definition, we can grab tasks descriptions and locations.
3
5
  # See: https://goo.gl/ksn6PE
4
6
  Rake::TaskManager.record_task_metadata = true
5
7
 
6
- module Rake
7
- # Redefine +Rake::Task#execute+, so it can report errors to Airbrake.
8
- class Task
9
- # Store the original method to use it later.
10
- alias execute_without_airbrake execute
11
-
12
- # A wrapper around the original +#execute+, that catches all errors and
13
- # notifies Airbrake.
14
- #
15
- # rubocop:disable Lint/RescueException
16
- def execute(args = nil)
17
- execute_without_airbrake(args)
18
- rescue Exception => ex
19
- notify_airbrake(ex, args)
20
- raise ex
21
- end
22
- # rubocop:enable Lint/RescueException
8
+ module Airbrake
9
+ module Rake
10
+ # Redefine +Rake::Task#execute+, so it can report errors to Airbrake.
11
+ module Task
12
+ # A wrapper around the original +#execute+, that catches all errors and
13
+ # notifies Airbrake.
14
+ #
15
+ # rubocop:disable Lint/RescueException
16
+ def execute(args = nil)
17
+ super(args)
18
+ rescue Exception => ex
19
+ notify_airbrake(ex, args)
20
+ raise ex
21
+ end
22
+ # rubocop:enable Lint/RescueException
23
23
 
24
- private
24
+ private
25
25
 
26
- def notify_airbrake(exception, args)
27
- notice = Airbrake.build_notice(exception)
28
- notice[:context][:component] = 'rake'
29
- notice[:context][:action] = name
30
- notice[:params].merge!(
31
- rake_task: task_info,
32
- execute_args: args,
33
- argv: ARGV.join(' ')
34
- )
26
+ def notify_airbrake(exception, args)
27
+ notice = Airbrake.build_notice(exception)
28
+ notice[:context][:component] = 'rake'
29
+ notice[:context][:action] = name
30
+ notice[:params].merge!(
31
+ rake_task: task_info,
32
+ execute_args: args,
33
+ argv: ARGV.join(' '),
34
+ )
35
35
 
36
- Airbrake.notify_sync(notice)
37
- end
36
+ Airbrake.notify_sync(notice)
37
+ end
38
38
 
39
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize
40
- def task_info
41
- info = {}
39
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize
40
+ def task_info
41
+ info = {}
42
42
 
43
- info[:name] = name
44
- info[:timestamp] = timestamp.to_s
45
- info[:investigation] = investigation
43
+ info[:name] = name
44
+ info[:timestamp] = timestamp.to_s
45
+ info[:investigation] = investigation
46
46
 
47
- info[:full_comment] = full_comment if full_comment
48
- info[:arg_names] = arg_names if arg_names.any?
49
- info[:arg_description] = arg_description if arg_description
50
- info[:locations] = locations if locations.any?
51
- info[:sources] = sources if sources.any?
47
+ info[:full_comment] = full_comment if full_comment
48
+ info[:arg_names] = arg_names if arg_names.any?
49
+ info[:arg_description] = arg_description if arg_description
50
+ info[:locations] = locations if locations.any?
51
+ info[:sources] = sources if sources.any?
52
52
 
53
- if prerequisite_tasks.any?
54
- info[:prerequisite_tasks] = prerequisite_tasks.map do |p|
55
- p.__send__(:task_info)
53
+ if prerequisite_tasks.any?
54
+ info[:prerequisite_tasks] = prerequisite_tasks.map do |p|
55
+ p.__send__(:task_info)
56
+ end
56
57
  end
57
- end
58
58
 
59
- info
59
+ info
60
+ end
61
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize
60
62
  end
61
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize
62
63
  end
63
64
  end
65
+
66
+ Rake::Task.prepend(Airbrake::Rake::Task)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Resque
2
4
  module Failure
3
5
  # Provides Resque integration with Airbrake.
@@ -27,3 +29,34 @@ module Resque
27
29
  end
28
30
  end
29
31
  end
32
+
33
+ module Airbrake
34
+ module Resque
35
+ # Measures elapsed time of a job and notifies Airbrake of the execution
36
+ # status.
37
+ #
38
+ # @since v9.6.0
39
+ module Job
40
+ def perform
41
+ timing = Airbrake::Benchmark.measure do
42
+ super
43
+ end
44
+ rescue StandardError => exception
45
+ Airbrake.notify_queue_sync(
46
+ queue: payload['class'],
47
+ error_count: 1,
48
+ timing: 0.01,
49
+ )
50
+ raise exception
51
+ else
52
+ Airbrake.notify_queue_sync(
53
+ queue: payload['class'],
54
+ error_count: 0,
55
+ timing: timing,
56
+ )
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ Resque::Job.prepend(Airbrake::Resque::Job)
@@ -1,14 +1,26 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Shoryuken
3
5
  # Provides integration with Shoryuken.
4
6
  class ErrorHandler
5
7
  # rubocop:disable Lint/RescueException
6
- def call(worker, queue, _sqs_msg, body)
7
- yield
8
+ def call(worker, queue, _sqs_msg, body, &block)
9
+ timing = Airbrake::Benchmark.measure(&block)
8
10
  rescue Exception => exception
9
11
  notify_airbrake(exception, worker, queue, body)
10
-
12
+ Airbrake.notify_queue(
13
+ queue: worker.class.to_s,
14
+ error_count: 1,
15
+ timing: 0.01,
16
+ )
11
17
  raise exception
18
+ else
19
+ Airbrake.notify_queue(
20
+ queue: worker.class.to_s,
21
+ error_count: 0,
22
+ timing: timing,
23
+ )
12
24
  end
13
25
  # rubocop:enable Lint/RescueException
14
26
 
@@ -24,7 +36,7 @@ module Airbrake
24
36
  def notice_context(queue, body)
25
37
  {
26
38
  queue: queue,
27
- body: body.is_a?(Array) ? { batch: body } : { body: body }
39
+ body: body.is_a?(Array) ? { batch: body } : { body: body },
28
40
  }
29
41
  end
30
42
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Sidekiq
3
5
  # Filter that can ignore notices from jobs that failed but will be retried
@@ -13,6 +15,10 @@ module Airbrake
13
15
  DEFAULT_MAX_RETRY_ATTEMPTS = ::Sidekiq::JobRetry::DEFAULT_MAX_RETRY_ATTEMPTS
14
16
  end
15
17
 
18
+ def initialize(max_retries: nil)
19
+ @max_retries = max_retries
20
+ end
21
+
16
22
  def call(notice)
17
23
  job = notice[:params][:job]
18
24
 
@@ -30,7 +36,9 @@ module Airbrake
30
36
  end
31
37
 
32
38
  def max_attempts_for(job)
33
- if job['retry'].is_a?(Integer)
39
+ if @max_retries
40
+ @max_retries
41
+ elsif job['retry'].is_a?(Integer)
34
42
  job['retry']
35
43
  else
36
44
  max_retries
@@ -1,22 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'airbrake/sidekiq/retryable_jobs_filter'
2
4
 
3
5
  module Airbrake
4
6
  module Sidekiq
5
7
  # Provides integration with Sidekiq v2+.
6
8
  class ErrorHandler
7
- # rubocop:disable Lint/RescueException
8
- def call(_worker, context, _queue)
9
- yield
10
- rescue Exception => exception
9
+ def call(_worker, context, _queue, &block)
10
+ timing = Airbrake::Benchmark.measure(&block)
11
+ rescue Exception => exception # rubocop:disable Lint/RescueException
11
12
  notify_airbrake(exception, context)
13
+ Airbrake.notify_queue(
14
+ queue: context['class'],
15
+ error_count: 1,
16
+ timing: 0.01,
17
+ )
12
18
  raise exception
19
+ else
20
+ Airbrake.notify_queue(
21
+ queue: context['class'],
22
+ error_count: 0,
23
+ timing: timing,
24
+ )
13
25
  end
14
- # rubocop:enable Lint/RescueException
15
26
 
16
27
  private
17
28
 
18
29
  def notify_airbrake(exception, context)
19
- Airbrake.notify(exception, context) do |notice|
30
+ Airbrake.notify(exception, job: context) do |notice|
20
31
  notice[:context][:component] = 'sidekiq'
21
32
  notice[:context][:action] = action(context)
22
33
  end
@@ -25,23 +36,18 @@ module Airbrake
25
36
  # @return [String] job's name. When ActiveJob is present, retrieve
26
37
  # job_class. When used directly, use worker's name
27
38
  def action(context)
28
- klass = context['class'] || context[:job] && context[:job]['class']
39
+ klass = context['class'] || (context[:job] && context[:job]['class'])
29
40
  return klass unless context[:job] && context[:job]['args'].first.is_a?(Hash)
30
41
  return klass unless (job_class = context[:job]['args'].first['job_class'])
42
+
31
43
  job_class
32
44
  end
33
45
  end
34
46
  end
35
47
  end
36
48
 
37
- if Sidekiq::VERSION < '3'
38
- Sidekiq.configure_server do |config|
39
- config.server_middleware do |chain|
40
- chain.add(Airbrake::Sidekiq::ErrorHandler)
41
- end
42
- end
43
- else
44
- Sidekiq.configure_server do |config|
45
- config.error_handlers << Airbrake::Sidekiq::ErrorHandler.new.method(:notify_airbrake)
49
+ Sidekiq.configure_server do |config|
50
+ config.server_middleware do |chain|
51
+ chain.add(Airbrake::Sidekiq::ErrorHandler)
46
52
  end
47
53
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Sneakers
3
5
  # Provides integration with Sneakers.
@@ -10,17 +12,21 @@ module Airbrake
10
12
  # @see https://github.com/airbrake/airbrake/issues/850
11
13
  IGNORED_KEYS = %i[delivery_tag consumer channel].freeze
12
14
 
13
- def call(exception, worker = nil, **context)
15
+ # rubocop:disable Style/OptionalArguments
16
+ def call(exception, worker = nil, context)
17
+ # Later versions add a middle argument.
14
18
  Airbrake.notify(exception, filter_context(context)) do |notice|
15
19
  notice[:context][:component] = 'sneakers'
16
20
  notice[:context][:action] = worker.class.to_s
17
21
  end
18
22
  end
23
+ # rubocop:enable Style/OptionalArguments
19
24
 
20
25
  private
21
26
 
22
27
  def filter_context(context)
23
28
  return context unless context[:delivery_info]
29
+
24
30
  h = context.dup
25
31
  h[:delivery_info] = context[:delivery_info].reject do |k, _v|
26
32
  IGNORED_KEYS.include?(k)
@@ -32,3 +38,35 @@ module Airbrake
32
38
  end
33
39
 
34
40
  Sneakers.error_reporters << Airbrake::Sneakers::ErrorReporter.new
41
+
42
+ module Airbrake
43
+ module Sneakers
44
+ # @todo Migrate to Sneakers v2.12.0 middleware API when it's released
45
+ # @see https://github.com/jondot/sneakers/pull/364
46
+ module Worker
47
+ # Sneakers v2.7.0+ renamed `do_work` to `process_work`.
48
+ define_method(
49
+ ::Sneakers::Worker.method_defined?(:process_work) ? :process_work : :do_work,
50
+ ) do |delivery_info, metadata, msg, handler|
51
+ timing = Airbrake::Benchmark.measure do
52
+ super(delivery_info, metadata, msg, handler)
53
+ end
54
+ rescue Exception => exception # rubocop:disable Lint/RescueException
55
+ Airbrake.notify_queue(
56
+ queue: self.class.to_s,
57
+ error_count: 1,
58
+ timing: 0.01,
59
+ )
60
+ raise exception
61
+ else
62
+ Airbrake.notify_queue(
63
+ queue: self.class.to_s,
64
+ error_count: 0,
65
+ timing: timing,
66
+ )
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ Sneakers::Worker.prepend(Airbrake::Sneakers::Worker)
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # We use Semantic Versioning v2.0.0
2
4
  # More information: http://semver.org/
3
5
  module Airbrake
4
- AIRBRAKE_VERSION = '9.3.0'.freeze
6
+ AIRBRAKE_VERSION = '13.0.3'
5
7
  end
data/lib/airbrake.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'shellwords'
2
4
  require 'English'
3
5