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,28 @@
1
+ require 'airbrake/rails/event'
2
+
3
+ module Airbrake
4
+ module Rails
5
+ # ActionControllerNotifySubscriber sends route stat information, including
6
+ # performance data.
7
+ #
8
+ # @since v8.0.0
9
+ class ActionControllerNotifySubscriber
10
+ def call(*args)
11
+ routes = Airbrake::Rack::RequestStore[:routes]
12
+ return if !routes || routes.none?
13
+
14
+ event = Airbrake::Rails::Event.new(*args)
15
+
16
+ routes.each do |route, _params|
17
+ Airbrake.notify_request(
18
+ method: event.method,
19
+ route: route,
20
+ status_code: event.status_code,
21
+ start_time: event.time,
22
+ end_time: Time.new
23
+ )
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,46 @@
1
+ require 'airbrake/rails/event'
2
+
3
+ module Airbrake
4
+ module Rails
5
+ # @since v8.3.0
6
+ class ActionControllerPerformanceBreakdownSubscriber
7
+ def call(*args)
8
+ routes = Airbrake::Rack::RequestStore[:routes]
9
+ return if !routes || routes.none?
10
+
11
+ event = Airbrake::Rails::Event.new(*args)
12
+ stash = build_stash
13
+
14
+ routes.each do |route, params|
15
+ groups = event.groups.merge(params[:groups])
16
+ next if groups.none?
17
+
18
+ breakdown_info = {
19
+ method: event.method,
20
+ route: route,
21
+ response_type: event.response_type,
22
+ groups: groups,
23
+ start_time: event.time
24
+ }
25
+
26
+ Airbrake.notify_performance_breakdown(breakdown_info, stash)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def build_stash
33
+ stash = {}
34
+ request = Airbrake::Rack::RequestStore[:request]
35
+ return stash unless request
36
+
37
+ stash[:request] = request
38
+ if (user = Airbrake::Rack::User.extract(request.env))
39
+ stash.merge!(user.as_json)
40
+ end
41
+
42
+ stash
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,46 @@
1
+ require 'airbrake/rails/event'
2
+ require 'airbrake/rails/app'
3
+
4
+ module Airbrake
5
+ module Rails
6
+ # @return [String]
7
+ CONTROLLER_KEY = 'controller'.freeze
8
+
9
+ # @return [String]
10
+ ACTION_KEY = 'action'.freeze
11
+
12
+ # ActionControllerRouteSubscriber sends route stat information, including
13
+ # performance data.
14
+ #
15
+ # @since v8.0.0
16
+ class ActionControllerRouteSubscriber
17
+ def initialize
18
+ @app = Airbrake::Rails::App.new
19
+ end
20
+
21
+ def call(*args)
22
+ # We don't track routeless events.
23
+ return unless (routes = Airbrake::Rack::RequestStore[:routes])
24
+
25
+ event = Airbrake::Rails::Event.new(*args)
26
+ route = find_route(event.params)
27
+ return unless route
28
+
29
+ routes[route.path] = {
30
+ method: event.method,
31
+ response_type: event.response_type,
32
+ groups: {}
33
+ }
34
+ end
35
+
36
+ private
37
+
38
+ def find_route(params)
39
+ @app.routes.find do |route|
40
+ route.controller == params[CONTROLLER_KEY] &&
41
+ route.action == params[ACTION_KEY]
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,33 @@
1
+ module Airbrake
2
+ module Rails
3
+ # Enables support for exceptions occurring in ActiveJob jobs.
4
+ module ActiveJob
5
+ extend ActiveSupport::Concern
6
+
7
+ # @return [Array<Regexp>] the list of known adapters
8
+ ADAPTERS = [/Resque/, /Sidekiq/, /DelayedJob/].freeze
9
+
10
+ def self.notify_airbrake(exception, job)
11
+ queue_adapter = job.class.queue_adapter.to_s
12
+
13
+ # Do not notify twice if a queue_adapter is configured already.
14
+ raise exception if ADAPTERS.any? { |a| a =~ queue_adapter }
15
+
16
+ notice = Airbrake.build_notice(exception)
17
+ notice[:context][:component] = 'active_job'
18
+ notice[:context][:action] = job.class.name
19
+ notice[:params].merge!(job.serialize)
20
+
21
+ Airbrake.notify(notice)
22
+
23
+ raise exception
24
+ end
25
+
26
+ included do
27
+ rescue_from(Exception) do |exception|
28
+ Airbrake::Rails::ActiveJob.notify_airbrake(exception, self)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ module Airbrake
2
+ module Rails
3
+ # Rails <4.2 has a bug with regard to swallowing exceptions in the
4
+ # +after_commit+ and the +after_rollback+ hooks: it doesn't bubble up
5
+ # exceptions from there.
6
+ #
7
+ # This module makes it possible to report exceptions occurring there.
8
+ #
9
+ # @see https://github.com/rails/rails/pull/14488 Detailed description of the
10
+ # bug and the fix
11
+ # @see https://goo.gl/348lor Rails 4.2+ implementation (fixed)
12
+ # @see https://goo.gl/ddFNg7 Rails <4.2 implementation (bugged)
13
+ module ActiveRecord
14
+ # Patches default +run_callbacks+ with our version, which is capable of
15
+ # notifying about exceptions.
16
+ #
17
+ # rubocop:disable Lint/RescueException
18
+ def run_callbacks(kind, *args, &block)
19
+ # Let the post process handle the exception if it's not a bugged hook.
20
+ return super unless %i[commit rollback].include?(kind)
21
+
22
+ # Handle the exception ourselves. The 'ex' exception won't be
23
+ # propagated, therefore we must notify it here.
24
+ begin
25
+ super
26
+ rescue Exception => ex
27
+ Airbrake.notify(ex)
28
+ raise ex
29
+ end
30
+ end
31
+ # rubocop:enable Lint/RescueException
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,42 @@
1
+ require 'airbrake/rails/event'
2
+ require 'airbrake/rails/backtrace_cleaner'
3
+
4
+ module Airbrake
5
+ module Rails
6
+ # ActiveRecordSubscriber sends SQL information, including performance data.
7
+ #
8
+ # @since v8.1.0
9
+ class ActiveRecordSubscriber
10
+ def call(*args)
11
+ routes = Airbrake::Rack::RequestStore[:routes]
12
+ return if !routes || routes.none?
13
+
14
+ event = Airbrake::Rails::Event.new(*args)
15
+ frame = last_caller
16
+
17
+ routes.each do |route, params|
18
+ Airbrake.notify_query(
19
+ route: route,
20
+ method: params[:method],
21
+ query: event.sql,
22
+ func: frame[:function],
23
+ file: frame[:file],
24
+ line: frame[:line],
25
+ start_time: event.time,
26
+ end_time: event.end
27
+ )
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def last_caller
34
+ exception = StandardError.new
35
+ exception.set_backtrace(
36
+ Airbrake::Rails::BacktraceCleaner.clean(Kernel.caller)
37
+ )
38
+ Airbrake::Backtrace.parse(exception).first || {}
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,43 @@
1
+ module Airbrake
2
+ module Rails
3
+ # App is a wrapper around Rails.application and Rails::Engine.
4
+ #
5
+ # @since v9.0.3
6
+ # @api private
7
+ class App
8
+ Route = Struct.new(:path, :controller, :action)
9
+
10
+ def routes
11
+ @routes ||= app_routes.merge(engine_routes).flat_map do |(engine_name, routes)|
12
+ routes.map { |rails_route| build_route(engine_name, rails_route) }
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def app_routes
19
+ # Engine name is nil because this is default (non-engine) routes.
20
+ { nil => ::Rails.application.routes.routes.routes }
21
+ end
22
+
23
+ def engine_routes
24
+ ::Rails::Engine.subclasses.flat_map.with_object({}) do |engine, hash|
25
+ next if (eng_routes = engine.routes.routes.routes).none?
26
+
27
+ hash[engine.engine_name] = eng_routes
28
+ end
29
+ end
30
+
31
+ def build_route(engine_name, rails_route)
32
+ engine_prefix = engine_name
33
+ engine_prefix += '#' if engine_prefix
34
+
35
+ Route.new(
36
+ "#{engine_prefix}#{rails_route.path.spec}",
37
+ rails_route.defaults[:controller],
38
+ rails_route.defaults[:action]
39
+ )
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,10 @@
1
+ module Airbrake
2
+ module Rails
3
+ # BacktraceCleaner is a wrapper around Rails.backtrace_cleaner.
4
+ class BacktraceCleaner
5
+ def self.clean(backtrace)
6
+ ::Rails.backtrace_cleaner.clean(backtrace).first(1)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,35 @@
1
+ module Curl
2
+ # Monkey-patch to measure request timing.
3
+ class Easy
4
+ alias http_without_airbrake http
5
+
6
+ def http(verb)
7
+ Airbrake::Rack.capture_timing(:http) do
8
+ http_without_airbrake(verb)
9
+ end
10
+ end
11
+
12
+ alias perform_without_airbrake perform
13
+
14
+ def perform(&block)
15
+ Airbrake::Rack.capture_timing(:http) do
16
+ perform_without_airbrake(&block)
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ module Curl
23
+ # Monkey-patch to measure request timing.
24
+ class Multi
25
+ class << self
26
+ alias http_without_airbrake http
27
+
28
+ def http(urls_with_config, multi_options = {}, &block)
29
+ Airbrake::Rack.capture_timing(:http) do
30
+ http_without_airbrake(urls_with_config, multi_options, &block)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,83 @@
1
+ module Airbrake
2
+ module Rails
3
+ # Event is a wrapper around ActiveSupport::Notifications::Event.
4
+ #
5
+ # @since v9.0.3
6
+ # @api private
7
+ class Event
8
+ # @see https://github.com/rails/rails/issues/8987
9
+ HTML_RESPONSE_WILDCARD = "*/*".freeze
10
+
11
+ include Airbrake::Loggable
12
+
13
+ def initialize(*args)
14
+ @event = ActiveSupport::Notifications::Event.new(*args)
15
+ end
16
+
17
+ def method
18
+ @event.payload[:method]
19
+ end
20
+
21
+ def response_type
22
+ response_type = @event.payload[:format]
23
+ response_type == HTML_RESPONSE_WILDCARD ? :html : response_type
24
+ end
25
+
26
+ def params
27
+ @event.payload[:params]
28
+ end
29
+
30
+ def sql
31
+ @event.payload[:sql]
32
+ end
33
+
34
+ def db_runtime
35
+ @db_runtime ||= @event.payload[:db_runtime] || 0
36
+ end
37
+
38
+ def view_runtime
39
+ @view_runtime ||= @event.payload[:view_runtime] || 0
40
+ end
41
+
42
+ def time
43
+ @event.time
44
+ end
45
+
46
+ def end
47
+ @event.end
48
+ end
49
+
50
+ def groups
51
+ groups = {}
52
+ groups[:db] = db_runtime if db_runtime > 0
53
+ groups[:view] = view_runtime if view_runtime > 0
54
+ groups
55
+ end
56
+
57
+ def status_code
58
+ return @event.payload[:status] if @event.payload[:status]
59
+
60
+ if @event.payload[:exception]
61
+ status = ActionDispatch::ExceptionWrapper.status_code_for_exception(
62
+ @event.payload[:exception].first
63
+ )
64
+ status = 500 if status == 0
65
+
66
+ return status
67
+ end
68
+
69
+ # The ActiveSupport event doesn't have status only in two cases:
70
+ # - an exception was thrown
71
+ # - unauthorized access
72
+ # We have already handled the exception so what's left is unauthorized
73
+ # access. There's no way to know for sure it's unauthorized access, so
74
+ # we are rather optimistic here.
75
+ 401
76
+ end
77
+
78
+ def duration
79
+ @event.duration
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,21 @@
1
+ require 'airbrake/rails/event'
2
+
3
+ module Airbrake
4
+ module Rails
5
+ # @api private
6
+ # @since v9.2.0
7
+ class Excon
8
+ def call(*args)
9
+ routes = Airbrake::Rack::RequestStore[:routes]
10
+ return if !routes || routes.none?
11
+
12
+ event = Airbrake::Rails::Event.new(*args)
13
+
14
+ routes.each do |_route_path, params|
15
+ params[:groups][:http] ||= 0
16
+ params[:groups][:http] += event.duration
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ module HTTP
2
+ # Monkey-patch to measure request timing.
3
+ class Client
4
+ alias perform_without_airbrake perform
5
+
6
+ def perform(request, options)
7
+ Airbrake::Rack.capture_timing(:http) do
8
+ perform_without_airbrake(request, options)
9
+ end
10
+ end
11
+ end
12
+ end