airbrake 9.4.5

Sign up to get free protection for your applications and to get access to all the features.
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