airbrake 10.0.2 → 13.0.4

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake/capistrano/capistrano2.rb +1 -1
  3. data/lib/airbrake/capistrano/capistrano3.rb +1 -1
  4. data/lib/airbrake/delayed_job.rb +28 -30
  5. data/lib/airbrake/logger.rb +3 -1
  6. data/lib/airbrake/rack/context_filter.rb +10 -6
  7. data/lib/airbrake/rack/http_headers_filter.rb +4 -4
  8. data/lib/airbrake/rack/instrumentable.rb +9 -5
  9. data/lib/airbrake/rack/route_filter.rb +3 -1
  10. data/lib/airbrake/rack/user.rb +2 -0
  11. data/lib/airbrake/rack.rb +2 -0
  12. data/lib/airbrake/rails/action_cable.rb +19 -17
  13. data/lib/airbrake/rails/action_controller.rb +3 -0
  14. data/lib/airbrake/rails/action_controller_notify_subscriber.rb +2 -0
  15. data/lib/airbrake/rails/action_controller_performance_breakdown_subscriber.rb +2 -0
  16. data/lib/airbrake/rails/action_controller_route_subscriber.rb +2 -0
  17. data/lib/airbrake/rails/active_job.rb +2 -2
  18. data/lib/airbrake/rails/active_record_subscriber.rb +2 -0
  19. data/lib/airbrake/rails/app.rb +22 -0
  20. data/lib/airbrake/rails/backtrace_cleaner.rb +11 -0
  21. data/lib/airbrake/rails/curb.rb +18 -23
  22. data/lib/airbrake/rails/event.rb +14 -2
  23. data/lib/airbrake/rails/excon_subscriber.rb +2 -0
  24. data/lib/airbrake/rails/http.rb +12 -8
  25. data/lib/airbrake/rails/http_client.rb +11 -7
  26. data/lib/airbrake/rails/railtie.rb +7 -99
  27. data/lib/airbrake/rails/railties/action_controller_tie.rb +90 -0
  28. data/lib/airbrake/rails/railties/active_record_tie.rb +74 -0
  29. data/lib/airbrake/rails/railties/middleware_tie.rb +62 -0
  30. data/lib/airbrake/rails/typhoeus.rb +10 -8
  31. data/lib/airbrake/rails.rb +6 -8
  32. data/lib/airbrake/rake/tasks.rb +10 -10
  33. data/lib/airbrake/rake.rb +47 -46
  34. data/lib/airbrake/resque.rb +26 -25
  35. data/lib/airbrake/shoryuken.rb +2 -4
  36. data/lib/airbrake/sidekiq/retryable_jobs_filter.rb +14 -8
  37. data/lib/airbrake/sidekiq.rb +5 -6
  38. data/lib/airbrake/sneakers.rb +28 -27
  39. data/lib/airbrake/version.rb +1 -1
  40. data/lib/generators/airbrake_generator.rb +3 -6
  41. data/lib/generators/airbrake_initializer.rb.erb +65 -65
  42. metadata +52 -138
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5138c1675426cc908ae8b1857898ddc04c135fda23dfe21db19e6b1bf0b36faf
4
- data.tar.gz: '085abd9125a9a615935ac2b1171cdfd74205bde892708e073bfbda44103c72ca'
3
+ metadata.gz: 4f91f4d04951002ed983c872b4d6042c1cc4c01ae609d3969b4b7e9b344bdf12
4
+ data.tar.gz: 58ee6fd62567a65f7df5178f6d0d9e6bd9cd58e9debbf62d9d75fa741b20b6d3
5
5
  SHA512:
6
- metadata.gz: 39bc3cfe3510b0db2adece334666b4e753298fd4d09f4d7d445086c0512aa0436ec8c5b38a85933fae822efeea155d7d2bea7e25866e169878b6f1b330fb4d4d
7
- data.tar.gz: 9c189959ccdfa6c30752462dffb49e8925df101f18597180ab6a31e2320a620431bcbcf6d66c83838d1acfd9be1b6c98623253e60cea77e8881fc84e5996aa14
6
+ metadata.gz: 6b464a25e936fafa269f35bb5c19ed6134ba65f4184f9d76e5f5a349370e1277dc71418393c21a8427dcecc572aabaa941a5ae41dbbfa1547fa280d1416fc630
7
+ data.tar.gz: a78fdeaba2521c51e47b4f9bbee5674b1d3228e25345e7d20cac5c3186c856c6c860a5daee82adc32f8001bce3b6422be50c31dbba32912f43dd535ba1dc965b
@@ -21,7 +21,7 @@ module Airbrake
21
21
  RAILS_ENV=#{fetch(:rails_env, nil)} \
22
22
 
23
23
  bundle exec rake airbrake:deploy \
24
- USERNAME=#{Shellwords.shellescape(ENV['USER'] || ENV['USERNAME'])} \
24
+ USERNAME=#{Shellwords.shellescape(ENV.fetch('USER', nil) || ENV.fetch('USERNAME', nil))} \
25
25
  ENVIRONMENT=#{fetch(:airbrake_env, fetch(:rails_env, 'production'))} \
26
26
  REVISION=#{current_revision.strip} \
27
27
  REPOSITORY=#{repository} \
@@ -13,7 +13,7 @@ namespace :airbrake do
13
13
  REVISION=#{fetch(:current_revision)} \
14
14
  REPOSITORY=#{fetch(:repo_url)} \
15
15
  VERSION=#{fetch(:app_version)}
16
- CMD
16
+ CMD
17
17
 
18
18
  info 'Notified Airbrake of the deploy'
19
19
  end
@@ -7,41 +7,39 @@ module Delayed
7
7
  class Airbrake < ::Delayed::Plugin
8
8
  callbacks do |lifecycle|
9
9
  lifecycle.around(:invoke_job) do |job, *args, &block|
10
- begin
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
16
- params = job.as_json
10
+ timing = ::Airbrake::Benchmark.measure do
11
+ # Forward the call to the next callback in the callback chain
12
+ block.call(job, *args)
13
+ end
14
+ rescue Exception => exception # rubocop:disable Lint/RescueException
15
+ params = job.as_json
17
16
 
18
- # If DelayedJob is used through ActiveJob, it contains extra info.
19
- if job.payload_object.respond_to?(:job_data)
20
- params[:active_job] = job.payload_object.job_data
21
- job_class = job.payload_object.job_data['job_class']
22
- end
17
+ # If DelayedJob is used through ActiveJob, it contains extra info.
18
+ if job.payload_object.respond_to?(:job_data)
19
+ params[:active_job] = job.payload_object.job_data
20
+ job_class = job.payload_object.job_data['job_class']
21
+ end
23
22
 
24
- action = job_class || job.payload_object.class.name
23
+ action = job_class || job.payload_object.class.name
25
24
 
26
- ::Airbrake.notify(exception, params) do |notice|
27
- notice[:context][:component] = 'delayed_job'
28
- notice[:context][:action] = action
29
- end
25
+ ::Airbrake.notify(exception, params) do |notice|
26
+ notice[:context][:component] = 'delayed_job'
27
+ notice[:context][:action] = action
28
+ end
30
29
 
31
- ::Airbrake.notify_queue_sync(
32
- queue: action,
33
- error_count: 1,
34
- timing: 0.01,
35
- )
30
+ ::Airbrake.notify_queue(
31
+ queue: action,
32
+ error_count: 1,
33
+ timing: 0.01,
34
+ )
36
35
 
37
- raise exception
38
- else
39
- ::Airbrake.notify_queue_sync(
40
- queue: job_class || job.payload_object.class.name,
41
- error_count: 0,
42
- timing: timing,
43
- )
44
- end
36
+ raise exception
37
+ else
38
+ ::Airbrake.notify_queue(
39
+ queue: job_class || job.payload_object.class.name,
40
+ error_count: 0,
41
+ timing: timing,
42
+ )
45
43
  end
46
44
  end
47
45
  end
@@ -9,7 +9,7 @@ module Airbrake
9
9
  #
10
10
  # @example
11
11
  # # Create a logger like you normally do and decorate it.
12
- # logger = Airbrake::AirbrakeLogger.new(Logger.new(STDOUT))
12
+ # logger = Airbrake::AirbrakeLogger.new(Logger.new($stdout))
13
13
  #
14
14
  # # Just use the logger like you normally do.
15
15
  # logger.fatal('oops')
@@ -24,6 +24,8 @@ module Airbrake
24
24
  attr_reader :airbrake_level
25
25
 
26
26
  def initialize(logger)
27
+ super
28
+
27
29
  __setobj__(logger)
28
30
  @airbrake_notifier = Airbrake
29
31
  self.level = logger.level
@@ -20,14 +20,9 @@ module Airbrake
20
20
  context = notice[:context]
21
21
 
22
22
  context[:url] = request.url
23
- context[:userAddr] =
24
- if request.respond_to?(:remote_ip)
25
- request.remote_ip
26
- else
27
- request.ip
28
- end
29
23
  context[:userAgent] = request.user_agent
30
24
 
25
+ add_ip(context, request)
31
26
  add_framework_version(context)
32
27
 
33
28
  controller = request.env['action_controller.instance']
@@ -60,6 +55,15 @@ module Airbrake
60
55
  }
61
56
  end
62
57
  end
58
+
59
+ def add_ip(context, request)
60
+ context[:userAddr] =
61
+ if request.respond_to?(:remote_ip)
62
+ request.remote_ip
63
+ else
64
+ request.ip
65
+ end
66
+ end
63
67
  end
64
68
  end
65
69
  end
@@ -8,10 +8,10 @@ module Airbrake
8
8
  class HttpHeadersFilter
9
9
  # @return [Array<String>] the prefixes of the majority of HTTP headers in
10
10
  # Rack (some prefixes match the header names for simplicity)
11
- HTTP_HEADER_PREFIXES = [
12
- 'HTTP_'.freeze,
13
- 'CONTENT_TYPE'.freeze,
14
- 'CONTENT_LENGTH'.freeze,
11
+ HTTP_HEADER_PREFIXES = %w[
12
+ HTTP_
13
+ CONTENT_TYPE
14
+ CONTENT_LENGTH
15
15
  ].freeze
16
16
 
17
17
  # @return [Integer]
@@ -57,6 +57,7 @@ module Airbrake
57
57
  klass.module_exec do
58
58
  mod = __airbrake_capture_timing_module__
59
59
  mod.module_exec do
60
+ # rubocop:disable Style/DocumentDynamicEvalDefinition
60
61
  module_eval <<-RUBY, __FILE__, __LINE__ + 1
61
62
  def #{method_name}(#{args})
62
63
  Airbrake::Rack.capture_timing(#{label.to_s.inspect}) do
@@ -65,6 +66,7 @@ module Airbrake
65
66
  end
66
67
  #{visibility} :#{method_name}
67
68
  RUBY
69
+ # rubocop:enable Style/DocumentDynamicEvalDefinition
68
70
  end
69
71
  prepend mod
70
72
  end
@@ -83,6 +85,7 @@ module Airbrake
83
85
  klass.module_exec do
84
86
  alias_method wrapped_method_name, method_name
85
87
  remove_method method_name if needs_removal
88
+ # rubocop:disable Style/DocumentDynamicEvalDefinition
86
89
  module_eval <<-RUBY, __FILE__, __LINE__ + 1
87
90
  def #{method_name}(#{args})
88
91
  Airbrake::Rack.capture_timing(#{label.to_s.inspect}) do
@@ -91,6 +94,7 @@ module Airbrake
91
94
  end
92
95
  #{visibility} :#{method_name}
93
96
  RUBY
97
+ # rubocop:enable Style/DocumentDynamicEvalDefinition
94
98
  end
95
99
  end
96
100
 
@@ -98,11 +102,11 @@ module Airbrake
98
102
  def self.method_visibility(klass, method_name)
99
103
  klass.module_exec do
100
104
  if protected_method_defined?(method_name)
101
- "protected".freeze
105
+ "protected"
102
106
  elsif private_method_defined?(method_name)
103
- "private".freeze
107
+ "private"
104
108
  else
105
- "public".freeze
109
+ "public"
106
110
  end
107
111
  end
108
112
  end
@@ -111,11 +115,11 @@ module Airbrake
111
115
  # A method instead of a constant so it isn't accessible in the target.
112
116
  if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7")
113
117
  def self.method_signature
114
- "*args, **kw_args, &block".freeze
118
+ "*args, **kw_args, &block"
115
119
  end
116
120
  else
117
121
  def self.method_signature
118
- "*args, &block".freeze
122
+ "*args, &block"
119
123
  end
120
124
  end
121
125
 
@@ -28,12 +28,14 @@ module Airbrake
28
28
 
29
29
  def rails_route(request)
30
30
  return unless (route = Airbrake::Rails::App.recognize_route(request))
31
+
31
32
  route.path
32
33
  end
33
34
 
34
35
  def sinatra_route(request)
35
36
  return unless (route = request.env['sinatra.route'])
36
- route.split(' ').drop(1).join(' ')
37
+
38
+ route.split.drop(1).join(' ')
37
39
  end
38
40
 
39
41
  def action_dispatch_request?(request)
@@ -28,6 +28,7 @@ module Airbrake
28
28
  controller = rack_env['action_controller.instance']
29
29
  return unless controller.respond_to?(:current_user, true)
30
30
  return unless [-1, 0].include?(controller.method(:current_user).arity)
31
+
31
32
  begin
32
33
  controller.__send__(:current_user)
33
34
  rescue Exception => _e # rubocop:disable Lint/RescueException
@@ -59,6 +60,7 @@ module Airbrake
59
60
  # try methods with no arguments or with variable number of arguments,
60
61
  # where none of them are required
61
62
  return unless @user.method(key).arity.between?(-1, 0)
63
+
62
64
  String(@user.__send__(key))
63
65
  end
64
66
 
data/lib/airbrake/rack.rb CHANGED
@@ -18,6 +18,8 @@ module Airbrake
18
18
  # @since v9.2.0
19
19
  # @api public
20
20
  def self.capture_timing(label)
21
+ return yield unless Airbrake::Config.instance.performance_stats
22
+
21
23
  routes = Airbrake::Rack::RequestStore[:routes]
22
24
  if !routes || routes.none?
23
25
  result = yield
@@ -10,26 +10,28 @@ require 'airbrake/rails/action_cable/notify_callback'
10
10
  end
11
11
  end
12
12
 
13
- module ActionCable
14
- module Channel
15
- # @since v8.3.0
16
- # @api private
17
- # @see https://github.com/rails/rails/blob/master/actioncable/lib/action_cable/channel/base.rb
18
- class Base
19
- alias perform_action_without_airbrake perform_action
13
+ module Airbrake
14
+ module ActionCable
15
+ module Channel
16
+ # @since v8.3.0
17
+ # @api private
18
+ # @see https://github.com/rails/rails/blob/master/actioncable/lib/action_cable/channel/base.rb
19
+ module Base
20
+ def perform_action(*args, &block)
21
+ super(*args, &block)
22
+ rescue Exception => ex # rubocop:disable Lint/RescueException
23
+ Airbrake.notify(ex) do |notice|
24
+ notice.stash[:action_cable_connection] = connection
25
+ notice[:context][:component] = self.class
26
+ notice[:context][:action] = args.first['action']
27
+ notice[:params].merge!(args.first)
28
+ end
20
29
 
21
- def perform_action(*args, &block)
22
- perform_action_without_airbrake(*args, &block)
23
- rescue Exception => ex # rubocop:disable Lint/RescueException
24
- Airbrake.notify(ex) do |notice|
25
- notice.stash[:action_cable_connection] = connection
26
- notice[:context][:component] = self.class
27
- notice[:context][:action] = args.first['action']
28
- notice[:params].merge!(args.first)
30
+ raise ex
29
31
  end
30
-
31
- raise ex
32
32
  end
33
33
  end
34
34
  end
35
35
  end
36
+
37
+ ActionCable::Channel::Base.prepend(Airbrake::ActionCable::Channel::Base)
@@ -14,6 +14,7 @@ module Airbrake
14
14
  # @see Airbrake#notify, #notify_airbrake_sync
15
15
  def notify_airbrake(exception, params = {}, &block)
16
16
  return unless (notice = build_notice(exception, params))
17
+
17
18
  Airbrake.notify(notice, params, &block)
18
19
  end
19
20
 
@@ -22,6 +23,7 @@ module Airbrake
22
23
  # @see Airbrake#notify_sync, #notify_airbrake
23
24
  def notify_airbrake_sync(exception, params = {}, &block)
24
25
  return unless (notice = build_notice(exception, params))
26
+
25
27
  Airbrake.notify_sync(notice, params, &block)
26
28
  end
27
29
 
@@ -29,6 +31,7 @@ module Airbrake
29
31
  # @return [Airbrake::Notice] the notice with information from the Rack env
30
32
  def build_notice(exception, params = {})
31
33
  return unless (notice = Airbrake.build_notice(exception, params))
34
+
32
35
  notice.stash[:rack_request] = request
33
36
  notice
34
37
  end
@@ -10,6 +10,8 @@ module Airbrake
10
10
  # @since v8.0.0
11
11
  class ActionControllerNotifySubscriber
12
12
  def call(*args)
13
+ return unless Airbrake::Config.instance.performance_stats
14
+
13
15
  routes = Airbrake::Rack::RequestStore[:routes]
14
16
  return if !routes || routes.none?
15
17
 
@@ -7,6 +7,8 @@ module Airbrake
7
7
  # @since v8.3.0
8
8
  class ActionControllerPerformanceBreakdownSubscriber
9
9
  def call(*args)
10
+ return unless Airbrake::Config.instance.performance_stats
11
+
10
12
  routes = Airbrake::Rack::RequestStore[:routes]
11
13
  return if !routes || routes.none?
12
14
 
@@ -11,6 +11,8 @@ module Airbrake
11
11
  # @since v8.0.0
12
12
  class ActionControllerRouteSubscriber
13
13
  def call(*args)
14
+ return unless Airbrake::Config.instance.performance_stats
15
+
14
16
  # We don't track routeless events.
15
17
  return unless (routes = Airbrake::Rack::RequestStore[:routes])
16
18
 
@@ -22,14 +22,14 @@ module Airbrake
22
22
  block.call
23
23
  end
24
24
  rescue StandardError => exception
25
- Airbrake.notify_queue_sync(
25
+ Airbrake.notify_queue(
26
26
  queue: job.class.name,
27
27
  error_count: 1,
28
28
  timing: 0.01,
29
29
  )
30
30
  raise exception
31
31
  else
32
- Airbrake.notify_queue_sync(
32
+ Airbrake.notify_queue(
33
33
  queue: job.class.name,
34
34
  error_count: 0,
35
35
  timing: timing,
@@ -10,6 +10,8 @@ module Airbrake
10
10
  # @since v8.1.0
11
11
  class ActiveRecordSubscriber
12
12
  def call(*args)
13
+ return unless Airbrake::Config.instance.query_stats
14
+
13
15
  routes = Airbrake::Rack::RequestStore[:routes]
14
16
  return if !routes || routes.none?
15
17
 
@@ -11,12 +11,19 @@ module Airbrake
11
11
 
12
12
  # @param [] request
13
13
  # @return [Airbrake::Rails::App::Route, nil]
14
+ # rubocop:disable Metrics/AbcSize
14
15
  def self.recognize_route(request)
15
16
  # Duplicate `request` because `recognize` *can* strip the request's
16
17
  # `path_info`, which results in broken engine links (when the engine has
17
18
  # an isolated namespace).
18
19
  request_copy = request.dup
19
20
 
21
+ # Save original script name because `router.recognize(request)` mutates
22
+ # it. It's a Rails bug. More info in:
23
+ # * https://github.com/airbrake/airbrake/issues/1072
24
+ # * https://github.com/rails/rails/issues/31152
25
+ original_script_name = request.env['SCRIPT_NAME']
26
+
20
27
  # We must search every engine individually to find a concrete route. If
21
28
  # we rely only on the `Rails.application.routes.router`, then the
22
29
  # recognize call would return the root route, neglecting PATH_INFO
@@ -26,6 +33,20 @@ module Airbrake
26
33
  # * `Marketing::Engine` recognizes it as `marketing#/pricing` (correct)
27
34
  engines.each do |engine|
28
35
  engine.routes.router.recognize(request_copy) do |route, _params|
36
+ # Restore original script name. Remove this code when/if the Rails
37
+ # bug is fixed: https://github.com/airbrake/airbrake/issues/1072
38
+ request.env['SCRIPT_NAME'] = original_script_name
39
+
40
+ # Skip "catch-all" routes such as:
41
+ # get '*path => 'pages#about'
42
+ #
43
+ # Ideally, we should be using `route.glob?` but in Rails 7+ this
44
+ # call would fail with a `NoMethodError`. This is because in
45
+ # Rails 7+ the AST for the route is not kept in memory anymore.
46
+ #
47
+ # See: https://github.com/rails/rails/pull/43006#discussion_r783895766
48
+ next if route.path.spec.any?(ActionDispatch::Journey::Nodes::Star)
49
+
29
50
  path =
30
51
  if engine == ::Rails.application
31
52
  route.path.spec.to_s
@@ -46,6 +67,7 @@ module Airbrake
46
67
 
47
68
  nil
48
69
  end
70
+ # rubocop:enable Metrics/AbcSize
49
71
 
50
72
  def self.engines
51
73
  @engines ||= [*::Rails::Engine.subclasses, ::Rails.application]
@@ -4,9 +4,20 @@ module Airbrake
4
4
  module Rails
5
5
  # BacktraceCleaner is a wrapper around Rails.backtrace_cleaner.
6
6
  class BacktraceCleaner
7
+ # @return [Regexp]
8
+ AIRBRAKE_FRAME_PATTERN = %r{/airbrake/lib/airbrake/}.freeze
9
+
7
10
  def self.clean(backtrace)
8
11
  ::Rails.backtrace_cleaner.clean(backtrace).first(1)
9
12
  end
10
13
  end
11
14
  end
12
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,37 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Curl
4
- # Monkey-patch to measure request timing.
5
- class Easy
6
- alias http_without_airbrake http
7
-
8
- def http(verb)
9
- Airbrake::Rack.capture_timing(:http) do
10
- http_without_airbrake(verb)
3
+ module Airbrake
4
+ module Rails
5
+ # Allows measuring request timing.
6
+ module CurlEasy
7
+ def http(verb)
8
+ Airbrake::Rack.capture_timing(:http) do
9
+ super(verb)
10
+ end
11
11
  end
12
- end
13
-
14
- alias perform_without_airbrake perform
15
12
 
16
- def perform(&block)
17
- Airbrake::Rack.capture_timing(:http) do
18
- perform_without_airbrake(&block)
13
+ def perform(&block)
14
+ Airbrake::Rack.capture_timing(:http) do
15
+ super(&block)
16
+ end
19
17
  end
20
18
  end
21
- end
22
- end
23
-
24
- module Curl
25
- # Monkey-patch to measure request timing.
26
- class Multi
27
- class << self
28
- alias http_without_airbrake http
29
19
 
20
+ # Allows measuring request timing.
21
+ module CurlMulti
30
22
  def http(urls_with_config, multi_options = {}, &block)
31
23
  Airbrake::Rack.capture_timing(:http) do
32
- http_without_airbrake(urls_with_config, multi_options, &block)
24
+ super(urls_with_config, multi_options, &block)
33
25
  end
34
26
  end
35
27
  end
36
28
  end
37
29
  end
30
+
31
+ Curl::Easy.prepend(Airbrake::Rails::CurlEasy)
32
+ Curl::Multi.singleton_class.prepend(Airbrake::Rails::CurlMulti)
@@ -8,12 +8,16 @@ module Airbrake
8
8
  # @api private
9
9
  class Event
10
10
  # @see https://github.com/rails/rails/issues/8987
11
- HTML_RESPONSE_WILDCARD = "*/*".freeze
11
+ HTML_RESPONSE_WILDCARD = "*/*"
12
+
13
+ # @return [Integer]
14
+ MILLISECOND = 1000
12
15
 
13
16
  include Airbrake::Loggable
14
17
 
15
18
  def initialize(*args)
16
19
  @event = ActiveSupport::Notifications::Event.new(*args)
20
+ @rails_7_or_greater = ::Rails::VERSION::MAJOR >= 7
17
21
  end
18
22
 
19
23
  def method
@@ -42,7 +46,15 @@ module Airbrake
42
46
  end
43
47
 
44
48
  def time
45
- @event.time
49
+ # On Rails 7+ `ActiveSupport::Notifications::Event#time` returns an
50
+ # instance of Float. It represents monotonic time in milliseconds.
51
+ # Airbrake Ruby expects that the provided time is in seconds. Hence,
52
+ # we need to convert it from milliseconds to seconds. In the
53
+ # versions below Rails 7, time is an instance of Time.
54
+ #
55
+ # Relevant commit:
56
+ # https://github.com/rails/rails/commit/81d0dc90becfe0b8e7f7f26beb66c25d84b8ec7f
57
+ @rails_7_or_greater ? @event.time / MILLISECOND : @event.time
46
58
  end
47
59
 
48
60
  def groups
@@ -8,6 +8,8 @@ module Airbrake
8
8
  # @since v9.2.0
9
9
  class Excon
10
10
  def call(*args)
11
+ return unless Airbrake::Config.instance.performance_stats
12
+
11
13
  routes = Airbrake::Rack::RequestStore[:routes]
12
14
  return if !routes || routes.none?
13
15
 
@@ -1,14 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module HTTP
4
- # Monkey-patch to measure request timing.
5
- class Client
6
- alias perform_without_airbrake perform
7
-
8
- def perform(request, options)
9
- Airbrake::Rack.capture_timing(:http) do
10
- perform_without_airbrake(request, options)
3
+ module Airbrake
4
+ module Rails
5
+ # Monkey-patch to measure request timing.
6
+ # @api private
7
+ # @since v11.0.2
8
+ module HTTP
9
+ def perform(request, options)
10
+ Airbrake::Rack.capture_timing(:http) do
11
+ super(request, options)
12
+ end
11
13
  end
12
14
  end
13
15
  end
14
16
  end
17
+
18
+ HTTP::Client.prepend(Airbrake::Rails::HTTP)
@@ -1,12 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Monkey-patch to measure request timing.
4
- class HTTPClient
5
- alias do_get_without_airbrake do_get_block
6
-
7
- def do_get_block(request, proxy, connection, &block)
8
- Airbrake::Rack.capture_timing(:http) do
9
- do_get_without_airbrake(request, proxy, connection, &block)
3
+ module Airbrake
4
+ module Rails
5
+ # Allows measuring request timing.
6
+ module HTTPClient
7
+ def do_get_block(request, proxy, connection, &block)
8
+ Airbrake::Rack.capture_timing(:http) do
9
+ super(request, proxy, connection, &block)
10
+ end
11
+ end
10
12
  end
11
13
  end
12
14
  end
15
+
16
+ HTTPClient.prepend(Airbrake::Rails::HTTPClient)