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