airbrake 9.5.0 → 11.0.1

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 +3 -1
  6. data/lib/airbrake/delayed_job.rb +24 -6
  7. data/lib/airbrake/logger.rb +2 -0
  8. data/lib/airbrake/rack.rb +4 -0
  9. data/lib/airbrake/rack/context_filter.rb +9 -2
  10. data/lib/airbrake/rack/http_headers_filter.rb +7 -5
  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 +8 -10
  17. data/lib/airbrake/rack/session_filter.rb +2 -0
  18. data/lib/airbrake/rack/user.rb +4 -0
  19. data/lib/airbrake/rack/user_filter.rb +2 -0
  20. data/lib/airbrake/rails.rb +8 -8
  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 +5 -0
  24. data/lib/airbrake/rails/action_controller_notify_subscriber.rb +6 -2
  25. data/lib/airbrake/rails/action_controller_performance_breakdown_subscriber.rb +6 -1
  26. data/lib/airbrake/rails/action_controller_route_subscriber.rb +8 -21
  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 +7 -3
  30. data/lib/airbrake/rails/app.rb +60 -25
  31. data/lib/airbrake/rails/backtrace_cleaner.rb +13 -0
  32. data/lib/airbrake/rails/curb.rb +19 -22
  33. data/lib/airbrake/rails/event.rb +4 -6
  34. data/lib/airbrake/rails/excon_subscriber.rb +4 -0
  35. data/lib/airbrake/rails/http.rb +2 -0
  36. data/lib/airbrake/rails/http_client.rb +12 -6
  37. data/lib/airbrake/rails/net_http.rb +14 -6
  38. data/lib/airbrake/rails/railtie.rb +55 -45
  39. data/lib/airbrake/rails/typhoeus.rb +11 -7
  40. data/lib/airbrake/rake.rb +3 -1
  41. data/lib/airbrake/rake/tasks.rb +7 -5
  42. data/lib/airbrake/resque.rb +32 -0
  43. data/lib/airbrake/shoryuken.rb +17 -3
  44. data/lib/airbrake/sidekiq.rb +22 -14
  45. data/lib/airbrake/sidekiq/retryable_jobs_filter.rb +2 -0
  46. data/lib/airbrake/sneakers.rb +39 -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 +5 -3
  50. metadata +61 -90
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 29bdd7916d90d84ac4933c2ce14d9069bd719a371bf822a3b886175841b2005c
4
- data.tar.gz: 94f382b83ce554984a9c4e8162241be85baa78739d928e3c74081cfb17217f1a
3
+ metadata.gz: 4b780328fc01621a0273577655179008c6314c6fe5187e5562f15c5112f296cd
4
+ data.tar.gz: 3036e80e03bbb2fa4159a9167a6971adcda2dcbee789ed3db320c0eaf0408d9b
5
5
  SHA512:
6
- metadata.gz: e7e27d67804da299808bd87f6d2f201ab778ef344ff377283536d5be2082c579df9e54102eb83d48a5cadc78cf434bfcb7bf3c999a816b449f8930e539c96743
7
- data.tar.gz: '028d1865c3e42686c54455164087d401f3dabbd1380b915aec7ca8191e0f777dc26c32930c9118bd160ef1bd6ba63f1fac289a4786f9fc720c98f7a3eadfe2ae'
6
+ metadata.gz: f307768db070afd129623ed312e7f4da4e4040f1bdf33cb9d1e31f2f1ee4278e992eac0e901a6bb257042ff9aff5f651c2a8bd74579606771c56d4040e63c0b1
7
+ data.tar.gz: b6376325c1b352ac7f13a0a0467737fb2a8cb08a12748c10d991265342119bcd6f510271744facb11018312de593857835aceadef557c20bbf1e85a2296e7bd4
@@ -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
@@ -11,7 +13,7 @@ namespace :airbrake do
11
13
  REVISION=#{fetch(:current_revision)} \
12
14
  REPOSITORY=#{fetch(:repo_url)} \
13
15
  VERSION=#{fetch(:app_version)}
14
- CMD
16
+ CMD
15
17
 
16
18
  info 'Notified Airbrake of the deploy'
17
19
  end
@@ -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'
@@ -16,6 +18,8 @@ module Airbrake
16
18
  # @since v9.2.0
17
19
  # @api public
18
20
  def self.capture_timing(label)
21
+ return yield unless Airbrake::Config.instance.performance_stats
22
+
19
23
  routes = Airbrake::Rack::RequestStore[:routes]
20
24
  if !routes || routes.none?
21
25
  result = yield
@@ -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.
@@ -6,10 +8,10 @@ module Airbrake
6
8
  class HttpHeadersFilter
7
9
  # @return [Array<String>] the prefixes of the majority of HTTP headers in
8
10
  # Rack (some prefixes match the header names for simplicity)
9
- HTTP_HEADER_PREFIXES = [
10
- 'HTTP_'.freeze,
11
- 'CONTENT_TYPE'.freeze,
12
- 'CONTENT_LENGTH'.freeze
11
+ HTTP_HEADER_PREFIXES = %w[
12
+ HTTP_
13
+ CONTENT_TYPE
14
+ CONTENT_LENGTH
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"
102
+ elsif private_method_defined?(method_name)
103
+ "private"
104
+ else
105
+ "public"
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"
115
+ end
116
+ else
117
+ def self.method_signature
118
+ "*args, &block"
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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'airbrake/rails/app'
4
+
1
5
  module Airbrake
2
6
  module Rack
3
7
  # Adds route slugs to context/route.
@@ -23,20 +27,14 @@ module Airbrake
23
27
  private
24
28
 
25
29
  def rails_route(request)
26
- ::Rails.application.routes.router.recognize(request) do |route, _parameters|
27
- # Rails can recognize multiple routes for the given request. For
28
- # example, if we visit /users/2/edit, then Rails sees these routes:
29
- # * "/users/:id/edit(.:format)"
30
- # * "/"
31
- #
32
- # We return the first route as, what it seems, the most optimal
33
- # approach.
34
- return route.path.spec.to_s
35
- end
30
+ return unless (route = Airbrake::Rails::App.recognize_route(request))
31
+
32
+ route.path
36
33
  end
37
34
 
38
35
  def sinatra_route(request)
39
36
  return unless (route = request.env['sinatra.route'])
37
+
40
38
  route.split(' ').drop(1).join(' ')
41
39
  end
42
40
 
@@ -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
@@ -26,6 +28,7 @@ module Airbrake
26
28
  controller = rack_env['action_controller.instance']
27
29
  return unless controller.respond_to?(:current_user, true)
28
30
  return unless [-1, 0].include?(controller.method(:current_user).arity)
31
+
29
32
  begin
30
33
  controller.__send__(:current_user)
31
34
  rescue Exception => _e # rubocop:disable Lint/RescueException
@@ -57,6 +60,7 @@ module Airbrake
57
60
  # try methods with no arguments or with variable number of arguments,
58
61
  # where none of them are required
59
62
  return unless @user.method(key).arity.between?(-1, 0)
63
+
60
64
  String(@user.__send__(key))
61
65
  end
62
66
 
@@ -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,19 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'airbrake/rails/railtie'
2
4
 
3
5
  module Airbrake
4
6
  # Rails namespace holds all Rails-related functionality.
5
7
  module Rails
6
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
+
7
13
  if ENV['RAILS_LOG_TO_STDOUT'].present?
8
- Logger.new(STDOUT, level: ::Rails.logger.level)
14
+ Logger.new(STDOUT, level: level)
9
15
  else
10
- Logger.new(
11
- ::Rails.root.join('log', 'airbrake.log'),
12
-
13
- # Rails.logger is not set in some Rake tasks such as
14
- # 'airbrake:deploy'. In this case we use a sensible fallback.
15
- level: (::Rails.logger ? ::Rails.logger.level : Logger::ERROR)
16
- )
16
+ Logger.new(::Rails.root.join('log', 'airbrake.log'), level: level)
17
17
  end
18
18
  end
19
19
  end