airbrake 9.5.3 → 10.0.2
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.
- checksums.yaml +4 -4
- data/lib/airbrake.rb +2 -0
- data/lib/airbrake/capistrano.rb +2 -0
- data/lib/airbrake/capistrano/capistrano2.rb +2 -0
- data/lib/airbrake/capistrano/capistrano3.rb +2 -0
- data/lib/airbrake/delayed_job.rb +24 -6
- data/lib/airbrake/logger.rb +2 -0
- data/lib/airbrake/rack.rb +2 -0
- data/lib/airbrake/rack/context_filter.rb +9 -2
- data/lib/airbrake/rack/http_headers_filter.rb +4 -2
- data/lib/airbrake/rack/http_params_filter.rb +2 -0
- data/lib/airbrake/rack/instrumentable.rb +112 -4
- data/lib/airbrake/rack/middleware.rb +3 -1
- data/lib/airbrake/rack/request_body_filter.rb +2 -0
- data/lib/airbrake/rack/request_store.rb +2 -0
- data/lib/airbrake/rack/route_filter.rb +3 -1
- data/lib/airbrake/rack/session_filter.rb +2 -0
- data/lib/airbrake/rack/user.rb +2 -0
- data/lib/airbrake/rack/user_filter.rb +2 -0
- data/lib/airbrake/rails.rb +3 -1
- data/lib/airbrake/rails/action_cable.rb +2 -0
- data/lib/airbrake/rails/action_cable/notify_callback.rb +2 -0
- data/lib/airbrake/rails/action_controller.rb +2 -0
- data/lib/airbrake/rails/action_controller_notify_subscriber.rb +4 -2
- data/lib/airbrake/rails/action_controller_performance_breakdown_subscriber.rb +4 -1
- data/lib/airbrake/rails/action_controller_route_subscriber.rb +5 -11
- data/lib/airbrake/rails/active_job.rb +25 -8
- data/lib/airbrake/rails/active_record.rb +2 -0
- data/lib/airbrake/rails/active_record_subscriber.rb +5 -3
- data/lib/airbrake/rails/app.rb +43 -9
- data/lib/airbrake/rails/backtrace_cleaner.rb +2 -0
- data/lib/airbrake/rails/curb.rb +2 -0
- data/lib/airbrake/rails/event.rb +3 -5
- data/lib/airbrake/rails/excon_subscriber.rb +2 -0
- data/lib/airbrake/rails/http.rb +2 -0
- data/lib/airbrake/rails/http_client.rb +2 -0
- data/lib/airbrake/rails/net_http.rb +14 -6
- data/lib/airbrake/rails/railtie.rb +11 -9
- data/lib/airbrake/rails/typhoeus.rb +2 -0
- data/lib/airbrake/rake.rb +3 -1
- data/lib/airbrake/rake/tasks.rb +6 -4
- data/lib/airbrake/resque.rb +32 -0
- data/lib/airbrake/shoryuken.rb +17 -3
- data/lib/airbrake/sidekiq.rb +20 -13
- data/lib/airbrake/sidekiq/retryable_jobs_filter.rb +2 -0
- data/lib/airbrake/sneakers.rb +38 -1
- data/lib/airbrake/version.rb +3 -1
- data/lib/generators/airbrake_generator.rb +2 -0
- data/lib/generators/airbrake_initializer.rb.erb +2 -0
- metadata +19 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5138c1675426cc908ae8b1857898ddc04c135fda23dfe21db19e6b1bf0b36faf
|
4
|
+
data.tar.gz: '085abd9125a9a615935ac2b1171cdfd74205bde892708e073bfbda44103c72ca'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39bc3cfe3510b0db2adece334666b4e753298fd4d09f4d7d445086c0512aa0436ec8c5b38a85933fae822efeea155d7d2bea7e25866e169878b6f1b330fb4d4d
|
7
|
+
data.tar.gz: 9c189959ccdfa6c30752462dffb49e8925df101f18597180ab6a31e2320a620431bcbcf6d66c83838d1acfd9be1b6c98623253e60cea77e8881fc84e5996aa14
|
data/lib/airbrake.rb
CHANGED
data/lib/airbrake/capistrano.rb
CHANGED
data/lib/airbrake/delayed_job.rb
CHANGED
@@ -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
|
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
|
-
|
10
|
-
|
11
|
-
|
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] =
|
28
|
+
notice[:context][:action] = action
|
23
29
|
end
|
24
30
|
|
31
|
+
::Airbrake.notify_queue_sync(
|
32
|
+
queue: action,
|
33
|
+
error_count: 1,
|
34
|
+
timing: 0.01,
|
35
|
+
)
|
36
|
+
|
25
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
|
+
)
|
26
44
|
end
|
27
45
|
end
|
28
46
|
end
|
29
47
|
end
|
30
|
-
# rubocop:enable
|
48
|
+
# rubocop:enable Metrics/BlockLength
|
31
49
|
end
|
32
50
|
end
|
33
51
|
|
data/lib/airbrake/logger.rb
CHANGED
data/lib/airbrake/rack.rb
CHANGED
@@ -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] =
|
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
|
# 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
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
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
|
require 'airbrake/rails/app'
|
2
4
|
|
3
5
|
module Airbrake
|
@@ -26,7 +28,7 @@ module Airbrake
|
|
26
28
|
|
27
29
|
def rails_route(request)
|
28
30
|
return unless (route = Airbrake::Rails::App.recognize_route(request))
|
29
|
-
route.path
|
31
|
+
route.path
|
30
32
|
end
|
31
33
|
|
32
34
|
def sinatra_route(request)
|
data/lib/airbrake/rack/user.rb
CHANGED
data/lib/airbrake/rails.rb
CHANGED
@@ -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/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
|
-
|
22
|
-
|
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
|
-
|
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,24 +16,16 @@ 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
|
-
routes[
|
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
|
-
|
28
|
-
def find_route_name(route)
|
29
|
-
if route.app.respond_to?(:app) && route.app.app.respond_to?(:engine_name)
|
30
|
-
"#{route.app.app.engine_name}##{route.path.spec}"
|
31
|
-
else
|
32
|
-
route.path.spec.to_s
|
33
|
-
end
|
34
|
-
end
|
35
29
|
end
|
36
30
|
end
|
37
31
|
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_sync(
|
26
|
+
queue: job.class.name,
|
27
|
+
error_count: 1,
|
28
|
+
timing: 0.01,
|
29
|
+
)
|
30
|
+
raise exception
|
31
|
+
else
|
32
|
+
Airbrake.notify_queue_sync(
|
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
|
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
|
-
|
26
|
-
|
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
|
data/lib/airbrake/rails/app.rb
CHANGED
@@ -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.
|
@@ -5,18 +7,50 @@ module Airbrake
|
|
5
7
|
# @since v9.0.3
|
6
8
|
# @api private
|
7
9
|
class App
|
10
|
+
Route = Struct.new(:path)
|
11
|
+
|
12
|
+
# @param [] request
|
13
|
+
# @return [Airbrake::Rails::App::Route, nil]
|
8
14
|
def self.recognize_route(request)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
# Duplicate `request` because `recognize` *can* strip the request's
|
16
|
+
# `path_info`, which results in broken engine links (when the engine has
|
17
|
+
# an isolated namespace).
|
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
|
+
path =
|
30
|
+
if engine == ::Rails.application
|
31
|
+
route.path.spec.to_s
|
32
|
+
else
|
33
|
+
"#{engine.engine_name}##{route.path.spec}"
|
34
|
+
end
|
35
|
+
|
36
|
+
# Rails can recognize multiple routes for the given request. For
|
37
|
+
# example, if we visit /users/2/edit, then Rails sees these routes:
|
38
|
+
# * "/users/:id/edit(.:format)"
|
39
|
+
# * "/"
|
40
|
+
#
|
41
|
+
# We return the first route as, what it seems, the most optimal
|
42
|
+
# approach.
|
43
|
+
return Route.new(path)
|
44
|
+
end
|
18
45
|
end
|
46
|
+
|
47
|
+
nil
|
19
48
|
end
|
49
|
+
|
50
|
+
def self.engines
|
51
|
+
@engines ||= [*::Rails::Engine.subclasses, ::Rails.application]
|
52
|
+
end
|
53
|
+
private_class_method :engines
|
20
54
|
end
|
21
55
|
end
|
22
56
|
end
|
data/lib/airbrake/rails/curb.rb
CHANGED
data/lib/airbrake/rails/event.rb
CHANGED
@@ -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
|
|
data/lib/airbrake/rails/http.rb
CHANGED
@@ -1,10 +1,18 @@
|
|
1
|
-
#
|
2
|
-
Net::HTTP.class_eval do
|
3
|
-
alias_method :request_without_airbrake, :request
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
data/lib/airbrake/rake.rb
CHANGED
@@ -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)
|
data/lib/airbrake/rake/tasks.rb
CHANGED
@@ -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
|
data/lib/airbrake/resque.rb
CHANGED
@@ -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
|
data/lib/airbrake/shoryuken.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/airbrake/sidekiq.rb
CHANGED
@@ -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
|
-
|
10
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
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
|
data/lib/airbrake/sneakers.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/airbrake/version.rb
CHANGED
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:
|
4
|
+
version: 10.0.2
|
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:
|
11
|
+
date: 2020-03-31 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.
|
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.
|
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
|
-
|
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
|