airbrake 9.5.0 → 11.0.1
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 +3 -1
- data/lib/airbrake/delayed_job.rb +24 -6
- data/lib/airbrake/logger.rb +2 -0
- data/lib/airbrake/rack.rb +4 -0
- data/lib/airbrake/rack/context_filter.rb +9 -2
- data/lib/airbrake/rack/http_headers_filter.rb +7 -5
- 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 +8 -10
- data/lib/airbrake/rack/session_filter.rb +2 -0
- data/lib/airbrake/rack/user.rb +4 -0
- data/lib/airbrake/rack/user_filter.rb +2 -0
- data/lib/airbrake/rails.rb +8 -8
- 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 +5 -0
- data/lib/airbrake/rails/action_controller_notify_subscriber.rb +6 -2
- data/lib/airbrake/rails/action_controller_performance_breakdown_subscriber.rb +6 -1
- data/lib/airbrake/rails/action_controller_route_subscriber.rb +8 -21
- 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 +7 -3
- data/lib/airbrake/rails/app.rb +60 -25
- data/lib/airbrake/rails/backtrace_cleaner.rb +13 -0
- data/lib/airbrake/rails/curb.rb +19 -22
- data/lib/airbrake/rails/event.rb +4 -6
- data/lib/airbrake/rails/excon_subscriber.rb +4 -0
- data/lib/airbrake/rails/http.rb +2 -0
- data/lib/airbrake/rails/http_client.rb +12 -6
- data/lib/airbrake/rails/net_http.rb +14 -6
- data/lib/airbrake/rails/railtie.rb +55 -45
- data/lib/airbrake/rails/typhoeus.rb +11 -7
- data/lib/airbrake/rake.rb +3 -1
- data/lib/airbrake/rake/tasks.rb +7 -5
- data/lib/airbrake/resque.rb +32 -0
- data/lib/airbrake/shoryuken.rb +17 -3
- data/lib/airbrake/sidekiq.rb +22 -14
- data/lib/airbrake/sidekiq/retryable_jobs_filter.rb +2 -0
- data/lib/airbrake/sneakers.rb +39 -1
- data/lib/airbrake/version.rb +3 -1
- data/lib/generators/airbrake_generator.rb +2 -0
- data/lib/generators/airbrake_initializer.rb.erb +5 -3
- metadata +61 -90
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4b780328fc01621a0273577655179008c6314c6fe5187e5562f15c5112f296cd
|
4
|
+
data.tar.gz: 3036e80e03bbb2fa4159a9167a6971adcda2dcbee789ed3db320c0eaf0408d9b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f307768db070afd129623ed312e7f4da4e4040f1bdf33cb9d1e31f2f1ee4278e992eac0e901a6bb257042ff9aff5f651c2a8bd74579606771c56d4040e63c0b1
|
7
|
+
data.tar.gz: b6376325c1b352ac7f13a0a0467737fb2a8cb08a12748c10d991265342119bcd6f510271744facb11018312de593857835aceadef557c20bbf1e85a2296e7bd4
|
data/lib/airbrake.rb
CHANGED
data/lib/airbrake/capistrano.rb
CHANGED
@@ -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
|
-
|
16
|
+
CMD
|
15
17
|
|
16
18
|
info 'Notified Airbrake of the deploy'
|
17
19
|
end
|
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(
|
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
|
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
|
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] =
|
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
|
-
|
11
|
-
|
12
|
-
|
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
|
# 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"
|
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,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.
|
27
|
-
|
28
|
-
|
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
|
|
data/lib/airbrake/rack/user.rb
CHANGED
@@ -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
|
|
data/lib/airbrake/rails.rb
CHANGED
@@ -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:
|
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
|