airbrake 9.5.0
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 +7 -0
- data/lib/airbrake.rb +30 -0
- data/lib/airbrake/capistrano.rb +6 -0
- data/lib/airbrake/capistrano/capistrano2.rb +38 -0
- data/lib/airbrake/capistrano/capistrano3.rb +21 -0
- data/lib/airbrake/delayed_job.rb +48 -0
- data/lib/airbrake/logger.rb +101 -0
- data/lib/airbrake/rack.rb +35 -0
- data/lib/airbrake/rack/context_filter.rb +58 -0
- data/lib/airbrake/rack/http_headers_filter.rb +42 -0
- data/lib/airbrake/rack/http_params_filter.rb +25 -0
- data/lib/airbrake/rack/instrumentable.rb +28 -0
- data/lib/airbrake/rack/middleware.rb +100 -0
- data/lib/airbrake/rack/request_body_filter.rb +31 -0
- data/lib/airbrake/rack/request_store.rb +32 -0
- data/lib/airbrake/rack/route_filter.rb +53 -0
- data/lib/airbrake/rack/session_filter.rb +23 -0
- data/lib/airbrake/rack/user.rb +70 -0
- data/lib/airbrake/rack/user_filter.rb +23 -0
- data/lib/airbrake/rails.rb +32 -0
- data/lib/airbrake/rails/action_cable.rb +33 -0
- data/lib/airbrake/rails/action_cable/notify_callback.rb +20 -0
- data/lib/airbrake/rails/action_controller.rb +35 -0
- data/lib/airbrake/rails/action_controller_notify_subscriber.rb +28 -0
- data/lib/airbrake/rails/action_controller_performance_breakdown_subscriber.rb +46 -0
- data/lib/airbrake/rails/action_controller_route_subscriber.rb +46 -0
- data/lib/airbrake/rails/active_job.rb +33 -0
- data/lib/airbrake/rails/active_record.rb +34 -0
- data/lib/airbrake/rails/active_record_subscriber.rb +42 -0
- data/lib/airbrake/rails/app.rb +43 -0
- data/lib/airbrake/rails/backtrace_cleaner.rb +10 -0
- data/lib/airbrake/rails/curb.rb +35 -0
- data/lib/airbrake/rails/event.rb +83 -0
- data/lib/airbrake/rails/excon_subscriber.rb +21 -0
- data/lib/airbrake/rails/http.rb +12 -0
- data/lib/airbrake/rails/http_client.rb +10 -0
- data/lib/airbrake/rails/net_http.rb +10 -0
- data/lib/airbrake/rails/railtie.rb +141 -0
- data/lib/airbrake/rails/typhoeus.rb +12 -0
- data/lib/airbrake/rake.rb +63 -0
- data/lib/airbrake/rake/tasks.rb +110 -0
- data/lib/airbrake/resque.rb +29 -0
- data/lib/airbrake/shoryuken.rb +40 -0
- data/lib/airbrake/sidekiq.rb +47 -0
- data/lib/airbrake/sidekiq/retryable_jobs_filter.rb +51 -0
- data/lib/airbrake/sneakers.rb +34 -0
- data/lib/airbrake/version.rb +5 -0
- data/lib/generators/airbrake_generator.rb +23 -0
- data/lib/generators/airbrake_initializer.rb.erb +78 -0
- metadata +416 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'airbrake/rails/event'
|
2
|
+
|
3
|
+
module Airbrake
|
4
|
+
module Rails
|
5
|
+
# ActionControllerNotifySubscriber sends route stat information, including
|
6
|
+
# performance data.
|
7
|
+
#
|
8
|
+
# @since v8.0.0
|
9
|
+
class ActionControllerNotifySubscriber
|
10
|
+
def call(*args)
|
11
|
+
routes = Airbrake::Rack::RequestStore[:routes]
|
12
|
+
return if !routes || routes.none?
|
13
|
+
|
14
|
+
event = Airbrake::Rails::Event.new(*args)
|
15
|
+
|
16
|
+
routes.each do |route, _params|
|
17
|
+
Airbrake.notify_request(
|
18
|
+
method: event.method,
|
19
|
+
route: route,
|
20
|
+
status_code: event.status_code,
|
21
|
+
start_time: event.time,
|
22
|
+
end_time: Time.new
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'airbrake/rails/event'
|
2
|
+
|
3
|
+
module Airbrake
|
4
|
+
module Rails
|
5
|
+
# @since v8.3.0
|
6
|
+
class ActionControllerPerformanceBreakdownSubscriber
|
7
|
+
def call(*args)
|
8
|
+
routes = Airbrake::Rack::RequestStore[:routes]
|
9
|
+
return if !routes || routes.none?
|
10
|
+
|
11
|
+
event = Airbrake::Rails::Event.new(*args)
|
12
|
+
stash = build_stash
|
13
|
+
|
14
|
+
routes.each do |route, params|
|
15
|
+
groups = event.groups.merge(params[:groups])
|
16
|
+
next if groups.none?
|
17
|
+
|
18
|
+
breakdown_info = {
|
19
|
+
method: event.method,
|
20
|
+
route: route,
|
21
|
+
response_type: event.response_type,
|
22
|
+
groups: groups,
|
23
|
+
start_time: event.time
|
24
|
+
}
|
25
|
+
|
26
|
+
Airbrake.notify_performance_breakdown(breakdown_info, stash)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def build_stash
|
33
|
+
stash = {}
|
34
|
+
request = Airbrake::Rack::RequestStore[:request]
|
35
|
+
return stash unless request
|
36
|
+
|
37
|
+
stash[:request] = request
|
38
|
+
if (user = Airbrake::Rack::User.extract(request.env))
|
39
|
+
stash.merge!(user.as_json)
|
40
|
+
end
|
41
|
+
|
42
|
+
stash
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'airbrake/rails/event'
|
2
|
+
require 'airbrake/rails/app'
|
3
|
+
|
4
|
+
module Airbrake
|
5
|
+
module Rails
|
6
|
+
# @return [String]
|
7
|
+
CONTROLLER_KEY = 'controller'.freeze
|
8
|
+
|
9
|
+
# @return [String]
|
10
|
+
ACTION_KEY = 'action'.freeze
|
11
|
+
|
12
|
+
# ActionControllerRouteSubscriber sends route stat information, including
|
13
|
+
# performance data.
|
14
|
+
#
|
15
|
+
# @since v8.0.0
|
16
|
+
class ActionControllerRouteSubscriber
|
17
|
+
def initialize
|
18
|
+
@app = Airbrake::Rails::App.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def call(*args)
|
22
|
+
# We don't track routeless events.
|
23
|
+
return unless (routes = Airbrake::Rack::RequestStore[:routes])
|
24
|
+
|
25
|
+
event = Airbrake::Rails::Event.new(*args)
|
26
|
+
route = find_route(event.params)
|
27
|
+
return unless route
|
28
|
+
|
29
|
+
routes[route.path] = {
|
30
|
+
method: event.method,
|
31
|
+
response_type: event.response_type,
|
32
|
+
groups: {}
|
33
|
+
}
|
34
|
+
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
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Airbrake
|
2
|
+
module Rails
|
3
|
+
# Enables support for exceptions occurring in ActiveJob jobs.
|
4
|
+
module ActiveJob
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
# @return [Array<Regexp>] the list of known adapters
|
8
|
+
ADAPTERS = [/Resque/, /Sidekiq/, /DelayedJob/].freeze
|
9
|
+
|
10
|
+
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
|
+
notice = Airbrake.build_notice(exception)
|
17
|
+
notice[:context][:component] = 'active_job'
|
18
|
+
notice[:context][:action] = job.class.name
|
19
|
+
notice[:params].merge!(job.serialize)
|
20
|
+
|
21
|
+
Airbrake.notify(notice)
|
22
|
+
|
23
|
+
raise exception
|
24
|
+
end
|
25
|
+
|
26
|
+
included do
|
27
|
+
rescue_from(Exception) do |exception|
|
28
|
+
Airbrake::Rails::ActiveJob.notify_airbrake(exception, self)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Airbrake
|
2
|
+
module Rails
|
3
|
+
# Rails <4.2 has a bug with regard to swallowing exceptions in the
|
4
|
+
# +after_commit+ and the +after_rollback+ hooks: it doesn't bubble up
|
5
|
+
# exceptions from there.
|
6
|
+
#
|
7
|
+
# This module makes it possible to report exceptions occurring there.
|
8
|
+
#
|
9
|
+
# @see https://github.com/rails/rails/pull/14488 Detailed description of the
|
10
|
+
# bug and the fix
|
11
|
+
# @see https://goo.gl/348lor Rails 4.2+ implementation (fixed)
|
12
|
+
# @see https://goo.gl/ddFNg7 Rails <4.2 implementation (bugged)
|
13
|
+
module ActiveRecord
|
14
|
+
# Patches default +run_callbacks+ with our version, which is capable of
|
15
|
+
# notifying about exceptions.
|
16
|
+
#
|
17
|
+
# rubocop:disable Lint/RescueException
|
18
|
+
def run_callbacks(kind, *args, &block)
|
19
|
+
# Let the post process handle the exception if it's not a bugged hook.
|
20
|
+
return super unless %i[commit rollback].include?(kind)
|
21
|
+
|
22
|
+
# Handle the exception ourselves. The 'ex' exception won't be
|
23
|
+
# propagated, therefore we must notify it here.
|
24
|
+
begin
|
25
|
+
super
|
26
|
+
rescue Exception => ex
|
27
|
+
Airbrake.notify(ex)
|
28
|
+
raise ex
|
29
|
+
end
|
30
|
+
end
|
31
|
+
# rubocop:enable Lint/RescueException
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'airbrake/rails/event'
|
2
|
+
require 'airbrake/rails/backtrace_cleaner'
|
3
|
+
|
4
|
+
module Airbrake
|
5
|
+
module Rails
|
6
|
+
# ActiveRecordSubscriber sends SQL information, including performance data.
|
7
|
+
#
|
8
|
+
# @since v8.1.0
|
9
|
+
class ActiveRecordSubscriber
|
10
|
+
def call(*args)
|
11
|
+
routes = Airbrake::Rack::RequestStore[:routes]
|
12
|
+
return if !routes || routes.none?
|
13
|
+
|
14
|
+
event = Airbrake::Rails::Event.new(*args)
|
15
|
+
frame = last_caller
|
16
|
+
|
17
|
+
routes.each do |route, params|
|
18
|
+
Airbrake.notify_query(
|
19
|
+
route: route,
|
20
|
+
method: params[:method],
|
21
|
+
query: event.sql,
|
22
|
+
func: frame[:function],
|
23
|
+
file: frame[:file],
|
24
|
+
line: frame[:line],
|
25
|
+
start_time: event.time,
|
26
|
+
end_time: event.end
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def last_caller
|
34
|
+
exception = StandardError.new
|
35
|
+
exception.set_backtrace(
|
36
|
+
Airbrake::Rails::BacktraceCleaner.clean(Kernel.caller)
|
37
|
+
)
|
38
|
+
Airbrake::Backtrace.parse(exception).first || {}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Airbrake
|
2
|
+
module Rails
|
3
|
+
# App is a wrapper around Rails.application and Rails::Engine.
|
4
|
+
#
|
5
|
+
# @since v9.0.3
|
6
|
+
# @api private
|
7
|
+
class App
|
8
|
+
Route = Struct.new(:path, :controller, :action)
|
9
|
+
|
10
|
+
def routes
|
11
|
+
@routes ||= app_routes.merge(engine_routes).flat_map do |(engine_name, routes)|
|
12
|
+
routes.map { |rails_route| build_route(engine_name, rails_route) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def app_routes
|
19
|
+
# Engine name is nil because this is default (non-engine) routes.
|
20
|
+
{ nil => ::Rails.application.routes.routes.routes }
|
21
|
+
end
|
22
|
+
|
23
|
+
def engine_routes
|
24
|
+
::Rails::Engine.subclasses.flat_map.with_object({}) do |engine, hash|
|
25
|
+
next if (eng_routes = engine.routes.routes.routes).none?
|
26
|
+
|
27
|
+
hash[engine.engine_name] = eng_routes
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def build_route(engine_name, rails_route)
|
32
|
+
engine_prefix = engine_name
|
33
|
+
engine_prefix += '#' if engine_prefix
|
34
|
+
|
35
|
+
Route.new(
|
36
|
+
"#{engine_prefix}#{rails_route.path.spec}",
|
37
|
+
rails_route.defaults[:controller],
|
38
|
+
rails_route.defaults[:action]
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Curl
|
2
|
+
# Monkey-patch to measure request timing.
|
3
|
+
class Easy
|
4
|
+
alias http_without_airbrake http
|
5
|
+
|
6
|
+
def http(verb)
|
7
|
+
Airbrake::Rack.capture_timing(:http) do
|
8
|
+
http_without_airbrake(verb)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
alias perform_without_airbrake perform
|
13
|
+
|
14
|
+
def perform(&block)
|
15
|
+
Airbrake::Rack.capture_timing(:http) do
|
16
|
+
perform_without_airbrake(&block)
|
17
|
+
end
|
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
|
+
|
28
|
+
def http(urls_with_config, multi_options = {}, &block)
|
29
|
+
Airbrake::Rack.capture_timing(:http) do
|
30
|
+
http_without_airbrake(urls_with_config, multi_options, &block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Airbrake
|
2
|
+
module Rails
|
3
|
+
# Event is a wrapper around ActiveSupport::Notifications::Event.
|
4
|
+
#
|
5
|
+
# @since v9.0.3
|
6
|
+
# @api private
|
7
|
+
class Event
|
8
|
+
# @see https://github.com/rails/rails/issues/8987
|
9
|
+
HTML_RESPONSE_WILDCARD = "*/*".freeze
|
10
|
+
|
11
|
+
include Airbrake::Loggable
|
12
|
+
|
13
|
+
def initialize(*args)
|
14
|
+
@event = ActiveSupport::Notifications::Event.new(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def method
|
18
|
+
@event.payload[:method]
|
19
|
+
end
|
20
|
+
|
21
|
+
def response_type
|
22
|
+
response_type = @event.payload[:format]
|
23
|
+
response_type == HTML_RESPONSE_WILDCARD ? :html : response_type
|
24
|
+
end
|
25
|
+
|
26
|
+
def params
|
27
|
+
@event.payload[:params]
|
28
|
+
end
|
29
|
+
|
30
|
+
def sql
|
31
|
+
@event.payload[:sql]
|
32
|
+
end
|
33
|
+
|
34
|
+
def db_runtime
|
35
|
+
@db_runtime ||= @event.payload[:db_runtime] || 0
|
36
|
+
end
|
37
|
+
|
38
|
+
def view_runtime
|
39
|
+
@view_runtime ||= @event.payload[:view_runtime] || 0
|
40
|
+
end
|
41
|
+
|
42
|
+
def time
|
43
|
+
@event.time
|
44
|
+
end
|
45
|
+
|
46
|
+
def end
|
47
|
+
@event.end
|
48
|
+
end
|
49
|
+
|
50
|
+
def groups
|
51
|
+
groups = {}
|
52
|
+
groups[:db] = db_runtime if db_runtime > 0
|
53
|
+
groups[:view] = view_runtime if view_runtime > 0
|
54
|
+
groups
|
55
|
+
end
|
56
|
+
|
57
|
+
def status_code
|
58
|
+
return @event.payload[:status] if @event.payload[:status]
|
59
|
+
|
60
|
+
if @event.payload[:exception]
|
61
|
+
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(
|
62
|
+
@event.payload[:exception].first
|
63
|
+
)
|
64
|
+
status = 500 if status == 0
|
65
|
+
|
66
|
+
return status
|
67
|
+
end
|
68
|
+
|
69
|
+
# The ActiveSupport event doesn't have status only in two cases:
|
70
|
+
# - an exception was thrown
|
71
|
+
# - unauthorized access
|
72
|
+
# We have already handled the exception so what's left is unauthorized
|
73
|
+
# access. There's no way to know for sure it's unauthorized access, so
|
74
|
+
# we are rather optimistic here.
|
75
|
+
401
|
76
|
+
end
|
77
|
+
|
78
|
+
def duration
|
79
|
+
@event.duration
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'airbrake/rails/event'
|
2
|
+
|
3
|
+
module Airbrake
|
4
|
+
module Rails
|
5
|
+
# @api private
|
6
|
+
# @since v9.2.0
|
7
|
+
class Excon
|
8
|
+
def call(*args)
|
9
|
+
routes = Airbrake::Rack::RequestStore[:routes]
|
10
|
+
return if !routes || routes.none?
|
11
|
+
|
12
|
+
event = Airbrake::Rails::Event.new(*args)
|
13
|
+
|
14
|
+
routes.each do |_route_path, params|
|
15
|
+
params[:groups][:http] ||= 0
|
16
|
+
params[:groups][:http] += event.duration
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module HTTP
|
2
|
+
# Monkey-patch to measure request timing.
|
3
|
+
class Client
|
4
|
+
alias perform_without_airbrake perform
|
5
|
+
|
6
|
+
def perform(request, options)
|
7
|
+
Airbrake::Rack.capture_timing(:http) do
|
8
|
+
perform_without_airbrake(request, options)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|