airbrake 9.5.0

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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/lib/airbrake.rb +30 -0
  3. data/lib/airbrake/capistrano.rb +6 -0
  4. data/lib/airbrake/capistrano/capistrano2.rb +38 -0
  5. data/lib/airbrake/capistrano/capistrano3.rb +21 -0
  6. data/lib/airbrake/delayed_job.rb +48 -0
  7. data/lib/airbrake/logger.rb +101 -0
  8. data/lib/airbrake/rack.rb +35 -0
  9. data/lib/airbrake/rack/context_filter.rb +58 -0
  10. data/lib/airbrake/rack/http_headers_filter.rb +42 -0
  11. data/lib/airbrake/rack/http_params_filter.rb +25 -0
  12. data/lib/airbrake/rack/instrumentable.rb +28 -0
  13. data/lib/airbrake/rack/middleware.rb +100 -0
  14. data/lib/airbrake/rack/request_body_filter.rb +31 -0
  15. data/lib/airbrake/rack/request_store.rb +32 -0
  16. data/lib/airbrake/rack/route_filter.rb +53 -0
  17. data/lib/airbrake/rack/session_filter.rb +23 -0
  18. data/lib/airbrake/rack/user.rb +70 -0
  19. data/lib/airbrake/rack/user_filter.rb +23 -0
  20. data/lib/airbrake/rails.rb +32 -0
  21. data/lib/airbrake/rails/action_cable.rb +33 -0
  22. data/lib/airbrake/rails/action_cable/notify_callback.rb +20 -0
  23. data/lib/airbrake/rails/action_controller.rb +35 -0
  24. data/lib/airbrake/rails/action_controller_notify_subscriber.rb +28 -0
  25. data/lib/airbrake/rails/action_controller_performance_breakdown_subscriber.rb +46 -0
  26. data/lib/airbrake/rails/action_controller_route_subscriber.rb +46 -0
  27. data/lib/airbrake/rails/active_job.rb +33 -0
  28. data/lib/airbrake/rails/active_record.rb +34 -0
  29. data/lib/airbrake/rails/active_record_subscriber.rb +42 -0
  30. data/lib/airbrake/rails/app.rb +43 -0
  31. data/lib/airbrake/rails/backtrace_cleaner.rb +10 -0
  32. data/lib/airbrake/rails/curb.rb +35 -0
  33. data/lib/airbrake/rails/event.rb +83 -0
  34. data/lib/airbrake/rails/excon_subscriber.rb +21 -0
  35. data/lib/airbrake/rails/http.rb +12 -0
  36. data/lib/airbrake/rails/http_client.rb +10 -0
  37. data/lib/airbrake/rails/net_http.rb +10 -0
  38. data/lib/airbrake/rails/railtie.rb +141 -0
  39. data/lib/airbrake/rails/typhoeus.rb +12 -0
  40. data/lib/airbrake/rake.rb +63 -0
  41. data/lib/airbrake/rake/tasks.rb +110 -0
  42. data/lib/airbrake/resque.rb +29 -0
  43. data/lib/airbrake/shoryuken.rb +40 -0
  44. data/lib/airbrake/sidekiq.rb +47 -0
  45. data/lib/airbrake/sidekiq/retryable_jobs_filter.rb +51 -0
  46. data/lib/airbrake/sneakers.rb +34 -0
  47. data/lib/airbrake/version.rb +5 -0
  48. data/lib/generators/airbrake_generator.rb +23 -0
  49. data/lib/generators/airbrake_initializer.rb.erb +78 -0
  50. metadata +416 -0
@@ -0,0 +1,10 @@
1
+ # Monkey-patch to measure request timing.
2
+ class HTTPClient
3
+ alias do_get_without_airbrake do_get_block
4
+
5
+ def do_get_block(request, proxy, connection, &block)
6
+ Airbrake::Rack.capture_timing(:http) do
7
+ do_get_without_airbrake(request, proxy, connection, &block)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # Monkey-patch Net::HTTP to benchmark it.
2
+ Net::HTTP.class_eval do
3
+ alias_method :request_without_airbrake, :request
4
+
5
+ def request(request, *args, &block)
6
+ Airbrake::Rack.capture_timing(:http) do
7
+ request_without_airbrake(request, *args, &block)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,141 @@
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, run_once: true) 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
80
+
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)
84
+
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, run_once: true) 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.query_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, run_once: true) 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, run_once: true) 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
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,12 @@
1
+ module Typhoeus
2
+ # Monkey-patch to measure request timing.
3
+ class Request
4
+ alias run_without_airbrake run
5
+
6
+ def run
7
+ Airbrake::Rack.capture_timing(:http) do
8
+ run_without_airbrake
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,63 @@
1
+ # This is not bulletproof, but if this file is executed before a task
2
+ # definition, we can grab tasks descriptions and locations.
3
+ # See: https://goo.gl/ksn6PE
4
+ Rake::TaskManager.record_task_metadata = true
5
+
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
23
+
24
+ private
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
+ )
35
+
36
+ Airbrake.notify_sync(notice)
37
+ end
38
+
39
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize
40
+ def task_info
41
+ info = {}
42
+
43
+ info[:name] = name
44
+ info[:timestamp] = timestamp.to_s
45
+ info[:investigation] = investigation
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?
52
+
53
+ if prerequisite_tasks.any?
54
+ info[:prerequisite_tasks] = prerequisite_tasks.map do |p|
55
+ p.__send__(:task_info)
56
+ end
57
+ end
58
+
59
+ info
60
+ end
61
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize
62
+ end
63
+ end
@@ -0,0 +1,110 @@
1
+ require 'airbrake-ruby'
2
+
3
+ namespace :airbrake do
4
+ desc 'Verify your gem installation by sending a test exception'
5
+ task test: (:environment if defined?(Rails)) do
6
+ raise Airbrake::Error, 'airbrake-ruby is not configured' unless Airbrake.configured?
7
+
8
+ require 'pp'
9
+
10
+ response = Airbrake.notify_sync('Exception from the test Rake task')
11
+ if response['error']
12
+ puts "Error: #{response['error']}"
13
+ elsif response.nil? || response['code']
14
+ puts <<-ERROR.gsub(/^\s+\|/, '')
15
+ |#{response['type']}: #{response['message']} (#{response['code']})
16
+ |
17
+ |Possible problems:
18
+ | 1. Project id/key is incorrect
19
+ | 2. Custom filters ignore the exception we try to send
20
+ | 3. Environment this task runs in is ignored (see `ignored_environments`)
21
+ |
22
+ |If nothing works, please file an issue at: https://github.com/airbrake/airbrake/issues
23
+ ERROR
24
+ elsif response['url']
25
+ puts <<-SUCCESS.gsub(/^\s+\|/, '')
26
+ |A test exception was sent to Airbrake.
27
+ |Find it here: #{response['url']}
28
+ SUCCESS
29
+ else
30
+ puts <<-ERROR.gsub(/^\s+\|/, '')
31
+ |Unexpected error occurred. Response from Airbrake:
32
+ |#{response}
33
+ ERROR
34
+ end
35
+ end
36
+
37
+ desc 'Notify Airbrake of a new deploy'
38
+ task :deploy do
39
+ if defined?(Rails)
40
+ initializer = Rails.root.join('config', 'initializers', 'airbrake.rb')
41
+
42
+ # Avoid loading the environment to speed up the deploy task and try guess
43
+ # the initializer file location.
44
+ if initializer.exist? && !Airbrake.configured?
45
+ load(initializer)
46
+ else
47
+ Rake::Task[:environment].invoke
48
+ end
49
+ end
50
+
51
+ raise Airbrake::Error, 'airbrake-ruby is not configured' unless Airbrake.configured?
52
+
53
+ deploy_params = {
54
+ environment: ENV['ENVIRONMENT'],
55
+ username: ENV['USERNAME'],
56
+ revision: ENV['REVISION'],
57
+ repository: ENV['REPOSITORY'],
58
+ version: ENV['VERSION']
59
+ }
60
+ promise = Airbrake.notify_deploy(deploy_params)
61
+ promise.then do
62
+ puts "The #{deploy_params[:environment]} environment was deployed."
63
+ end
64
+ promise.rescue { |error| abort(error) }
65
+ end
66
+
67
+ desc 'Install a Heroku deploy hook to notify Airbrake of deploys'
68
+ task :install_heroku_deploy_hook do
69
+ app = ENV['HEROKU_APP']
70
+
71
+ config = Bundler.with_clean_env do
72
+ `heroku config --shell#{ " --app #{app}" if app }`
73
+ end
74
+
75
+ heroku_env = config.each_line.with_object({}) do |line, h|
76
+ h.merge!(Hash[*line.rstrip.split("\n").flat_map { |v| v.split('=', 2) }])
77
+ end
78
+
79
+ id = heroku_env['AIRBRAKE_PROJECT_ID']
80
+ key = heroku_env['AIRBRAKE_API_KEY']
81
+
82
+ exit!(1) if [id, key].any?(&:nil?)
83
+
84
+ unless (env = heroku_env['RAILS_ENV'])
85
+ env = 'production'
86
+ puts "Airbrake couldn't identify your app's environment, so the '#{env}'" \
87
+ " environment will be used."
88
+ end
89
+
90
+ unless (repo = ENV['REPOSITORY_URL'])
91
+ repo = `git remote get-url origin 2>/dev/null`.chomp
92
+ if repo.empty?
93
+ puts "Airbrake couldn't identify your app's repository."
94
+ else
95
+ puts "Airbrake couldn't identify your app's repository, so the " \
96
+ "'origin' remote url '#{repo}' will be used."
97
+ end
98
+ end
99
+
100
+ url = "https://airbrake.io/api/v3/projects/#{id}/heroku-deploys?key=#{key}"
101
+ url << "&environment=#{env}"
102
+ url << "&repository=#{repo}" unless repo.empty?
103
+
104
+ command = %(heroku addons:create deployhooks:http --url="#{url}")
105
+ command << " --app #{app}" if app
106
+
107
+ puts "$ #{command}"
108
+ Bundler.with_clean_env { puts `#{command}` }
109
+ end
110
+ end
@@ -0,0 +1,29 @@
1
+ module Resque
2
+ module Failure
3
+ # Provides Resque integration with Airbrake.
4
+ #
5
+ # @since v5.0.0
6
+ # @see https://github.com/resque/resque/wiki/Failure-Backends
7
+ class Airbrake < Base
8
+ def save
9
+ ::Airbrake.notify_sync(exception, payload) do |notice|
10
+ notice[:context][:component] = 'resque'
11
+ notice[:context][:action] = action(payload)
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ # @return [String] job's name. When ActiveJob is present, retrieve
18
+ # job_class. When used directly, use worker's name
19
+ def action(payload)
20
+ active_job_args = payload['args'].first if payload['args']
21
+ if active_job_args.is_a?(Hash) && active_job_args['job_class']
22
+ active_job_args['job_class']
23
+ else
24
+ payload['class'].to_s
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,40 @@
1
+ module Airbrake
2
+ module Shoryuken
3
+ # Provides integration with Shoryuken.
4
+ class ErrorHandler
5
+ # rubocop:disable Lint/RescueException
6
+ def call(worker, queue, _sqs_msg, body)
7
+ yield
8
+ rescue Exception => exception
9
+ notify_airbrake(exception, worker, queue, body)
10
+
11
+ raise exception
12
+ end
13
+ # rubocop:enable Lint/RescueException
14
+
15
+ private
16
+
17
+ def notify_airbrake(exception, worker, queue, body)
18
+ Airbrake.notify(exception, notice_context(queue, body)) do |notice|
19
+ notice[:context][:component] = 'shoryuken'
20
+ notice[:context][:action] = worker.class.to_s
21
+ end
22
+ end
23
+
24
+ def notice_context(queue, body)
25
+ {
26
+ queue: queue,
27
+ body: body.is_a?(Array) ? { batch: body } : { body: body }
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ if defined?(::Shoryuken)
35
+ Shoryuken.configure_server do |config|
36
+ config.server_middleware do |chain|
37
+ chain.add Airbrake::Shoryuken::ErrorHandler
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,47 @@
1
+ require 'airbrake/sidekiq/retryable_jobs_filter'
2
+
3
+ module Airbrake
4
+ module Sidekiq
5
+ # Provides integration with Sidekiq v2+.
6
+ class ErrorHandler
7
+ # rubocop:disable Lint/RescueException
8
+ def call(_worker, context, _queue)
9
+ yield
10
+ rescue Exception => exception
11
+ notify_airbrake(exception, context)
12
+ raise exception
13
+ end
14
+ # rubocop:enable Lint/RescueException
15
+
16
+ private
17
+
18
+ def notify_airbrake(exception, context)
19
+ Airbrake.notify(exception, context) do |notice|
20
+ notice[:context][:component] = 'sidekiq'
21
+ notice[:context][:action] = action(context)
22
+ end
23
+ end
24
+
25
+ # @return [String] job's name. When ActiveJob is present, retrieve
26
+ # job_class. When used directly, use worker's name
27
+ def action(context)
28
+ klass = context['class'] || context[:job] && context[:job]['class']
29
+ return klass unless context[:job] && context[:job]['args'].first.is_a?(Hash)
30
+ return klass unless (job_class = context[:job]['args'].first['job_class'])
31
+ job_class
32
+ end
33
+ end
34
+ end
35
+ end
36
+
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)
46
+ end
47
+ end