celerbrake 0.1.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/celerbrake/capistrano/capistrano2.rb +40 -0
- data/lib/celerbrake/capistrano/capistrano3.rb +23 -0
- data/lib/celerbrake/capistrano.rb +8 -0
- data/lib/celerbrake/delayed_job.rb +64 -0
- data/lib/celerbrake/logger.rb +105 -0
- data/lib/celerbrake/rack/context_filter.rb +69 -0
- data/lib/celerbrake/rack/http_headers_filter.rb +44 -0
- data/lib/celerbrake/rack/http_params_filter.rb +27 -0
- data/lib/celerbrake/rack/instrumentable.rb +140 -0
- data/lib/celerbrake/rack/middleware.rb +102 -0
- data/lib/celerbrake/rack/request_body_filter.rb +33 -0
- data/lib/celerbrake/rack/request_store.rb +34 -0
- data/lib/celerbrake/rack/route_filter.rb +51 -0
- data/lib/celerbrake/rack/session_filter.rb +25 -0
- data/lib/celerbrake/rack/user.rb +74 -0
- data/lib/celerbrake/rack/user_filter.rb +25 -0
- data/lib/celerbrake/rack.rb +39 -0
- data/lib/celerbrake/rails/action_cable/notify_callback.rb +22 -0
- data/lib/celerbrake/rails/action_cable.rb +37 -0
- data/lib/celerbrake/rails/action_controller.rb +40 -0
- data/lib/celerbrake/rails/action_controller_notify_subscriber.rb +32 -0
- data/lib/celerbrake/rails/action_controller_performance_breakdown_subscriber.rb +51 -0
- data/lib/celerbrake/rails/action_controller_route_subscriber.rb +33 -0
- data/lib/celerbrake/rails/active_job.rb +50 -0
- data/lib/celerbrake/rails/active_record.rb +36 -0
- data/lib/celerbrake/rails/active_record_subscriber.rb +46 -0
- data/lib/celerbrake/rails/app.rb +78 -0
- data/lib/celerbrake/rails/backtrace_cleaner.rb +23 -0
- data/lib/celerbrake/rails/curb.rb +32 -0
- data/lib/celerbrake/rails/event.rb +93 -0
- data/lib/celerbrake/rails/excon_subscriber.rb +25 -0
- data/lib/celerbrake/rails/http.rb +18 -0
- data/lib/celerbrake/rails/http_client.rb +16 -0
- data/lib/celerbrake/rails/net_http.rb +18 -0
- data/lib/celerbrake/rails/railtie.rb +54 -0
- data/lib/celerbrake/rails/railties/action_controller_tie.rb +90 -0
- data/lib/celerbrake/rails/railties/active_record_tie.rb +74 -0
- data/lib/celerbrake/rails/railties/middleware_tie.rb +62 -0
- data/lib/celerbrake/rails/typhoeus.rb +16 -0
- data/lib/celerbrake/rails.rb +32 -0
- data/lib/celerbrake/rake/tasks.rb +112 -0
- data/lib/celerbrake/rake.rb +66 -0
- data/lib/celerbrake/resque.rb +62 -0
- data/lib/celerbrake/shoryuken.rb +52 -0
- data/lib/celerbrake/sidekiq/retryable_jobs_filter.rb +53 -0
- data/lib/celerbrake/sidekiq.rb +53 -0
- data/lib/celerbrake/sneakers.rb +72 -0
- data/lib/celerbrake/version.rb +7 -0
- data/lib/celerbrake.rb +32 -0
- data/lib/generators/celerbrake_generator.rb +22 -0
- data/lib/generators/celerbrake_initializer.rb.erb +80 -0
- metadata +380 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Celerbrake
|
|
4
|
+
module Rails
|
|
5
|
+
# BacktraceCleaner is a wrapper around Rails.backtrace_cleaner.
|
|
6
|
+
class BacktraceCleaner
|
|
7
|
+
# @return [Regexp]
|
|
8
|
+
CELERBRAKE_FRAME_PATTERN = %r{/celerbrake/lib/celerbrake/}.freeze
|
|
9
|
+
|
|
10
|
+
def self.clean(backtrace)
|
|
11
|
+
::Rails.backtrace_cleaner.clean(backtrace).first(1)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
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 =~ Celerbrake::Rails::BacktraceCleaner::CELERBRAKE_FRAME_PATTERN
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Celerbrake
|
|
4
|
+
module Rails
|
|
5
|
+
# Allows measuring request timing.
|
|
6
|
+
module CurlEasy
|
|
7
|
+
def http(verb)
|
|
8
|
+
Celerbrake::Rack.capture_timing(:http) do
|
|
9
|
+
super(verb)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def perform(&block)
|
|
14
|
+
Celerbrake::Rack.capture_timing(:http) do
|
|
15
|
+
super(&block)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Allows measuring request timing.
|
|
21
|
+
module CurlMulti
|
|
22
|
+
def http(urls_with_config, multi_options = {}, &block)
|
|
23
|
+
Celerbrake::Rack.capture_timing(:http) do
|
|
24
|
+
super(urls_with_config, multi_options, &block)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
Curl::Easy.prepend(Celerbrake::Rails::CurlEasy)
|
|
32
|
+
Curl::Multi.singleton_class.prepend(Celerbrake::Rails::CurlMulti)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Celerbrake
|
|
4
|
+
module Rails
|
|
5
|
+
# Event is a wrapper around ActiveSupport::Notifications::Event.
|
|
6
|
+
#
|
|
7
|
+
# @since v9.0.3
|
|
8
|
+
# @api private
|
|
9
|
+
class Event
|
|
10
|
+
# @see https://github.com/rails/rails/issues/8987
|
|
11
|
+
HTML_RESPONSE_WILDCARD = "*/*"
|
|
12
|
+
|
|
13
|
+
# @return [Integer]
|
|
14
|
+
MILLISECOND = 1000
|
|
15
|
+
|
|
16
|
+
include Celerbrake::Loggable
|
|
17
|
+
|
|
18
|
+
def initialize(*args)
|
|
19
|
+
@event = ActiveSupport::Notifications::Event.new(*args)
|
|
20
|
+
@rails_7_or_greater = ::Rails::VERSION::MAJOR >= 7
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def method
|
|
24
|
+
@event.payload[:method]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def response_type
|
|
28
|
+
response_type = @event.payload[:format]
|
|
29
|
+
response_type == HTML_RESPONSE_WILDCARD ? :html : response_type
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def params
|
|
33
|
+
@event.payload[:params]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def sql
|
|
37
|
+
@event.payload[:sql]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def db_runtime
|
|
41
|
+
@db_runtime ||= @event.payload[:db_runtime] || 0
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def view_runtime
|
|
45
|
+
@view_runtime ||= @event.payload[:view_runtime] || 0
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def time
|
|
49
|
+
# On Rails 7+ `ActiveSupport::Notifications::Event#time` returns an
|
|
50
|
+
# instance of Float. It represents monotonic time in milliseconds.
|
|
51
|
+
# Celerbrake Ruby expects that the provided time is in seconds. Hence,
|
|
52
|
+
# we need to convert it from milliseconds to seconds. In the
|
|
53
|
+
# versions below Rails 7, time is an instance of Time.
|
|
54
|
+
#
|
|
55
|
+
# Relevant commit:
|
|
56
|
+
# https://github.com/rails/rails/commit/81d0dc90becfe0b8e7f7f26beb66c25d84b8ec7f
|
|
57
|
+
@rails_7_or_greater ? @event.time / MILLISECOND : @event.time
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def groups
|
|
61
|
+
groups = {}
|
|
62
|
+
groups[:db] = db_runtime if db_runtime > 0
|
|
63
|
+
groups[:view] = view_runtime if view_runtime > 0
|
|
64
|
+
groups
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def status_code
|
|
68
|
+
return @event.payload[:status] if @event.payload[:status]
|
|
69
|
+
|
|
70
|
+
if @event.payload[:exception]
|
|
71
|
+
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(
|
|
72
|
+
@event.payload[:exception].first,
|
|
73
|
+
)
|
|
74
|
+
status = 500 if status == 0
|
|
75
|
+
|
|
76
|
+
return status
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# The ActiveSupport event doesn't have status only in two cases:
|
|
80
|
+
# - an exception was thrown
|
|
81
|
+
# - unauthorized access
|
|
82
|
+
# We have already handled the exception so what's left is unauthorized
|
|
83
|
+
# access. There's no way to know for sure it's unauthorized access, so
|
|
84
|
+
# we are rather optimistic here.
|
|
85
|
+
401
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def duration
|
|
89
|
+
@event.duration
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'celerbrake/rails/event'
|
|
4
|
+
|
|
5
|
+
module Celerbrake
|
|
6
|
+
module Rails
|
|
7
|
+
# @api private
|
|
8
|
+
# @since v9.2.0
|
|
9
|
+
class Excon
|
|
10
|
+
def call(*args)
|
|
11
|
+
return unless Celerbrake::Config.instance.performance_stats
|
|
12
|
+
|
|
13
|
+
routes = Celerbrake::Rack::RequestStore[:routes]
|
|
14
|
+
return if !routes || routes.none?
|
|
15
|
+
|
|
16
|
+
event = Celerbrake::Rails::Event.new(*args)
|
|
17
|
+
|
|
18
|
+
routes.each do |_route_path, params|
|
|
19
|
+
params[:groups][:http] ||= 0
|
|
20
|
+
params[:groups][:http] += event.duration
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Celerbrake
|
|
4
|
+
module Rails
|
|
5
|
+
# Monkey-patch to measure request timing.
|
|
6
|
+
# @api private
|
|
7
|
+
# @since v11.0.2
|
|
8
|
+
module HTTP
|
|
9
|
+
def perform(request, options)
|
|
10
|
+
Celerbrake::Rack.capture_timing(:http) do
|
|
11
|
+
super(request, options)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
HTTP::Client.prepend(Celerbrake::Rails::HTTP)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Celerbrake
|
|
4
|
+
module Rails
|
|
5
|
+
# Allows measuring request timing.
|
|
6
|
+
module HTTPClient
|
|
7
|
+
def do_get_block(request, proxy, connection, &block)
|
|
8
|
+
Celerbrake::Rack.capture_timing(:http) do
|
|
9
|
+
super(request, proxy, connection, &block)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
HTTPClient.prepend(Celerbrake::Rails::HTTPClient)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Celerbrake
|
|
4
|
+
module Rails
|
|
5
|
+
# Monkey-patch Net::HTTP to benchmark it.
|
|
6
|
+
# @api private
|
|
7
|
+
# @since v10.0.2
|
|
8
|
+
module NetHttp
|
|
9
|
+
def request(request, *args, &block)
|
|
10
|
+
Celerbrake::Rack.capture_timing(:http) do
|
|
11
|
+
super(request, *args, &block)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
Net::HTTP.prepend(Celerbrake::Rails::NetHttp)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Celerbrake
|
|
4
|
+
module Rails
|
|
5
|
+
# This railtie works for any Rails application that supports railties (Rails
|
|
6
|
+
# 3.2+ apps). It makes Celerbrake Ruby work with Rails and report errors
|
|
7
|
+
# occurring in the application automatically.
|
|
8
|
+
class Railtie < ::Rails::Railtie
|
|
9
|
+
initializer('celerbrake.middleware') do |app|
|
|
10
|
+
require 'celerbrake/rails/railties/middleware_tie'
|
|
11
|
+
Railties::MiddlewareTie.new(app).call
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
rake_tasks do
|
|
15
|
+
# Report exceptions occurring in Rake tasks.
|
|
16
|
+
require 'celerbrake/rake'
|
|
17
|
+
|
|
18
|
+
# Defines tasks such as `celerbrake:test` & `celerbrake:deploy`.
|
|
19
|
+
require 'celerbrake/rake/tasks'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
initializer('celerbrake.action_controller') do
|
|
23
|
+
require 'celerbrake/rails/railties/action_controller_tie'
|
|
24
|
+
Railties::ActionControllerTie.new.call
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
initializer('celerbrake.active_record') do
|
|
28
|
+
require 'celerbrake/rails/railties/active_record_tie'
|
|
29
|
+
Railties::ActiveRecordTie.new.call
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
initializer('celerbrake.active_job') do
|
|
33
|
+
ActiveSupport.on_load(:active_job, run_once: true) do
|
|
34
|
+
# Reports exceptions occurring in ActiveJob jobs.
|
|
35
|
+
require 'celerbrake/rails/active_job'
|
|
36
|
+
include Celerbrake::Rails::ActiveJob
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
initializer('celerbrake.action_cable') do
|
|
41
|
+
ActiveSupport.on_load(:action_cable, run_once: true) do
|
|
42
|
+
# Reports exceptions occurring in ActionCable connections.
|
|
43
|
+
require 'celerbrake/rails/action_cable'
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
runner do
|
|
48
|
+
at_exit do
|
|
49
|
+
Celerbrake.notify_sync($ERROR_INFO) if $ERROR_INFO
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'celerbrake/rails/action_controller'
|
|
4
|
+
require 'celerbrake/rails/action_controller_route_subscriber'
|
|
5
|
+
require 'celerbrake/rails/action_controller_notify_subscriber'
|
|
6
|
+
require 'celerbrake/rails/action_controller_performance_breakdown_subscriber'
|
|
7
|
+
|
|
8
|
+
module Celerbrake
|
|
9
|
+
module Rails
|
|
10
|
+
module Railties
|
|
11
|
+
# Ties Celerbrake APM (routes) and HTTP clients with Rails.
|
|
12
|
+
#
|
|
13
|
+
# @api private
|
|
14
|
+
# @since v13.0.1
|
|
15
|
+
class ActionControllerTie
|
|
16
|
+
def initialize
|
|
17
|
+
@route_subscriber = Celerbrake::Rails::ActionControllerRouteSubscriber.new
|
|
18
|
+
@notify_subscriber = Celerbrake::Rails::ActionControllerNotifySubscriber.new
|
|
19
|
+
@performance_breakdown_subscriber =
|
|
20
|
+
Celerbrake::Rails::ActionControllerPerformanceBreakdownSubscriber.new
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def call
|
|
24
|
+
ActiveSupport.on_load(:action_controller, run_once: true, yield: self) do
|
|
25
|
+
# Patches ActionController with methods that allow us to retrieve
|
|
26
|
+
# interesting request data. Appends that information to notices.
|
|
27
|
+
::ActionController::Base.include(Celerbrake::Rails::ActionController)
|
|
28
|
+
|
|
29
|
+
tie_routes_apm
|
|
30
|
+
tie_http_integrations
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def tie_routes_apm
|
|
37
|
+
[
|
|
38
|
+
# Cache route information for the duration of the request.
|
|
39
|
+
['start_processing.action_controller', @route_subscriber],
|
|
40
|
+
|
|
41
|
+
# Send route stats.
|
|
42
|
+
['process_action.action_controller', @notify_subscriber],
|
|
43
|
+
|
|
44
|
+
# Send performance breakdown: where a request spends its time.
|
|
45
|
+
['process_action.action_controller', @performance_breakdown_subscriber],
|
|
46
|
+
].each do |(event, callback)|
|
|
47
|
+
ActiveSupport::Notifications.subscribe(event, callback)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def tie_http_integrations
|
|
52
|
+
tie_net_http
|
|
53
|
+
tie_curl
|
|
54
|
+
tie_http
|
|
55
|
+
tie_http_client
|
|
56
|
+
tie_typhoeus
|
|
57
|
+
tie_excon
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def tie_net_http
|
|
61
|
+
require 'celerbrake/rails/net_http' if defined?(Net) && defined?(Net::HTTP)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def tie_curl
|
|
65
|
+
require 'celerbrake/rails/curb' if defined?(Curl) && defined?(Curl::CURB_VERSION)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def tie_http
|
|
69
|
+
require 'celerbrake/rails/http' if defined?(HTTP) && defined?(HTTP::Client)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def tie_http_client
|
|
73
|
+
require 'celerbrake/rails/http_client' if defined?(HTTPClient)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def tie_typhoeus
|
|
77
|
+
require 'celerbrake/rails/typhoeus' if defined?(Typhoeus)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def tie_excon
|
|
81
|
+
return unless defined?(Excon)
|
|
82
|
+
|
|
83
|
+
require 'celerbrake/rails/excon_subscriber'
|
|
84
|
+
ActiveSupport::Notifications.subscribe(/excon/, Celerbrake::Rails::Excon.new)
|
|
85
|
+
::Excon.defaults[:instrumentor] = ActiveSupport::Notifications
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'celerbrake/rails/active_record'
|
|
4
|
+
require 'celerbrake/rails/active_record_subscriber'
|
|
5
|
+
|
|
6
|
+
module Celerbrake
|
|
7
|
+
module Rails
|
|
8
|
+
module Railties
|
|
9
|
+
# Ties Celerbrake APM (queries) with Rails.
|
|
10
|
+
#
|
|
11
|
+
# @api private
|
|
12
|
+
# @since v13.0.1
|
|
13
|
+
class ActiveRecordTie
|
|
14
|
+
def initialize
|
|
15
|
+
@active_record_subscriber = Celerbrake::Rails::ActiveRecordSubscriber.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call
|
|
19
|
+
ActiveSupport.on_load(:active_record, run_once: true, yield: self) do
|
|
20
|
+
tie_activerecord_callback_fix
|
|
21
|
+
tie_activerecord_apm
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def tie_activerecord_callback_fix
|
|
28
|
+
# Reports exceptions occurring in some bugged ActiveRecord callbacks.
|
|
29
|
+
# Applicable only to the versions of Rails lower than 4.2.
|
|
30
|
+
return unless defined?(::Rails)
|
|
31
|
+
return if Gem::Version.new(::Rails.version) > Gem::Version.new('4.2')
|
|
32
|
+
|
|
33
|
+
ActiveRecord::Base.include(Celerbrake::Rails::ActiveRecord)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def tie_activerecord_apm
|
|
37
|
+
# Some Rails apps don't use ActiveRecord.
|
|
38
|
+
return unless defined?(::ActiveRecord)
|
|
39
|
+
|
|
40
|
+
# However, some dependencies might still require it, so we need an
|
|
41
|
+
# extra check. Apps that don't need ActiveRecord will likely have no
|
|
42
|
+
# AR configurations defined. We will skip APM integration in that
|
|
43
|
+
# case. See: https://github.com/celerbrake/celerbrake/issues/1222
|
|
44
|
+
configurations = ::ActiveRecord::Base.configurations
|
|
45
|
+
return unless configurations.any?
|
|
46
|
+
|
|
47
|
+
# Send SQL queries.
|
|
48
|
+
ActiveSupport::Notifications.subscribe(
|
|
49
|
+
'sql.active_record',
|
|
50
|
+
@active_record_subscriber,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Filter out parameters from SQL body.
|
|
54
|
+
sql_filter = Celerbrake::Filters::SqlFilter.new(
|
|
55
|
+
detect_activerecord_adapter(configurations),
|
|
56
|
+
)
|
|
57
|
+
Celerbrake.add_performance_filter(sql_filter)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Rails 6+ introduces the `configs_for` API instead of the deprecated
|
|
61
|
+
# `#[]`, so we need an updated call.
|
|
62
|
+
def detect_activerecord_adapter(configurations)
|
|
63
|
+
unless configurations.respond_to?(:configs_for)
|
|
64
|
+
return configurations[::Rails.env]['adapter']
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
cfg = configurations.configs_for(env_name: ::Rails.env).first
|
|
68
|
+
# Rails 7+ API : Rails 6 API.
|
|
69
|
+
cfg.respond_to?(:adapter) ? cfg.adapter : cfg.config['adapter']
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Celerbrake
|
|
4
|
+
module Rails
|
|
5
|
+
module Railties
|
|
6
|
+
# Ties Celerbrake Rails Middleware with Rails (error sending).
|
|
7
|
+
#
|
|
8
|
+
# Since Rails 3.2 the ActionDispatch::DebugExceptions middleware is
|
|
9
|
+
# responsible for logging exceptions and showing a debugging page in case
|
|
10
|
+
# the request is local. We want to insert our middleware after
|
|
11
|
+
# DebugExceptions, so we don't notify Celerbrake about local requests.
|
|
12
|
+
#
|
|
13
|
+
# @api private
|
|
14
|
+
# @since v13.0.1
|
|
15
|
+
class MiddlewareTie
|
|
16
|
+
def initialize(app)
|
|
17
|
+
@app = app
|
|
18
|
+
@middleware = app.config.middleware
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def call
|
|
22
|
+
return tie_rails_5_or_above if ::Rails.version.to_i >= 5
|
|
23
|
+
|
|
24
|
+
if defined?(::ActiveRecord::ConnectionAdapters::ConnectionManagement)
|
|
25
|
+
return tie_rails_4_or_below_with_active_record
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
tie_rails_4_or_below_with_active_record
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# Avoid the warning about deprecated strings.
|
|
34
|
+
# Insert after DebugExceptions, since ConnectionManagement doesn't
|
|
35
|
+
# exist in Rails 5 anymore.
|
|
36
|
+
def tie_rails_5_or_above
|
|
37
|
+
@middleware.insert_after(
|
|
38
|
+
ActionDispatch::DebugExceptions,
|
|
39
|
+
Celerbrake::Rack::Middleware,
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Insert after ConnectionManagement to avoid DB connection leakage:
|
|
44
|
+
# https://github.com/celerbrake/celerbrake/pull/568
|
|
45
|
+
def tie_rails_4_or_below_with_active_record
|
|
46
|
+
@middleware.insert_after(
|
|
47
|
+
::ActiveRecord::ConnectionAdapters::ConnectionManagement,
|
|
48
|
+
'Celerbrake::Rack::Middleware',
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Insert after DebugExceptions for apps without ActiveRecord.
|
|
53
|
+
def tie_rails_4_or_below_without_active_record
|
|
54
|
+
@middleware.insert_after(
|
|
55
|
+
ActionDispatch::DebugExceptions,
|
|
56
|
+
'Celerbrake::Rack::Middleware',
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Celerbrake
|
|
4
|
+
module Rails
|
|
5
|
+
# Allow measuring request timing.
|
|
6
|
+
module TyphoeusRequest
|
|
7
|
+
def run
|
|
8
|
+
Celerbrake::Rack.capture_timing(:http) do
|
|
9
|
+
super
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
Typhoeus::Request.prepend(Celerbrake::Rails::TyphoeusRequest)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'celerbrake/rails/railtie'
|
|
4
|
+
|
|
5
|
+
module Celerbrake
|
|
6
|
+
# Rails namespace holds all Rails-related functionality.
|
|
7
|
+
module Rails
|
|
8
|
+
def self.logger
|
|
9
|
+
# Rails.logger is not set in some Rake tasks such as
|
|
10
|
+
# 'celerbrake:deploy'. In this case we use a sensible fallback.
|
|
11
|
+
level = (::Rails.logger ? ::Rails.logger.level : Logger::ERROR)
|
|
12
|
+
|
|
13
|
+
if ENV['RAILS_LOG_TO_STDOUT'].present?
|
|
14
|
+
Logger.new($stdout, level: level)
|
|
15
|
+
else
|
|
16
|
+
Logger.new(::Rails.root.join('log', 'celerbrake.log'), level: level)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if defined?(ActionController::Metal)
|
|
23
|
+
require 'celerbrake/rails/action_controller'
|
|
24
|
+
module ActionController
|
|
25
|
+
# Adds support for Rails API/Metal for Rails < 5. Rails 5+ uses standard
|
|
26
|
+
# hooks.
|
|
27
|
+
# @see https://github.com/celerbrake/celerbrake/issues/821
|
|
28
|
+
class Metal
|
|
29
|
+
include Celerbrake::Rails::ActionController
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'celerbrake-ruby'
|
|
4
|
+
|
|
5
|
+
namespace :celerbrake do
|
|
6
|
+
desc 'Verify your gem installation by sending a test exception'
|
|
7
|
+
task test: (:environment if defined?(Rails)) do
|
|
8
|
+
raise Celerbrake::Error, 'celerbrake-ruby is not configured' unless Celerbrake.configured?
|
|
9
|
+
|
|
10
|
+
require 'pp'
|
|
11
|
+
|
|
12
|
+
response = Celerbrake.notify_sync('Exception from the test Rake task')
|
|
13
|
+
if response['error']
|
|
14
|
+
puts "Error: #{response['error']}"
|
|
15
|
+
elsif response.nil? || response['code']
|
|
16
|
+
puts <<-ERROR.gsub(/^\s+\|/, '')
|
|
17
|
+
|#{response['type']}: #{response['message']} (#{response['code']})
|
|
18
|
+
|
|
|
19
|
+
|Possible problems:
|
|
20
|
+
| 1. Project id/key is incorrect
|
|
21
|
+
| 2. Custom filters ignore the exception we try to send
|
|
22
|
+
| 3. Environment this task runs in is ignored (see `ignored_environments`)
|
|
23
|
+
|
|
|
24
|
+
|If nothing works, please file an issue at: https://github.com/celerbrake/celerbrake/issues
|
|
25
|
+
ERROR
|
|
26
|
+
elsif response['url']
|
|
27
|
+
puts <<-SUCCESS.gsub(/^\s+\|/, '')
|
|
28
|
+
|A test exception was sent to Celerbrake.
|
|
29
|
+
|Find it here: #{response['url']}
|
|
30
|
+
SUCCESS
|
|
31
|
+
else
|
|
32
|
+
puts <<-ERROR.gsub(/^\s+\|/, '')
|
|
33
|
+
|Unexpected error occurred. Response from Celerbrake:
|
|
34
|
+
|#{response}
|
|
35
|
+
ERROR
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
desc 'Notify Celerbrake of a new deploy'
|
|
40
|
+
task :deploy do
|
|
41
|
+
if defined?(Rails)
|
|
42
|
+
initializer = Rails.root.join('config', 'initializers', 'celerbrake.rb')
|
|
43
|
+
|
|
44
|
+
# Avoid loading the environment to speed up the deploy task and try guess
|
|
45
|
+
# the initializer file location.
|
|
46
|
+
if initializer.exist? && !Celerbrake.configured?
|
|
47
|
+
load(initializer)
|
|
48
|
+
else
|
|
49
|
+
Rake::Task[:environment].invoke
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
raise Celerbrake::Error, 'celerbrake-ruby is not configured' unless Celerbrake.configured?
|
|
54
|
+
|
|
55
|
+
deploy_params = {
|
|
56
|
+
environment: ENV.fetch('ENVIRONMENT', nil),
|
|
57
|
+
username: ENV.fetch('USERNAME', nil),
|
|
58
|
+
revision: ENV.fetch('REVISION', nil),
|
|
59
|
+
repository: ENV.fetch('REPOSITORY', nil),
|
|
60
|
+
version: ENV.fetch('VERSION', nil),
|
|
61
|
+
}
|
|
62
|
+
promise = Celerbrake.notify_deploy(deploy_params)
|
|
63
|
+
promise.then do
|
|
64
|
+
puts "The #{deploy_params[:environment]} environment was deployed."
|
|
65
|
+
end
|
|
66
|
+
promise.rescue { |error| abort(error) }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
desc 'Install a Heroku deploy hook to notify Celerbrake of deploys'
|
|
70
|
+
task :install_heroku_deploy_hook do
|
|
71
|
+
app = ENV.fetch('HEROKU_APP', nil)
|
|
72
|
+
|
|
73
|
+
config = Bundler.with_clean_env do
|
|
74
|
+
`heroku config --shell#{" --app #{app}" if app}`
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
heroku_env = config.each_line.with_object({}) do |line, h|
|
|
78
|
+
h.merge!(Hash[*line.rstrip.split("\n").flat_map { |v| v.split('=', 2) }])
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
id = heroku_env['CELERBRAKE_PROJECT_ID']
|
|
82
|
+
key = heroku_env['CELERBRAKE_API_KEY']
|
|
83
|
+
|
|
84
|
+
exit!(1) if [id, key].any?(&:nil?)
|
|
85
|
+
|
|
86
|
+
unless (env = heroku_env['RAILS_ENV'])
|
|
87
|
+
env = 'production'
|
|
88
|
+
puts "Celerbrake couldn't identify your app's environment, so the '#{env}'" \
|
|
89
|
+
" environment will be used."
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
unless (repo = ENV.fetch('REPOSITORY_URL', nil))
|
|
93
|
+
repo = `git remote get-url origin 2>/dev/null`.chomp
|
|
94
|
+
if repo.empty?
|
|
95
|
+
puts "Celerbrake couldn't identify your app's repository."
|
|
96
|
+
else
|
|
97
|
+
puts "Celerbrake couldn't identify your app's repository, so the " \
|
|
98
|
+
"'origin' remote url '#{repo}' will be used."
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
url = ["https://celerbrake.com/api/v3/projects/#{id}/heroku-deploys?key=#{key}"]
|
|
103
|
+
url << "&environment=#{env}"
|
|
104
|
+
url << "&repository=#{repo}" unless repo.empty?
|
|
105
|
+
|
|
106
|
+
command = [%(heroku addons:create deployhooks:http --url="#{url.join}")]
|
|
107
|
+
command << " --app #{app}" if app
|
|
108
|
+
|
|
109
|
+
puts "$ #{command.join}"
|
|
110
|
+
Bundler.with_clean_env { puts `#{command}` }
|
|
111
|
+
end
|
|
112
|
+
end
|