airbrake 9.3.0 → 13.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +5 -5
  2. data/lib/airbrake/capistrano/capistrano2.rb +3 -1
  3. data/lib/airbrake/capistrano/capistrano3.rb +3 -1
  4. data/lib/airbrake/capistrano.rb +2 -0
  5. data/lib/airbrake/delayed_job.rb +31 -15
  6. data/lib/airbrake/logger.rb +5 -1
  7. data/lib/airbrake/rack/context_filter.rb +13 -2
  8. data/lib/airbrake/rack/http_headers_filter.rb +7 -5
  9. data/lib/airbrake/rack/http_params_filter.rb +2 -0
  10. data/lib/airbrake/rack/instrumentable.rb +116 -4
  11. data/lib/airbrake/rack/middleware.rb +3 -1
  12. data/lib/airbrake/rack/request_body_filter.rb +2 -0
  13. data/lib/airbrake/rack/request_store.rb +2 -0
  14. data/lib/airbrake/rack/route_filter.rb +9 -11
  15. data/lib/airbrake/rack/session_filter.rb +2 -0
  16. data/lib/airbrake/rack/user.rb +9 -1
  17. data/lib/airbrake/rack/user_filter.rb +2 -0
  18. data/lib/airbrake/rack.rb +4 -0
  19. data/lib/airbrake/rails/action_cable/notify_callback.rb +2 -0
  20. data/lib/airbrake/rails/action_cable.rb +21 -17
  21. data/lib/airbrake/rails/action_controller.rb +9 -4
  22. data/lib/airbrake/rails/action_controller_notify_subscriber.rb +6 -2
  23. data/lib/airbrake/rails/action_controller_performance_breakdown_subscriber.rb +6 -1
  24. data/lib/airbrake/rails/action_controller_route_subscriber.rb +8 -15
  25. data/lib/airbrake/rails/active_job.rb +25 -8
  26. data/lib/airbrake/rails/active_record.rb +2 -0
  27. data/lib/airbrake/rails/active_record_subscriber.rb +7 -3
  28. data/lib/airbrake/rails/app.rb +64 -19
  29. data/lib/airbrake/rails/backtrace_cleaner.rb +13 -0
  30. data/lib/airbrake/rails/curb.rb +19 -22
  31. data/lib/airbrake/rails/event.rb +26 -8
  32. data/lib/airbrake/rails/excon_subscriber.rb +4 -0
  33. data/lib/airbrake/rails/http.rb +13 -7
  34. data/lib/airbrake/rails/http_client.rb +12 -6
  35. data/lib/airbrake/rails/net_http.rb +14 -6
  36. data/lib/airbrake/rails/railtie.rb +54 -0
  37. data/lib/airbrake/rails/railties/action_controller_tie.rb +90 -0
  38. data/lib/airbrake/rails/railties/active_record_tie.rb +74 -0
  39. data/lib/airbrake/rails/railties/middleware_tie.rb +62 -0
  40. data/lib/airbrake/rails/typhoeus.rb +11 -7
  41. data/lib/airbrake/rails.rb +14 -135
  42. data/lib/airbrake/rake/tasks.rb +15 -13
  43. data/lib/airbrake/rake.rb +49 -46
  44. data/lib/airbrake/resque.rb +33 -0
  45. data/lib/airbrake/shoryuken.rb +16 -4
  46. data/lib/airbrake/sidekiq/retryable_jobs_filter.rb +9 -1
  47. data/lib/airbrake/sidekiq.rb +22 -16
  48. data/lib/airbrake/sneakers.rb +39 -1
  49. data/lib/airbrake/version.rb +3 -1
  50. data/lib/airbrake.rb +2 -0
  51. data/lib/generators/airbrake_generator.rb +5 -6
  52. data/lib/generators/airbrake_initializer.rb.erb +67 -65
  53. metadata +53 -97
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f0a71f9f79da2a5989445a300bebe3d9025df839
4
- data.tar.gz: 177632611970f6791ed686d3344df1eef7923010
2
+ SHA256:
3
+ metadata.gz: 29da4f7ace6d408a64f855ddab6385fd40863235694a22cd31f5e46d1ae9c957
4
+ data.tar.gz: 4c08d2305072b5b48936e87dd2e53bf1610459637c7e23c7e75e118e475232b9
5
5
  SHA512:
6
- metadata.gz: 978eef94355d7bc09429086301983dfabb87426e7c54d0bb2044ef19ebcf3c39c1fae79f9e2954e718013ce7071c13a16005294b9ce48c47ff4e7fbf1aba2719
7
- data.tar.gz: c1006e0378f3f3b195362a9ca172359a3e131eae8de3e31cbaa017be6b7a8dc43dc58e3bb2ec4ca88b28cebab9f38bd9518a4b8a2f8e19c30340760c732124fa
6
+ metadata.gz: 9ddb0e206f5723439061ca14af99f2c228f80f6db5907f07a16a8fde611b4f1e13533a0dea5e5b27d564a90e3044fb449fa09731274042c59fbb84bdb7f83af1
7
+ data.tar.gz: b78091a0e4391c27eea3c884130e1c6515cf10cf417ea29ec6c6df32075f5917dead2efd2f6e42395a64142f761d3870408ba5f7c7fb3ed1a90f53204c7bc8b2
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Airbrake
2
4
  # The Capistrano v2 integration.
3
5
  module Capistrano
@@ -19,7 +21,7 @@ module Airbrake
19
21
  RAILS_ENV=#{fetch(:rails_env, nil)} \
20
22
 
21
23
  bundle exec rake airbrake:deploy \
22
- USERNAME=#{Shellwords.shellescape(ENV['USER'] || ENV['USERNAME'])} \
24
+ USERNAME=#{Shellwords.shellescape(ENV.fetch('USER', nil) || ENV.fetch('USERNAME', nil))} \
23
25
  ENVIRONMENT=#{fetch(:airbrake_env, fetch(:rails_env, 'production'))} \
24
26
  REVISION=#{current_revision.strip} \
25
27
  REPOSITORY=#{repository} \
@@ -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,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,33 +1,49 @@
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
- begin
10
+ timing = ::Airbrake::Benchmark.measure do
9
11
  # Forward the call to the next callback in the callback chain
10
12
  block.call(job, *args)
11
- rescue Exception => exception
12
- params = job.as_json
13
+ end
14
+ rescue Exception => exception # rubocop:disable Lint/RescueException
15
+ params = job.as_json
13
16
 
14
- # If DelayedJob is used through ActiveJob, it contains extra info.
15
- if job.payload_object.respond_to?(:job_data)
16
- params[:active_job] = job.payload_object.job_data
17
- job_class = job.payload_object.job_data['job_class']
18
- 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
19
22
 
20
- ::Airbrake.notify(exception, params) do |notice|
21
- notice[:context][:component] = 'delayed_job'
22
- notice[:context][:action] = job_class || job.payload_object.class.name
23
- end
23
+ action = job_class || job.payload_object.class.name
24
24
 
25
- raise exception
25
+ ::Airbrake.notify(exception, params) do |notice|
26
+ notice[:context][:component] = 'delayed_job'
27
+ notice[:context][:action] = action
26
28
  end
29
+
30
+ ::Airbrake.notify_queue(
31
+ queue: action,
32
+ error_count: 1,
33
+ timing: 0.01,
34
+ )
35
+
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
+ )
27
43
  end
28
44
  end
29
45
  end
30
- # rubocop:enable Lint/RescueException
46
+ # rubocop:enable Metrics/BlockLength
31
47
  end
32
48
  end
33
49
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
  require 'delegate'
3
5
 
@@ -7,7 +9,7 @@ module Airbrake
7
9
  #
8
10
  # @example
9
11
  # # Create a logger like you normally do and decorate it.
10
- # logger = Airbrake::AirbrakeLogger.new(Logger.new(STDOUT))
12
+ # logger = Airbrake::AirbrakeLogger.new(Logger.new($stdout))
11
13
  #
12
14
  # # Just use the logger like you normally do.
13
15
  # logger.fatal('oops')
@@ -22,6 +24,8 @@ module Airbrake
22
24
  attr_reader :airbrake_level
23
25
 
24
26
  def initialize(logger)
27
+ super
28
+
25
29
  __setobj__(logger)
26
30
  @airbrake_notifier = Airbrake
27
31
  self.level = logger.level
@@ -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,9 +20,9 @@ module Airbrake
18
20
  context = notice[:context]
19
21
 
20
22
  context[:url] = request.url
21
- context[:userAddr] = request.ip
22
23
  context[:userAgent] = request.user_agent
23
24
 
25
+ add_ip(context, request)
24
26
  add_framework_version(context)
25
27
 
26
28
  controller = request.env['action_controller.instance']
@@ -49,10 +51,19 @@ module Airbrake
49
51
  else
50
52
  {
51
53
  'rack_version' => ::Rack.version,
52
- 'rack_release' => ::Rack.release
54
+ 'rack_release' => ::Rack.release,
53
55
  }
54
56
  end
55
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
56
67
  end
57
68
  end
58
69
  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,124 @@ 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
+ # rubocop:disable Style/DocumentDynamicEvalDefinition
61
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
62
+ def #{method_name}(#{args})
63
+ Airbrake::Rack.capture_timing(#{label.to_s.inspect}) do
64
+ super
65
+ end
66
+ end
67
+ #{visibility} :#{method_name}
68
+ RUBY
69
+ # rubocop:enable Style/DocumentDynamicEvalDefinition
70
+ end
71
+ prepend mod
72
+ end
73
+ end
74
+
75
+ # @api private
76
+ def self.chain_capture_timing(klass, method_name, label)
77
+ args = method_signature
78
+ visibility = method_visibility(klass, method_name)
19
79
 
20
- define_method(method_name) do |*args|
21
- Airbrake::Rack.capture_timing(label) do
22
- __send__("#{method_name}_without_airbrake", *args)
80
+ # Generate the wrapper method.
81
+ aliased = method_name.to_s.sub(/([?!=])$/, '')
82
+ punctuation = Regexp.last_match(1)
83
+ wrapped_method_name = "#{aliased}_without_airbrake#{punctuation}"
84
+ needs_removal = method_needs_removal(klass, method_name)
85
+ klass.module_exec do
86
+ alias_method wrapped_method_name, method_name
87
+ remove_method method_name if needs_removal
88
+ # rubocop:disable Style/DocumentDynamicEvalDefinition
89
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
90
+ def #{method_name}(#{args})
91
+ Airbrake::Rack.capture_timing(#{label.to_s.inspect}) do
92
+ __send__("#{aliased}_without_airbrake#{punctuation}", #{args})
93
+ end
94
+ end
95
+ #{visibility} :#{method_name}
96
+ RUBY
97
+ # rubocop:enable Style/DocumentDynamicEvalDefinition
98
+ end
99
+ end
100
+
101
+ # @api private
102
+ def self.method_visibility(klass, method_name)
103
+ klass.module_exec do
104
+ if protected_method_defined?(method_name)
105
+ "protected"
106
+ elsif private_method_defined?(method_name)
107
+ "private"
108
+ else
109
+ "public"
23
110
  end
24
111
  end
25
112
  end
113
+
114
+ # @api private
115
+ # A method instead of a constant so it isn't accessible in the target.
116
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7")
117
+ def self.method_signature
118
+ "*args, **kw_args, &block"
119
+ end
120
+ else
121
+ def self.method_signature
122
+ "*args, &block"
123
+ end
124
+ end
125
+
126
+ # @api private
127
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.6")
128
+ def self.method_needs_removal(klass, method_name)
129
+ klass.method_defined?(method_name, false) ||
130
+ klass.private_method_defined?(method_name, false)
131
+ end
132
+ else
133
+ def self.method_needs_removal(klass, method_name)
134
+ klass.instance_methods(false).include?(method_name) ||
135
+ klass.private_instance_methods(false).include?(method_name)
136
+ end
137
+ end
26
138
  end
27
139
  end
28
140
  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,21 +27,15 @@ 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'])
40
- route.split(' ').drop(1).join(' ')
37
+
38
+ route.split.drop(1).join(' ')
41
39
  end
42
40
 
43
41
  def action_dispatch_request?(request)
@@ -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,7 +28,12 @@ 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)
29
- controller.__send__(:current_user)
31
+
32
+ begin
33
+ controller.__send__(:current_user)
34
+ rescue Exception => _e # rubocop:disable Lint/RescueException
35
+ nil
36
+ end
30
37
  end
31
38
  private_class_method :try_current_user
32
39
 
@@ -53,6 +60,7 @@ module Airbrake
53
60
  # try methods with no arguments or with variable number of arguments,
54
61
  # where none of them are required
55
62
  return unless @user.method(key).arity.between?(-1, 0)
63
+
56
64
  String(@user.__send__(key))
57
65
  end
58
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.
data/lib/airbrake/rack.rb CHANGED
@@ -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 Rails
3
5
  module ActionCable
@@ -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|
@@ -8,26 +10,28 @@ require 'airbrake/rails/action_cable/notify_callback'
8
10
  end
9
11
  end
10
12
 
11
- module ActionCable
12
- module Channel
13
- # @since v8.3.0
14
- # @api private
15
- # @see https://github.com/rails/rails/blob/master/actioncable/lib/action_cable/channel/base.rb
16
- class Base
17
- 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
18
29
 
19
- def perform_action(*args, &block)
20
- perform_action_without_airbrake(*args, &block)
21
- rescue Exception => ex # rubocop:disable Lint/RescueException
22
- Airbrake.notify(ex) do |notice|
23
- notice.stash[:action_cable_connection] = connection
24
- notice[:context][:component] = self.class
25
- notice[:context][:action] = args.first['action']
26
- notice[:params].merge!(args.first)
30
+ raise ex
27
31
  end
28
-
29
- raise ex
30
32
  end
31
33
  end
32
34
  end
33
35
  end
36
+
37
+ ActionCable::Channel::Base.prepend(Airbrake::ActionCable::Channel::Base)
@@ -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
@@ -10,23 +12,26 @@ module Airbrake
10
12
  # A helper method for sending notices to Airbrake *asynchronously*.
11
13
  # Attaches information from the Rack env.
12
14
  # @see Airbrake#notify, #notify_airbrake_sync
13
- def notify_airbrake(exception, params = {})
15
+ def notify_airbrake(exception, params = {}, &block)
14
16
  return unless (notice = build_notice(exception, params))
15
- Airbrake.notify(notice, params)
17
+
18
+ Airbrake.notify(notice, params, &block)
16
19
  end
17
20
 
18
21
  # A helper method for sending notices to Airbrake *synchronously*.
19
22
  # Attaches information from the Rack env.
20
23
  # @see Airbrake#notify_sync, #notify_airbrake
21
- def notify_airbrake_sync(exception, params = {})
24
+ def notify_airbrake_sync(exception, params = {}, &block)
22
25
  return unless (notice = build_notice(exception, params))
23
- Airbrake.notify_sync(notice, params)
26
+
27
+ Airbrake.notify_sync(notice, params, &block)
24
28
  end
25
29
 
26
30
  # @param [Exception] exception
27
31
  # @return [Airbrake::Notice] the notice with information from the Rack env
28
32
  def build_notice(exception, params = {})
29
33
  return unless (notice = Airbrake.build_notice(exception, params))
34
+
30
35
  notice.stash[:rack_request] = request
31
36
  notice
32
37
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'airbrake/rails/event'
2
4
 
3
5
  module Airbrake
@@ -8,6 +10,8 @@ module Airbrake
8
10
  # @since v8.0.0
9
11
  class ActionControllerNotifySubscriber
10
12
  def call(*args)
13
+ return unless Airbrake::Config.instance.performance_stats
14
+
11
15
  routes = Airbrake::Rack::RequestStore[:routes]
12
16
  return if !routes || routes.none?
13
17
 
@@ -18,8 +22,8 @@ module Airbrake
18
22
  method: event.method,
19
23
  route: route,
20
24
  status_code: event.status_code,
21
- start_time: event.time,
22
- end_time: Time.new
25
+ timing: event.duration,
26
+ time: event.time,
23
27
  )
24
28
  end
25
29
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'airbrake/rails/event'
2
4
 
3
5
  module Airbrake
@@ -5,6 +7,8 @@ module Airbrake
5
7
  # @since v8.3.0
6
8
  class ActionControllerPerformanceBreakdownSubscriber
7
9
  def call(*args)
10
+ return unless Airbrake::Config.instance.performance_stats
11
+
8
12
  routes = Airbrake::Rack::RequestStore[:routes]
9
13
  return if !routes || routes.none?
10
14
 
@@ -20,7 +24,8 @@ module Airbrake
20
24
  route: route,
21
25
  response_type: event.response_type,
22
26
  groups: groups,
23
- start_time: event.time
27
+ timing: event.duration,
28
+ time: event.time,
24
29
  }
25
30
 
26
31
  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
 
@@ -8,33 +10,24 @@ module Airbrake
8
10
  #
9
11
  # @since v8.0.0
10
12
  class ActionControllerRouteSubscriber
11
- def initialize
12
- @app = Airbrake::Rails::App.new
13
- end
14
-
15
13
  def call(*args)
14
+ return unless Airbrake::Config.instance.performance_stats
15
+
16
16
  # We don't track routeless events.
17
17
  return unless (routes = Airbrake::Rack::RequestStore[:routes])
18
18
 
19
19
  event = Airbrake::Rails::Event.new(*args)
20
- route = find_route(event.params)
20
+ route = Airbrake::Rails::App.recognize_route(
21
+ Airbrake::Rack::RequestStore[:request],
22
+ )
21
23
  return unless route
22
24
 
23
25
  routes[route.path] = {
24
26
  method: event.method,
25
27
  response_type: event.response_type,
26
- groups: {}
28
+ groups: {},
27
29
  }
28
30
  end
29
-
30
- private
31
-
32
- def find_route(params)
33
- @app.routes.find do |route|
34
- route.controller == params['controller'] &&
35
- route.action == params['action']
36
- end
37
- end
38
31
  end
39
32
  end
40
33
  end