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
@@ -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
|
@@ -12,6 +14,7 @@ module Airbrake
|
|
12
14
|
# @see Airbrake#notify, #notify_airbrake_sync
|
13
15
|
def notify_airbrake(exception, params = {}, &block)
|
14
16
|
return unless (notice = build_notice(exception, params))
|
17
|
+
|
15
18
|
Airbrake.notify(notice, params, &block)
|
16
19
|
end
|
17
20
|
|
@@ -20,6 +23,7 @@ module Airbrake
|
|
20
23
|
# @see Airbrake#notify_sync, #notify_airbrake
|
21
24
|
def notify_airbrake_sync(exception, params = {}, &block)
|
22
25
|
return unless (notice = build_notice(exception, params))
|
26
|
+
|
23
27
|
Airbrake.notify_sync(notice, params, &block)
|
24
28
|
end
|
25
29
|
|
@@ -27,6 +31,7 @@ module Airbrake
|
|
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
|
-
|
22
|
-
|
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
|
-
|
27
|
+
timing: event.duration,
|
28
|
+
time: event.time,
|
24
29
|
}
|
25
30
|
|
26
31
|
Airbrake.notify_performance_breakdown(breakdown_info, stash)
|
@@ -1,46 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'airbrake/rails/event'
|
2
4
|
require 'airbrake/rails/app'
|
3
5
|
|
4
6
|
module Airbrake
|
5
7
|
module Rails
|
6
|
-
# @return [String]
|
7
|
-
CONTROLLER_KEY = 'controller'.freeze
|
8
|
-
|
9
|
-
# @return [String]
|
10
|
-
ACTION_KEY = 'action'.freeze
|
11
|
-
|
12
8
|
# ActionControllerRouteSubscriber sends route stat information, including
|
13
9
|
# performance data.
|
14
10
|
#
|
15
11
|
# @since v8.0.0
|
16
12
|
class ActionControllerRouteSubscriber
|
17
|
-
def initialize
|
18
|
-
@app = Airbrake::Rails::App.new
|
19
|
-
end
|
20
|
-
|
21
13
|
def call(*args)
|
14
|
+
return unless Airbrake::Config.instance.performance_stats
|
15
|
+
|
22
16
|
# We don't track routeless events.
|
23
17
|
return unless (routes = Airbrake::Rack::RequestStore[:routes])
|
24
18
|
|
25
19
|
event = Airbrake::Rails::Event.new(*args)
|
26
|
-
route =
|
20
|
+
route = Airbrake::Rails::App.recognize_route(
|
21
|
+
Airbrake::Rack::RequestStore[:request],
|
22
|
+
)
|
27
23
|
return unless route
|
28
24
|
|
29
25
|
routes[route.path] = {
|
30
26
|
method: event.method,
|
31
27
|
response_type: event.response_type,
|
32
|
-
groups: {}
|
28
|
+
groups: {},
|
33
29
|
}
|
34
30
|
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def find_route(params)
|
39
|
-
@app.routes.find do |route|
|
40
|
-
route.controller == params[CONTROLLER_KEY] &&
|
41
|
-
route.action == params[ACTION_KEY]
|
42
|
-
end
|
43
|
-
end
|
44
31
|
end
|
45
32
|
end
|
46
33
|
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(
|
26
|
+
queue: job.class.name,
|
27
|
+
error_count: 1,
|
28
|
+
timing: 0.01,
|
29
|
+
)
|
30
|
+
raise exception
|
31
|
+
else
|
32
|
+
Airbrake.notify_queue(
|
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
|
|
@@ -8,6 +10,8 @@ module Airbrake
|
|
8
10
|
# @since v8.1.0
|
9
11
|
class ActiveRecordSubscriber
|
10
12
|
def call(*args)
|
13
|
+
return unless Airbrake::Config.instance.query_stats
|
14
|
+
|
11
15
|
routes = Airbrake::Rack::RequestStore[:routes]
|
12
16
|
return if !routes || routes.none?
|
13
17
|
|
@@ -22,8 +26,8 @@ module Airbrake
|
|
22
26
|
func: frame[:function],
|
23
27
|
file: frame[:file],
|
24
28
|
line: frame[:line],
|
25
|
-
|
26
|
-
|
29
|
+
timing: event.duration,
|
30
|
+
time: event.time,
|
27
31
|
)
|
28
32
|
end
|
29
33
|
end
|
@@ -33,7 +37,7 @@ module Airbrake
|
|
33
37
|
def last_caller
|
34
38
|
exception = StandardError.new
|
35
39
|
exception.set_backtrace(
|
36
|
-
Airbrake::Rails::BacktraceCleaner.clean(Kernel.caller)
|
40
|
+
Airbrake::Rails::BacktraceCleaner.clean(Kernel.caller),
|
37
41
|
)
|
38
42
|
Airbrake::Backtrace.parse(exception).first || {}
|
39
43
|
end
|
data/lib/airbrake/rails/app.rb
CHANGED
@@ -1,43 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Airbrake
|
2
4
|
module Rails
|
3
|
-
# App is a wrapper around Rails.application
|
5
|
+
# App is a wrapper around Rails.application.
|
4
6
|
#
|
5
7
|
# @since v9.0.3
|
6
8
|
# @api private
|
7
9
|
class App
|
8
|
-
Route = Struct.new(:path
|
10
|
+
Route = Struct.new(:path)
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
# @param [] request
|
13
|
+
# @return [Airbrake::Rails::App::Route, nil]
|
14
|
+
# rubocop:disable Metrics/AbcSize
|
15
|
+
def self.recognize_route(request)
|
16
|
+
# Duplicate `request` because `recognize` *can* strip the request's
|
17
|
+
# `path_info`, which results in broken engine links (when the engine has
|
18
|
+
# an isolated namespace).
|
19
|
+
request_copy = request.dup
|
15
20
|
|
16
|
-
|
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']
|
17
26
|
|
18
|
-
|
19
|
-
#
|
20
|
-
|
21
|
-
|
27
|
+
# We must search every engine individually to find a concrete route. If
|
28
|
+
# we rely only on the `Rails.application.routes.router`, then the
|
29
|
+
# recognize call would return the root route, neglecting PATH_INFO
|
30
|
+
# completely. For example:
|
31
|
+
# * a request is made to `marketing#pricing`
|
32
|
+
# * `Rails.application` recognizes it as `marketing#/` (incorrect)
|
33
|
+
# * `Marketing::Engine` recognizes it as `marketing#/pricing` (correct)
|
34
|
+
engines.each do |engine|
|
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
|
22
39
|
|
23
|
-
|
24
|
-
|
25
|
-
|
40
|
+
# Skip "catch-all" routes such as:
|
41
|
+
# get '*path => 'pages#about'
|
42
|
+
#
|
43
|
+
# @todo The `glob?` method was added in Rails v4.2.0.beta1. We
|
44
|
+
# should remove the `respond_to?` check once we drop old Rails
|
45
|
+
# versions support.
|
46
|
+
#
|
47
|
+
# https://github.com/rails/rails/commit/5460591f0226a9d248b7b4f89186bd5553e7768f
|
48
|
+
next if route.respond_to?(:glob?) && route.glob?
|
26
49
|
|
27
|
-
|
50
|
+
path =
|
51
|
+
if engine == ::Rails.application
|
52
|
+
route.path.spec.to_s
|
53
|
+
else
|
54
|
+
"#{engine.engine_name}##{route.path.spec}"
|
55
|
+
end
|
56
|
+
|
57
|
+
# Rails can recognize multiple routes for the given request. For
|
58
|
+
# example, if we visit /users/2/edit, then Rails sees these routes:
|
59
|
+
# * "/users/:id/edit(.:format)"
|
60
|
+
# * "/"
|
61
|
+
#
|
62
|
+
# We return the first route as, what it seems, the most optimal
|
63
|
+
# approach.
|
64
|
+
return Route.new(path)
|
65
|
+
end
|
28
66
|
end
|
29
|
-
end
|
30
67
|
|
31
|
-
|
32
|
-
|
33
|
-
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
# rubocop:enable Metrics/AbcSize
|
34
71
|
|
35
|
-
|
36
|
-
|
37
|
-
rails_route.defaults[:controller],
|
38
|
-
rails_route.defaults[:action]
|
39
|
-
)
|
72
|
+
def self.engines
|
73
|
+
@engines ||= [*::Rails::Engine.subclasses, ::Rails.application]
|
40
74
|
end
|
75
|
+
private_class_method :engines
|
41
76
|
end
|
42
77
|
end
|
43
78
|
end
|
@@ -1,10 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Airbrake
|
2
4
|
module Rails
|
3
5
|
# BacktraceCleaner is a wrapper around Rails.backtrace_cleaner.
|
4
6
|
class BacktraceCleaner
|
7
|
+
# @return [Regexp]
|
8
|
+
AIRBRAKE_FRAME_PATTERN = %r{/airbrake/lib/airbrake/}.freeze
|
9
|
+
|
5
10
|
def self.clean(backtrace)
|
6
11
|
::Rails.backtrace_cleaner.clean(backtrace).first(1)
|
7
12
|
end
|
8
13
|
end
|
9
14
|
end
|
10
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
|
data/lib/airbrake/rails/curb.rb
CHANGED
@@ -1,35 +1,32 @@
|
|
1
|
-
|
2
|
-
# Monkey-patch to measure request timing.
|
3
|
-
class Easy
|
4
|
-
alias http_without_airbrake http
|
1
|
+
# frozen_string_literal: true
|
5
2
|
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
9
11
|
end
|
10
|
-
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
perform_without_airbrake(&block)
|
13
|
+
def perform(&block)
|
14
|
+
Airbrake::Rack.capture_timing(:http) do
|
15
|
+
super(&block)
|
16
|
+
end
|
17
17
|
end
|
18
18
|
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
module Curl
|
23
|
-
# Monkey-patch to measure request timing.
|
24
|
-
class Multi
|
25
|
-
class << self
|
26
|
-
alias http_without_airbrake http
|
27
19
|
|
20
|
+
# Allows measuring request timing.
|
21
|
+
module CurlMulti
|
28
22
|
def http(urls_with_config, multi_options = {}, &block)
|
29
23
|
Airbrake::Rack.capture_timing(:http) do
|
30
|
-
|
24
|
+
super(urls_with_config, multi_options, &block)
|
31
25
|
end
|
32
26
|
end
|
33
27
|
end
|
34
28
|
end
|
35
29
|
end
|
30
|
+
|
31
|
+
Curl::Easy.prepend(Airbrake::Rails::CurlEasy)
|
32
|
+
Curl::Multi.singleton_class.prepend(Airbrake::Rails::CurlMulti)
|
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.
|
@@ -6,7 +8,7 @@ module Airbrake
|
|
6
8
|
# @api private
|
7
9
|
class Event
|
8
10
|
# @see https://github.com/rails/rails/issues/8987
|
9
|
-
HTML_RESPONSE_WILDCARD = "*/*"
|
11
|
+
HTML_RESPONSE_WILDCARD = "*/*"
|
10
12
|
|
11
13
|
include Airbrake::Loggable
|
12
14
|
|
@@ -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
|
|