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
@@ -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
|
|