airbrake 9.5.5 → 10.0.4

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 +4 -4
  2. data/lib/airbrake.rb +2 -0
  3. data/lib/airbrake/capistrano.rb +2 -0
  4. data/lib/airbrake/capistrano/capistrano2.rb +2 -0
  5. data/lib/airbrake/capistrano/capistrano3.rb +2 -0
  6. data/lib/airbrake/delayed_job.rb +24 -6
  7. data/lib/airbrake/logger.rb +2 -0
  8. data/lib/airbrake/rack.rb +2 -0
  9. data/lib/airbrake/rack/context_filter.rb +9 -2
  10. data/lib/airbrake/rack/http_headers_filter.rb +4 -2
  11. data/lib/airbrake/rack/http_params_filter.rb +2 -0
  12. data/lib/airbrake/rack/instrumentable.rb +112 -4
  13. data/lib/airbrake/rack/middleware.rb +3 -1
  14. data/lib/airbrake/rack/request_body_filter.rb +2 -0
  15. data/lib/airbrake/rack/request_store.rb +2 -0
  16. data/lib/airbrake/rack/route_filter.rb +2 -0
  17. data/lib/airbrake/rack/session_filter.rb +2 -0
  18. data/lib/airbrake/rack/user.rb +2 -0
  19. data/lib/airbrake/rack/user_filter.rb +2 -0
  20. data/lib/airbrake/rails.rb +3 -1
  21. data/lib/airbrake/rails/action_cable.rb +2 -0
  22. data/lib/airbrake/rails/action_cable/notify_callback.rb +2 -0
  23. data/lib/airbrake/rails/action_controller.rb +2 -0
  24. data/lib/airbrake/rails/action_controller_notify_subscriber.rb +4 -2
  25. data/lib/airbrake/rails/action_controller_performance_breakdown_subscriber.rb +4 -1
  26. data/lib/airbrake/rails/action_controller_route_subscriber.rb +4 -2
  27. data/lib/airbrake/rails/active_job.rb +25 -8
  28. data/lib/airbrake/rails/active_record.rb +2 -0
  29. data/lib/airbrake/rails/active_record_subscriber.rb +5 -3
  30. data/lib/airbrake/rails/app.rb +43 -15
  31. data/lib/airbrake/rails/backtrace_cleaner.rb +13 -0
  32. data/lib/airbrake/rails/curb.rb +2 -0
  33. data/lib/airbrake/rails/event.rb +3 -5
  34. data/lib/airbrake/rails/excon_subscriber.rb +2 -0
  35. data/lib/airbrake/rails/http.rb +2 -0
  36. data/lib/airbrake/rails/http_client.rb +2 -0
  37. data/lib/airbrake/rails/net_http.rb +14 -6
  38. data/lib/airbrake/rails/railtie.rb +11 -9
  39. data/lib/airbrake/rails/typhoeus.rb +2 -0
  40. data/lib/airbrake/rake.rb +3 -1
  41. data/lib/airbrake/rake/tasks.rb +6 -4
  42. data/lib/airbrake/resque.rb +32 -0
  43. data/lib/airbrake/shoryuken.rb +17 -3
  44. data/lib/airbrake/sidekiq.rb +20 -13
  45. data/lib/airbrake/sidekiq/retryable_jobs_filter.rb +2 -0
  46. data/lib/airbrake/sneakers.rb +38 -1
  47. data/lib/airbrake/version.rb +3 -1
  48. data/lib/generators/airbrake_generator.rb +2 -0
  49. data/lib/generators/airbrake_initializer.rb.erb +2 -0
  50. metadata +19 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 90f7f39f6012a7e8383f50d1d99c8631582032feddaaa5a452bb7ba9afc40fc6
4
- data.tar.gz: 728831ea6a9a57815fec54ac507f4712923bcfe5b012b31658ddd0c1b4d576ba
3
+ metadata.gz: d1bea1ce3705909bcf1406466ceab591c1c58aa138cd274c820a20b7d52e3bd1
4
+ data.tar.gz: bb33d7d3fe68fc0ef1c8e65d9eccb9e3b663bb88440a53bd36b4ee5e8841ce00
5
5
  SHA512:
6
- metadata.gz: d899543189d807cbc393fba4643274eea3b02134fa09aec828926dc2dced70adef3071b36092fa0f3ba9809217b3c74aa8e6d25849c22cfcc88640a6d257c85b
7
- data.tar.gz: ca0c24bbd41fdd983a128763c99a3ce43aef445266d71606d54263782894e9a4987dde638c08083eb037ae6cae322d51ba1f42e4b690422478f4c3550cec817b
6
+ metadata.gz: 37aa7af6de420774bc83c9af305591536ca98331b5ec695b30fc18e503cfe9ec14bd27a8923906f6cf83ada31b5e3536b0f075308988aee35dbec32a0a330a81
7
+ data.tar.gz: 6dc006e4d19670905ec22f8c843adbaa2d59c2c369264c590ce73178c5dbccf75848062c18a1132e4a944f6766a37a94a4d5b1d0e3c4be429df47d55718c9079
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'shellwords'
2
4
  require 'English'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  if defined?(Capistrano::VERSION) &&
2
4
  Gem::Version.new(Capistrano::VERSION).release >= Gem::Version.new('3.0.0')
3
5
  require 'airbrake/capistrano/capistrano3'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  # The Capistrano v2 integration.
3
5
  module Capistrano
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  namespace :airbrake do
2
4
  desc "Notify Airbrake of the deploy"
3
5
  task :deploy do
@@ -1,14 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Delayed
2
4
  module Plugins
3
5
  # Provides integration with Delayed Job.
4
- # rubocop:disable Lint/RescueException
6
+ # rubocop:disable Metrics/BlockLength
5
7
  class Airbrake < ::Delayed::Plugin
6
8
  callbacks do |lifecycle|
7
9
  lifecycle.around(:invoke_job) do |job, *args, &block|
8
10
  begin
9
- # Forward the call to the next callback in the callback chain
10
- block.call(job, *args)
11
- rescue Exception => exception
11
+ timing = ::Airbrake::Benchmark.measure do
12
+ # Forward the call to the next callback in the callback chain
13
+ block.call(job, *args)
14
+ end
15
+ rescue Exception => exception # rubocop:disable Lint/RescueException
12
16
  params = job.as_json
13
17
 
14
18
  # If DelayedJob is used through ActiveJob, it contains extra info.
@@ -17,17 +21,31 @@ module Delayed
17
21
  job_class = job.payload_object.job_data['job_class']
18
22
  end
19
23
 
24
+ action = job_class || job.payload_object.class.name
25
+
20
26
  ::Airbrake.notify(exception, params) do |notice|
21
27
  notice[:context][:component] = 'delayed_job'
22
- notice[:context][:action] = job_class || job.payload_object.class.name
28
+ notice[:context][:action] = action
23
29
  end
24
30
 
31
+ ::Airbrake.notify_queue(
32
+ queue: action,
33
+ error_count: 1,
34
+ timing: 0.01,
35
+ )
36
+
25
37
  raise exception
38
+ else
39
+ ::Airbrake.notify_queue(
40
+ queue: job_class || job.payload_object.class.name,
41
+ error_count: 0,
42
+ timing: timing,
43
+ )
26
44
  end
27
45
  end
28
46
  end
29
47
  end
30
- # rubocop:enable Lint/RescueException
48
+ # rubocop:enable Metrics/BlockLength
31
49
  end
32
50
  end
33
51
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
  require 'delegate'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'airbrake/rack/user'
2
4
  require 'airbrake/rack/user_filter'
3
5
  require 'airbrake/rack/context_filter'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rack
3
5
  # Adds context (URL, User-Agent, framework version, controller and more).
@@ -18,7 +20,12 @@ module Airbrake
18
20
  context = notice[:context]
19
21
 
20
22
  context[:url] = request.url
21
- context[:userAddr] = request.ip
23
+ context[:userAddr] =
24
+ if request.respond_to?(:remote_ip)
25
+ request.remote_ip
26
+ else
27
+ request.ip
28
+ end
22
29
  context[:userAgent] = request.user_agent
23
30
 
24
31
  add_framework_version(context)
@@ -49,7 +56,7 @@ module Airbrake
49
56
  else
50
57
  {
51
58
  'rack_version' => ::Rack.version,
52
- 'rack_release' => ::Rack.release
59
+ 'rack_release' => ::Rack.release,
53
60
  }
54
61
  end
55
62
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rack
3
5
  # Adds HTTP request parameters.
@@ -9,7 +11,7 @@ module Airbrake
9
11
  HTTP_HEADER_PREFIXES = [
10
12
  'HTTP_'.freeze,
11
13
  'CONTENT_TYPE'.freeze,
12
- 'CONTENT_LENGTH'.freeze
14
+ 'CONTENT_LENGTH'.freeze,
13
15
  ].freeze
14
16
 
15
17
  # @return [Integer]
@@ -34,7 +36,7 @@ module Airbrake
34
36
  notice[:context].merge!(
35
37
  httpMethod: request.request_method,
36
38
  referer: request.referer,
37
- headers: http_headers
39
+ headers: http_headers,
38
40
  )
39
41
  end
40
42
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rack
3
5
  # Adds HTTP request parameters.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rack
3
5
  # Instrumentable holds methods that simplify instrumenting Rack apps.
@@ -15,14 +17,120 @@ module Airbrake
15
17
  # @since v9.2.0
16
18
  module Instrumentable
17
19
  def airbrake_capture_timing(method_name, label: method_name.to_s)
18
- alias_method "#{method_name}_without_airbrake", method_name
20
+ instrumentable = ::Airbrake::Rack::Instrumentable
21
+ if instrumentable.should_prepend?(self, method_name)
22
+ instrumentable.prepend_capture_timing(self, method_name, label)
23
+ else
24
+ instrumentable.chain_capture_timing(self, method_name, label)
25
+ end
26
+ method_name
27
+ end
28
+
29
+ # @api private
30
+ def __airbrake_capture_timing_module__
31
+ # Module used to store prepended wrapper methods, saved as an instance
32
+ # variable so each target class/module gets its own module. This just
33
+ # a convenience to avoid prepending a lot of anonymous modules.
34
+ @__airbrake_capture_timing_module__ ||= ::Module.new
35
+ end
36
+ private :__airbrake_capture_timing_module__
37
+
38
+ # Using api private self methods so they don't get defined in the target
39
+ # class or module, but can still be called by the above method.
40
+
41
+ # @api private
42
+ def self.should_prepend?(klass, method_name)
43
+ # Don't chain already-prepended or operator methods.
44
+ klass.module_exec do
45
+ self_class_idx = ancestors.index(self)
46
+ method_owner_idx = ancestors.index(instance_method(method_name).owner)
47
+ method_owner_idx < self_class_idx || !(/\A\W/ =~ method_name).nil?
48
+ end
49
+ end
50
+
51
+ # @api private
52
+ def self.prepend_capture_timing(klass, method_name, label)
53
+ args = method_signature
54
+ visibility = method_visibility(klass, method_name)
55
+
56
+ # Generate the wrapper method.
57
+ klass.module_exec do
58
+ mod = __airbrake_capture_timing_module__
59
+ mod.module_exec do
60
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
61
+ def #{method_name}(#{args})
62
+ Airbrake::Rack.capture_timing(#{label.to_s.inspect}) do
63
+ super
64
+ end
65
+ end
66
+ #{visibility} :#{method_name}
67
+ RUBY
68
+ end
69
+ prepend mod
70
+ end
71
+ end
72
+
73
+ # @api private
74
+ def self.chain_capture_timing(klass, method_name, label)
75
+ args = method_signature
76
+ visibility = method_visibility(klass, method_name)
19
77
 
20
- define_method(method_name) do |*args|
21
- Airbrake::Rack.capture_timing(label) do
22
- __send__("#{method_name}_without_airbrake", *args)
78
+ # Generate the wrapper method.
79
+ aliased = method_name.to_s.sub(/([?!=])$/, '')
80
+ punctuation = Regexp.last_match(1)
81
+ wrapped_method_name = "#{aliased}_without_airbrake#{punctuation}"
82
+ needs_removal = method_needs_removal(klass, method_name)
83
+ klass.module_exec do
84
+ alias_method wrapped_method_name, method_name
85
+ remove_method method_name if needs_removal
86
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
87
+ def #{method_name}(#{args})
88
+ Airbrake::Rack.capture_timing(#{label.to_s.inspect}) do
89
+ __send__("#{aliased}_without_airbrake#{punctuation}", #{args})
90
+ end
91
+ end
92
+ #{visibility} :#{method_name}
93
+ RUBY
94
+ end
95
+ end
96
+
97
+ # @api private
98
+ def self.method_visibility(klass, method_name)
99
+ klass.module_exec do
100
+ if protected_method_defined?(method_name)
101
+ "protected".freeze
102
+ elsif private_method_defined?(method_name)
103
+ "private".freeze
104
+ else
105
+ "public".freeze
23
106
  end
24
107
  end
25
108
  end
109
+
110
+ # @api private
111
+ # A method instead of a constant so it isn't accessible in the target.
112
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7")
113
+ def self.method_signature
114
+ "*args, **kw_args, &block".freeze
115
+ end
116
+ else
117
+ def self.method_signature
118
+ "*args, &block".freeze
119
+ end
120
+ end
121
+
122
+ # @api private
123
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.6")
124
+ def self.method_needs_removal(klass, method_name)
125
+ klass.method_defined?(method_name, false) ||
126
+ klass.private_method_defined?(method_name, false)
127
+ end
128
+ else
129
+ def self.method_needs_removal(klass, method_name)
130
+ klass.instance_methods(false).include?(method_name) ||
131
+ klass.private_instance_methods(false).include?(method_name)
132
+ end
133
+ end
26
134
  end
27
135
  end
28
136
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rack
3
5
  # Airbrake Rack middleware for Rails and Sinatra applications (or any other
@@ -94,7 +96,7 @@ end
94
96
  Airbrake::Rack::SessionFilter,
95
97
  Airbrake::Rack::HttpParamsFilter,
96
98
  Airbrake::Rack::HttpHeadersFilter,
97
- Airbrake::Rack::RouteFilter
99
+ Airbrake::Rack::RouteFilter,
98
100
  ].each do |filter|
99
101
  Airbrake.add_filter(filter.new)
100
102
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rack
3
5
  # A filter that appends Rack request body to the notice.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rack
3
5
  # RequestStore is a thin (and limited) wrapper around *Thread.current* that
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'airbrake/rails/app'
2
4
 
3
5
  module Airbrake
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rack
3
5
  # Adds HTTP session.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rack
3
5
  # Represents an authenticated user, which can be converted to Airbrake's
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rack
3
5
  # Adds current user information.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'airbrake/rails/railtie'
2
4
 
3
5
  module Airbrake
@@ -12,7 +14,7 @@ module Airbrake
12
14
 
13
15
  # Rails.logger is not set in some Rake tasks such as
14
16
  # 'airbrake:deploy'. In this case we use a sensible fallback.
15
- level: (::Rails.logger ? ::Rails.logger.level : Logger::ERROR)
17
+ level: (::Rails.logger ? ::Rails.logger.level : Logger::ERROR),
16
18
  )
17
19
  end
18
20
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'airbrake/rails/action_cable/notify_callback'
2
4
 
3
5
  %i[subscribe unsubscribe].each do |callback_name|
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rails
3
5
  module ActionCable
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rails
3
5
  # Contains helper methods that can be used inside Rails controllers to send
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'airbrake/rails/event'
2
4
 
3
5
  module Airbrake
@@ -18,8 +20,8 @@ module Airbrake
18
20
  method: event.method,
19
21
  route: route,
20
22
  status_code: event.status_code,
21
- start_time: event.time,
22
- end_time: Time.new
23
+ timing: event.duration,
24
+ time: event.time,
23
25
  )
24
26
  end
25
27
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'airbrake/rails/event'
2
4
 
3
5
  module Airbrake
@@ -20,7 +22,8 @@ module Airbrake
20
22
  route: route,
21
23
  response_type: event.response_type,
22
24
  groups: groups,
23
- start_time: event.time
25
+ timing: event.duration,
26
+ time: event.time,
24
27
  }
25
28
 
26
29
  Airbrake.notify_performance_breakdown(breakdown_info, stash)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'airbrake/rails/event'
2
4
  require 'airbrake/rails/app'
3
5
 
@@ -14,14 +16,14 @@ module Airbrake
14
16
 
15
17
  event = Airbrake::Rails::Event.new(*args)
16
18
  route = Airbrake::Rails::App.recognize_route(
17
- Airbrake::Rack::RequestStore[:request]
19
+ Airbrake::Rack::RequestStore[:request],
18
20
  )
19
21
  return unless route
20
22
 
21
23
  routes[route.path] = {
22
24
  method: event.method,
23
25
  response_type: event.response_type,
24
- groups: {}
26
+ groups: {},
25
27
  }
26
28
  end
27
29
  end
@@ -1,18 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rails
3
5
  # Enables support for exceptions occurring in ActiveJob jobs.
4
6
  module ActiveJob
5
7
  extend ActiveSupport::Concern
6
8
 
7
- # @return [Array<Regexp>] the list of known adapters
8
- ADAPTERS = [/Resque/, /Sidekiq/, /DelayedJob/].freeze
9
-
10
9
  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
10
  notice = Airbrake.build_notice(exception)
17
11
  notice[:context][:component] = 'active_job'
18
12
  notice[:context][:action] = job.class.name
@@ -23,10 +17,33 @@ module Airbrake
23
17
  raise exception
24
18
  end
25
19
 
20
+ def self.perform(job, block)
21
+ timing = Airbrake::Benchmark.measure do
22
+ block.call
23
+ end
24
+ rescue StandardError => exception
25
+ Airbrake.notify_queue(
26
+ queue: job.class.name,
27
+ error_count: 1,
28
+ timing: 0.01,
29
+ )
30
+ raise exception
31
+ else
32
+ Airbrake.notify_queue(
33
+ queue: job.class.name,
34
+ error_count: 0,
35
+ timing: timing,
36
+ )
37
+ end
38
+
26
39
  included do
27
40
  rescue_from(Exception) do |exception|
28
41
  Airbrake::Rails::ActiveJob.notify_airbrake(exception, self)
29
42
  end
43
+
44
+ around_perform do |job, block|
45
+ Airbrake::Rails::ActiveJob.perform(job, block)
46
+ end
30
47
  end
31
48
  end
32
49
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rails
3
5
  # Rails <4.2 has a bug with regard to swallowing exceptions in the
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'airbrake/rails/event'
2
4
  require 'airbrake/rails/backtrace_cleaner'
3
5
 
@@ -22,8 +24,8 @@ module Airbrake
22
24
  func: frame[:function],
23
25
  file: frame[:file],
24
26
  line: frame[:line],
25
- start_time: event.time,
26
- end_time: event.end
27
+ timing: event.duration,
28
+ time: event.time,
27
29
  )
28
30
  end
29
31
  end
@@ -33,7 +35,7 @@ module Airbrake
33
35
  def last_caller
34
36
  exception = StandardError.new
35
37
  exception.set_backtrace(
36
- Airbrake::Rails::BacktraceCleaner.clean(Kernel.caller)
38
+ Airbrake::Rails::BacktraceCleaner.clean(Kernel.caller),
37
39
  )
38
40
  Airbrake::Backtrace.parse(exception).first || {}
39
41
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rails
3
5
  # App is a wrapper around Rails.application.
@@ -13,26 +15,52 @@ module Airbrake
13
15
  # Duplicate `request` because `recognize` *can* strip the request's
14
16
  # `path_info`, which results in broken engine links (when the engine has
15
17
  # an isolated namespace).
16
- ::Rails.application.routes.router.recognize(request.dup) do |route, _params|
17
- path =
18
- if route.app.respond_to?(:app) && route.app.app.respond_to?(:engine_name)
19
- "#{route.app.app.engine_name}##{route.path.spec}"
20
- else
21
- route.path.spec.to_s
22
- end
18
+ request_copy = request.dup
19
+
20
+ # We must search every engine individually to find a concrete route. If
21
+ # we rely only on the `Rails.application.routes.router`, then the
22
+ # recognize call would return the root route, neglecting PATH_INFO
23
+ # completely. For example:
24
+ # * a request is made to `marketing#pricing`
25
+ # * `Rails.application` recognizes it as `marketing#/` (incorrect)
26
+ # * `Marketing::Engine` recognizes it as `marketing#/pricing` (correct)
27
+ engines.each do |engine|
28
+ engine.routes.router.recognize(request_copy) do |route, _params|
29
+ # Skip "catch-all" routes such as:
30
+ # get '*path => 'pages#about'
31
+ #
32
+ # @todo The `glob?` method was added in Rails v4.2.0.beta1. We
33
+ # should remove the `respond_to?` check once we drop old Rails
34
+ # versions support.
35
+ #
36
+ # https://github.com/rails/rails/commit/5460591f0226a9d248b7b4f89186bd5553e7768f
37
+ next if route.respond_to?(:glob?) && route.glob?
38
+
39
+ path =
40
+ if engine == ::Rails.application
41
+ route.path.spec.to_s
42
+ else
43
+ "#{engine.engine_name}##{route.path.spec}"
44
+ end
23
45
 
24
- # Rails can recognize multiple routes for the given request. For
25
- # example, if we visit /users/2/edit, then Rails sees these routes:
26
- # * "/users/:id/edit(.:format)"
27
- # * "/"
28
- #
29
- # We return the first route as, what it seems, the most optimal
30
- # approach.
31
- return Route.new(path)
46
+ # Rails can recognize multiple routes for the given request. For
47
+ # example, if we visit /users/2/edit, then Rails sees these routes:
48
+ # * "/users/:id/edit(.:format)"
49
+ # * "/"
50
+ #
51
+ # We return the first route as, what it seems, the most optimal
52
+ # approach.
53
+ return Route.new(path)
54
+ end
32
55
  end
33
56
 
34
57
  nil
35
58
  end
59
+
60
+ def self.engines
61
+ @engines ||= [*::Rails::Engine.subclasses, ::Rails.application]
62
+ end
63
+ private_class_method :engines
36
64
  end
37
65
  end
38
66
  end
@@ -1,10 +1,23 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rails
3
5
  # BacktraceCleaner is a wrapper around Rails.backtrace_cleaner.
4
6
  class BacktraceCleaner
7
+ # @return [Regexp]
8
+ AIRBRAKE_FRAME_PATTERN = %r{/airbrake/lib/airbrake/}
9
+
5
10
  def self.clean(backtrace)
6
11
  ::Rails.backtrace_cleaner.clean(backtrace).first(1)
7
12
  end
8
13
  end
9
14
  end
10
15
  end
16
+
17
+ if defined?(Rails)
18
+ # Silence own frames to let the cleaner proceed to the next line (and probably
19
+ # find the correct call-site coming from the app code rather this library).
20
+ Rails.backtrace_cleaner.add_silencer do |line|
21
+ line =~ Airbrake::Rails::BacktraceCleaner::AIRBRAKE_FRAME_PATTERN
22
+ end
23
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Curl
2
4
  # Monkey-patch to measure request timing.
3
5
  class Easy
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rails
3
5
  # Event is a wrapper around ActiveSupport::Notifications::Event.
@@ -43,10 +45,6 @@ module Airbrake
43
45
  @event.time
44
46
  end
45
47
 
46
- def end
47
- @event.end
48
- end
49
-
50
48
  def groups
51
49
  groups = {}
52
50
  groups[:db] = db_runtime if db_runtime > 0
@@ -59,7 +57,7 @@ module Airbrake
59
57
 
60
58
  if @event.payload[:exception]
61
59
  status = ActionDispatch::ExceptionWrapper.status_code_for_exception(
62
- @event.payload[:exception].first
60
+ @event.payload[:exception].first,
63
61
  )
64
62
  status = 500 if status == 0
65
63
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'airbrake/rails/event'
2
4
 
3
5
  module Airbrake
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module HTTP
2
4
  # Monkey-patch to measure request timing.
3
5
  class Client
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Monkey-patch to measure request timing.
2
4
  class HTTPClient
3
5
  alias do_get_without_airbrake do_get_block
@@ -1,10 +1,18 @@
1
- # Monkey-patch Net::HTTP to benchmark it.
2
- Net::HTTP.class_eval do
3
- alias_method :request_without_airbrake, :request
1
+ # frozen_string_literal: true
4
2
 
5
- def request(request, *args, &block)
6
- Airbrake::Rack.capture_timing(:http) do
7
- request_without_airbrake(request, *args, &block)
3
+ module Airbrake
4
+ module Rails
5
+ # Monkey-patch Net::HTTP to benchmark it.
6
+ # @api private
7
+ # @since v10.0.2
8
+ module NetHttp
9
+ def request(request, *args, &block)
10
+ Airbrake::Rack.capture_timing(:http) do
11
+ super(request, *args, &block)
12
+ end
13
+ end
8
14
  end
9
15
  end
10
16
  end
17
+
18
+ Net::HTTP.prepend(Airbrake::Rails::NetHttp)
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  module Rails
3
5
  # This railtie works for any Rails application that supports railties (Rails
4
6
  # 3.2+ apps). It makes Airbrake Ruby work with Rails and report errors
5
7
  # occurring in the application automatically.
6
8
  class Railtie < ::Rails::Railtie
7
- initializer('airbrake.middleware') do |app|
9
+ initializer('airbrake.middleware', after: :load_config_initializers) do |app|
8
10
  # Since Rails 3.2 the ActionDispatch::DebugExceptions middleware is
9
11
  # responsible for logging exceptions and showing a debugging page in
10
12
  # case the request is local. We want to insert our middleware after
@@ -16,20 +18,20 @@ module Airbrake
16
18
  # exist in Rails 5 anymore.
17
19
  app.config.middleware.insert_after(
18
20
  ActionDispatch::DebugExceptions,
19
- Airbrake::Rack::Middleware
21
+ Airbrake::Rack::Middleware,
20
22
  )
21
23
  elsif defined?(::ActiveRecord::ConnectionAdapters::ConnectionManagement)
22
24
  # Insert after ConnectionManagement to avoid DB connection leakage:
23
25
  # https://github.com/airbrake/airbrake/pull/568
24
26
  app.config.middleware.insert_after(
25
27
  ::ActiveRecord::ConnectionAdapters::ConnectionManagement,
26
- 'Airbrake::Rack::Middleware'
28
+ 'Airbrake::Rack::Middleware',
27
29
  )
28
30
  else
29
31
  # Insert after DebugExceptions for apps without ActiveRecord.
30
32
  app.config.middleware.insert_after(
31
33
  ActionDispatch::DebugExceptions,
32
- 'Airbrake::Rack::Middleware'
34
+ 'Airbrake::Rack::Middleware',
33
35
  )
34
36
  end
35
37
  end
@@ -55,21 +57,21 @@ module Airbrake
55
57
  require 'airbrake/rails/action_controller_route_subscriber'
56
58
  ActiveSupport::Notifications.subscribe(
57
59
  'start_processing.action_controller',
58
- Airbrake::Rails::ActionControllerRouteSubscriber.new
60
+ Airbrake::Rails::ActionControllerRouteSubscriber.new,
59
61
  )
60
62
 
61
63
  # Send route stats.
62
64
  require 'airbrake/rails/action_controller_notify_subscriber'
63
65
  ActiveSupport::Notifications.subscribe(
64
66
  'process_action.action_controller',
65
- Airbrake::Rails::ActionControllerNotifySubscriber.new
67
+ Airbrake::Rails::ActionControllerNotifySubscriber.new,
66
68
  )
67
69
 
68
70
  # Send performance breakdown: where a request spends its time.
69
71
  require 'airbrake/rails/action_controller_performance_breakdown_subscriber'
70
72
  ActiveSupport::Notifications.subscribe(
71
73
  'process_action.action_controller',
72
- Airbrake::Rails::ActionControllerPerformanceBreakdownSubscriber.new
74
+ Airbrake::Rails::ActionControllerPerformanceBreakdownSubscriber.new,
73
75
  )
74
76
 
75
77
  require 'airbrake/rails/net_http' if defined?(Net) && defined?(Net::HTTP)
@@ -112,8 +114,8 @@ module Airbrake
112
114
  # Filter out parameters from SQL body.
113
115
  Airbrake.add_performance_filter(
114
116
  Airbrake::Filters::SqlFilter.new(
115
- ::ActiveRecord::Base.connection_config[:adapter]
116
- )
117
+ ::ActiveRecord::Base.connection_config[:adapter],
118
+ ),
117
119
  )
118
120
  end
119
121
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Typhoeus
2
4
  # Monkey-patch to measure request timing.
3
5
  class Request
@@ -1,3 +1,5 @@
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
@@ -30,7 +32,7 @@ module Rake
30
32
  notice[:params].merge!(
31
33
  rake_task: task_info,
32
34
  execute_args: args,
33
- argv: ARGV.join(' ')
35
+ argv: ARGV.join(' '),
34
36
  )
35
37
 
36
38
  Airbrake.notify_sync(notice)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'airbrake-ruby'
2
4
 
3
5
  namespace :airbrake do
@@ -55,7 +57,7 @@ namespace :airbrake do
55
57
  username: ENV['USERNAME'],
56
58
  revision: ENV['REVISION'],
57
59
  repository: ENV['REPOSITORY'],
58
- version: ENV['VERSION']
60
+ version: ENV['VERSION'],
59
61
  }
60
62
  promise = Airbrake.notify_deploy(deploy_params)
61
63
  promise.then do
@@ -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
@@ -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,33 @@ module Resque
27
29
  end
28
30
  end
29
31
  end
32
+
33
+ module Resque
34
+ # Measures elapsed time of a job and notifies Airbrake of the execution
35
+ # status.
36
+ #
37
+ # @since v9.6.0
38
+ class Job
39
+ # Store the original method to use it later.
40
+ alias perform_without_airbrake perform
41
+
42
+ def perform
43
+ timing = Airbrake::Benchmark.measure do
44
+ perform_without_airbrake
45
+ end
46
+ rescue StandardError => exception
47
+ Airbrake.notify_queue_sync(
48
+ queue: payload['class'],
49
+ error_count: 1,
50
+ timing: 0.01,
51
+ )
52
+ raise exception
53
+ else
54
+ Airbrake.notify_queue_sync(
55
+ queue: payload['class'],
56
+ error_count: 0,
57
+ timing: timing,
58
+ )
59
+ end
60
+ end
61
+ end
@@ -1,14 +1,28 @@
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
8
  def call(worker, queue, _sqs_msg, body)
7
- yield
9
+ timing = Airbrake::Benchmark.measure do
10
+ yield
11
+ end
8
12
  rescue Exception => exception
9
13
  notify_airbrake(exception, worker, queue, body)
10
-
14
+ Airbrake.notify_queue(
15
+ queue: worker.class.to_s,
16
+ error_count: 1,
17
+ timing: 0.01,
18
+ )
11
19
  raise exception
20
+ else
21
+ Airbrake.notify_queue(
22
+ queue: worker.class.to_s,
23
+ error_count: 0,
24
+ timing: timing,
25
+ )
12
26
  end
13
27
  # rubocop:enable Lint/RescueException
14
28
 
@@ -24,7 +38,7 @@ module Airbrake
24
38
  def notice_context(queue, body)
25
39
  {
26
40
  queue: queue,
27
- body: body.is_a?(Array) ? { batch: body } : { body: body }
41
+ body: body.is_a?(Array) ? { batch: body } : { body: body },
28
42
  }
29
43
  end
30
44
  end
@@ -1,17 +1,30 @@
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
9
  def call(_worker, context, _queue)
9
- yield
10
- rescue Exception => exception
10
+ timing = Airbrake::Benchmark.measure do
11
+ yield
12
+ end
13
+ rescue Exception => exception # rubocop:disable Lint/RescueException
11
14
  notify_airbrake(exception, context)
15
+ Airbrake.notify_queue(
16
+ queue: context['class'],
17
+ error_count: 1,
18
+ timing: 0.01,
19
+ )
12
20
  raise exception
21
+ else
22
+ Airbrake.notify_queue(
23
+ queue: context['class'],
24
+ error_count: 0,
25
+ timing: timing,
26
+ )
13
27
  end
14
- # rubocop:enable Lint/RescueException
15
28
 
16
29
  private
17
30
 
@@ -34,14 +47,8 @@ module Airbrake
34
47
  end
35
48
  end
36
49
 
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)
50
+ Sidekiq.configure_server do |config|
51
+ config.server_middleware do |chain|
52
+ chain.add(Airbrake::Sidekiq::ErrorHandler)
46
53
  end
47
54
  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
@@ -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,12 +12,15 @@ 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
 
@@ -32,3 +37,35 @@ module Airbrake
32
37
  end
33
38
 
34
39
  Sneakers.error_reporters << Airbrake::Sneakers::ErrorReporter.new
40
+
41
+ module Sneakers
42
+ # @todo Migrate to Sneakers v2.12.0 middleware API when it's released
43
+ # @see https://github.com/jondot/sneakers/pull/364
44
+ module Worker
45
+ # Sneakers v2.7.0+ renamed `do_work` to `process_work`.
46
+ if method_defined?(:process_work)
47
+ alias process_work_without_airbrake process_work
48
+ else
49
+ alias process_work_without_airbrake do_work
50
+ end
51
+
52
+ def process_work(delivery_info, metadata, msg, handler)
53
+ timing = Airbrake::Benchmark.measure do
54
+ process_work_without_airbrake(delivery_info, metadata, msg, handler)
55
+ end
56
+ rescue Exception => exception # rubocop:disable Lint/RescueException
57
+ Airbrake.notify_queue(
58
+ queue: self.class.to_s,
59
+ error_count: 1,
60
+ timing: 0.01,
61
+ )
62
+ raise exception
63
+ else
64
+ Airbrake.notify_queue(
65
+ queue: self.class.to_s,
66
+ error_count: 0,
67
+ timing: timing,
68
+ )
69
+ end
70
+ end
71
+ end
@@ -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.5.5'.freeze
6
+ AIRBRAKE_VERSION = '10.0.4'.freeze
5
7
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Creates the Airbrake initializer file for Rails apps.
2
4
  #
3
5
  # @example Invokation from terminal
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Airbrake is an online tool that provides robust exception tracking in your Rails
2
4
  # applications. In doing so, it allows you to easily review errors, tie an error
3
5
  # to an individual piece of code, and trace the cause back to recent
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: airbrake
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.5.5
4
+ version: 10.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Airbrake Technologies, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-02 00:00:00.000000000 Z
11
+ date: 2020-05-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: airbrake-ruby
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '4.8'
19
+ version: '4.13'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '4.8'
26
+ version: '4.13'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -254,6 +254,20 @@ dependencies:
254
254
  - - '='
255
255
  - !ruby/object:Gem::Version
256
256
  version: 1.9.0
257
+ - !ruby/object:Gem::Dependency
258
+ name: redis-namespace
259
+ requirement: !ruby/object:Gem::Requirement
260
+ requirements:
261
+ - - '='
262
+ - !ruby/object:Gem::Version
263
+ version: 1.6.0
264
+ type: :development
265
+ prerelease: false
266
+ version_requirements: !ruby/object:Gem::Requirement
267
+ requirements:
268
+ - - '='
269
+ - !ruby/object:Gem::Version
270
+ version: 1.6.0
257
271
  - !ruby/object:Gem::Dependency
258
272
  name: sidekiq
259
273
  requirement: !ruby/object:Gem::Requirement
@@ -421,8 +435,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
421
435
  - !ruby/object:Gem::Version
422
436
  version: '0'
423
437
  requirements: []
424
- rubyforge_project:
425
- rubygems_version: 2.7.6.2
438
+ rubygems_version: 3.1.2
426
439
  signing_key:
427
440
  specification_version: 4
428
441
  summary: Airbrake is an online tool that provides robust exception tracking in any