airbrake 9.5.0 → 11.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|