rails_performance 0.9.7 → 1.0.0.beta3
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/README.md +50 -8
- data/app/controllers/rails_performance/rails_performance_controller.rb +48 -36
- data/app/helpers/rails_performance/application_helper.rb +25 -7
- data/app/views/rails_performance/javascripts/app.js +2 -2
- data/app/views/rails_performance/layouts/rails_performance.html.erb +1 -1
- data/app/views/rails_performance/rails_performance/_summary.html.erb +1 -1
- data/app/views/rails_performance/rails_performance/custom.html.erb +83 -0
- data/app/views/rails_performance/rails_performance/delayed_job.html.erb +74 -0
- data/app/views/rails_performance/rails_performance/grape.html.erb +64 -0
- data/app/views/rails_performance/rails_performance/rake.html.erb +55 -0
- data/app/views/rails_performance/rails_performance/recent.html.erb +3 -1
- data/app/views/rails_performance/rails_performance/{jobs.html.erb → sidekiq.html.erb} +5 -4
- data/app/views/rails_performance/rails_performance/summary.js.erb +1 -1
- data/app/views/rails_performance/rails_performance/trace.js.erb +1 -1
- data/app/views/rails_performance/shared/_header.html.erb +9 -1
- data/app/views/rails_performance/stylesheets/style.css +5 -0
- data/config/routes.rb +5 -1
- data/lib/generators/rails_performance/install/USAGE +8 -0
- data/lib/generators/rails_performance/install/install_generator.rb +8 -0
- data/lib/generators/rails_performance/install/templates/initializer.rb +23 -0
- data/lib/rails_performance.rb +35 -8
- data/lib/rails_performance/data_source.rb +55 -16
- data/lib/rails_performance/engine.rb +39 -18
- data/lib/rails_performance/extensions/{capture_everything.rb → trace.rb} +2 -2
- data/lib/rails_performance/gems/custom_ext.rb +33 -0
- data/lib/rails_performance/gems/delayed_job_ext.rb +54 -0
- data/lib/rails_performance/gems/grape_ext.rb +35 -0
- data/lib/rails_performance/gems/rake_ext.rb +40 -0
- data/lib/rails_performance/gems/{sidekiq.rb → sidekiq_ext.rb} +13 -12
- data/lib/rails_performance/instrument/metrics_collector.rb +4 -3
- data/lib/rails_performance/models/base_record.rb +12 -0
- data/lib/rails_performance/models/custom_record.rb +48 -0
- data/lib/rails_performance/models/delayed_job_record.rb +62 -0
- data/lib/rails_performance/models/grape_record.rb +61 -0
- data/lib/rails_performance/models/rake_record.rb +49 -0
- data/lib/rails_performance/models/request_record.rb +98 -0
- data/lib/rails_performance/models/sidekiq_record.rb +66 -0
- data/lib/rails_performance/models/trace_record.rb +19 -0
- data/lib/rails_performance/rails/middleware.rb +42 -16
- data/lib/rails_performance/rails/query_builder.rb +1 -1
- data/lib/rails_performance/reports/breakdown_report.rb +4 -16
- data/lib/rails_performance/reports/crash_report.rb +4 -15
- data/lib/rails_performance/reports/recent_requests_report.rb +7 -44
- data/lib/rails_performance/reports/trace_report.rb +2 -2
- data/lib/rails_performance/{models → thread}/current_request.rb +9 -4
- data/lib/rails_performance/utils.rb +15 -29
- data/lib/rails_performance/version.rb +2 -1
- metadata +97 -11
- data/lib/rails_performance/models/job_record.rb +0 -48
- data/lib/rails_performance/models/record.rb +0 -68
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'action_view/log_subscriber'
|
1
2
|
require_relative './rails/middleware.rb'
|
2
3
|
require_relative './models/collection.rb'
|
3
4
|
require_relative './instrument/metrics_collector.rb'
|
@@ -6,36 +7,56 @@ module RailsPerformance
|
|
6
7
|
class Engine < ::Rails::Engine
|
7
8
|
isolate_namespace RailsPerformance
|
8
9
|
|
9
|
-
|
10
|
+
initializer "rails_performance.middleware" do |app|
|
11
|
+
next unless RailsPerformance.enabled
|
10
12
|
|
11
13
|
if ::Rails::VERSION::MAJOR.to_i >= 5
|
12
|
-
|
14
|
+
app.middleware.insert_after ActionDispatch::Executor, RailsPerformance::Rails::Middleware
|
13
15
|
else
|
14
|
-
|
16
|
+
app.middleware.insert_after ActionDispatch::Static, RailsPerformance::Rails::Middleware
|
15
17
|
end
|
18
|
+
# look like it works in reverse order?
|
19
|
+
app.middleware.insert_before RailsPerformance::Rails::Middleware, RailsPerformance::Rails::MiddlewareTraceStorerAndCleanup
|
16
20
|
|
17
|
-
|
18
|
-
|
19
|
-
"process_action.action_controller",
|
20
|
-
RailsPerformance::Instrument::MetricsCollector.new
|
21
|
-
)
|
22
|
-
|
23
|
-
config.after_initialize do |app|
|
24
|
-
ActionView::LogSubscriber.send :prepend, RailsPerformance::Extensions::View
|
25
|
-
ActiveRecord::LogSubscriber.send :prepend, RailsPerformance::Extensions::Db
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
if const_defined?("Sidekiq")
|
30
|
-
require_relative './gems/sidekiq.rb'
|
21
|
+
if defined?(::Sidekiq)
|
22
|
+
require_relative './gems/sidekiq_ext.rb'
|
31
23
|
Sidekiq.configure_server do |config|
|
32
24
|
config.server_middleware do |chain|
|
33
|
-
chain.add RailsPerformance::Gems::
|
25
|
+
chain.add RailsPerformance::Gems::SidekiqExt
|
34
26
|
end
|
35
27
|
end
|
36
28
|
end
|
37
29
|
|
30
|
+
if defined?(::Grape)
|
31
|
+
require_relative './gems/grape_ext.rb'
|
32
|
+
RailsPerformance::Gems::GrapeExt.init
|
33
|
+
end
|
34
|
+
|
35
|
+
if defined?(::Delayed::Job)
|
36
|
+
require_relative './gems/delayed_job_ext.rb'
|
37
|
+
RailsPerformance::Gems::DelayedJobExt.init
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
initializer :configure_metrics, after: :initialize_logger do
|
42
|
+
next unless RailsPerformance.enabled
|
43
|
+
|
44
|
+
ActiveSupport::Notifications.subscribe(
|
45
|
+
"process_action.action_controller",
|
46
|
+
RailsPerformance::Instrument::MetricsCollector.new
|
47
|
+
)
|
38
48
|
end
|
39
49
|
|
50
|
+
config.after_initialize do
|
51
|
+
next unless RailsPerformance.enabled
|
52
|
+
|
53
|
+
ActionView::LogSubscriber.send :prepend, RailsPerformance::Extensions::View
|
54
|
+
ActiveRecord::LogSubscriber.send :prepend, RailsPerformance::Extensions::Db
|
55
|
+
end
|
56
|
+
|
57
|
+
if defined?(::Rake::Task)
|
58
|
+
require_relative './gems/rake_ext.rb'
|
59
|
+
RailsPerformance::Gems::RakeExt.init
|
60
|
+
end
|
40
61
|
end
|
41
62
|
end
|
@@ -3,7 +3,7 @@ module RailsPerformance
|
|
3
3
|
module View
|
4
4
|
|
5
5
|
def info(&block)
|
6
|
-
CurrentRequest.current.
|
6
|
+
CurrentRequest.current.trace({
|
7
7
|
group: :view,
|
8
8
|
message: block.call
|
9
9
|
})
|
@@ -19,7 +19,7 @@ module RailsPerformance
|
|
19
19
|
module Db
|
20
20
|
|
21
21
|
def sql(event)
|
22
|
-
CurrentRequest.current.
|
22
|
+
CurrentRequest.current.trace({
|
23
23
|
group: :db,
|
24
24
|
duration: event.duration.round(2),
|
25
25
|
sql: event.payload[:sql]
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module RailsPerformance
|
2
|
+
module Gems
|
3
|
+
module CustomExtension
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def measure(tag_name, namespace_name = nil)
|
7
|
+
return yield unless RailsPerformance.enabled
|
8
|
+
|
9
|
+
begin
|
10
|
+
now = Time.now
|
11
|
+
status = 'success'
|
12
|
+
result = yield
|
13
|
+
result
|
14
|
+
rescue Exception => ex
|
15
|
+
status = 'error'
|
16
|
+
raise(ex)
|
17
|
+
ensure
|
18
|
+
RailsPerformance::Models::CustomRecord.new(
|
19
|
+
tag_name: tag_name,
|
20
|
+
namespace_name: namespace_name,
|
21
|
+
status: status,
|
22
|
+
duration: (Time.now - now) * 1000,
|
23
|
+
datetime: now.strftime(RailsPerformance::FORMAT),
|
24
|
+
datetimei: now.to_i,
|
25
|
+
).save
|
26
|
+
|
27
|
+
result
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module RailsPerformance
|
2
|
+
module Gems
|
3
|
+
class DelayedJobExt
|
4
|
+
|
5
|
+
class Plugin < ::Delayed::Plugin
|
6
|
+
callbacks do |lifecycle|
|
7
|
+
lifecycle.around(:invoke_job) do |job, *args, &block|
|
8
|
+
begin
|
9
|
+
now = Time.now
|
10
|
+
block.call(job, *args)
|
11
|
+
status = 'success'
|
12
|
+
rescue Exception => error
|
13
|
+
status = 'error'
|
14
|
+
raise error
|
15
|
+
ensure
|
16
|
+
meta_data = RailsPerformance::Gems::DelayedJobExt::Plugin.meta(job.payload_object)
|
17
|
+
record = RailsPerformance::Models::DelayedJobRecord.new(
|
18
|
+
jid: job.id,
|
19
|
+
duration: (Time.now - now) * 1000,
|
20
|
+
datetime: now.strftime(RailsPerformance::FORMAT),
|
21
|
+
datetimei: now.to_i,
|
22
|
+
source_type: meta_data[0],
|
23
|
+
class_name: meta_data[1],
|
24
|
+
method_name: meta_data[2],
|
25
|
+
status: status
|
26
|
+
)
|
27
|
+
record.save
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# [source_type, class_name, method_name, duration]
|
33
|
+
def self.meta(payload_object)
|
34
|
+
if payload_object.is_a?(::Delayed::PerformableMethod)
|
35
|
+
if payload_object.object.is_a?(Module)
|
36
|
+
[:class_method, payload_object.object.name, payload_object.method_name.to_s]
|
37
|
+
else
|
38
|
+
[:instance_method, payload_object.object.class.name, payload_object.method_name.to_s]
|
39
|
+
end
|
40
|
+
else
|
41
|
+
[:instance_method, payload_object.class.name, "perform"]
|
42
|
+
end
|
43
|
+
rescue
|
44
|
+
[:unknown, :unknown, :unknown]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.init
|
49
|
+
::Delayed::Worker.plugins += [::RailsPerformance::Gems::DelayedJobExt::Plugin]
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module RailsPerformance
|
2
|
+
module Gems
|
3
|
+
class GrapeExt
|
4
|
+
|
5
|
+
def self.init
|
6
|
+
ActiveSupport::Notifications.subscribe(/grape/) do |name, start, finish, id, payload|
|
7
|
+
# TODO change to set
|
8
|
+
CurrentRequest.current.ignore.add(:performance)
|
9
|
+
|
10
|
+
now = Time.now
|
11
|
+
CurrentRequest.current.data ||= {}
|
12
|
+
CurrentRequest.current.record ||= RailsPerformance::Models::GrapeRecord.new(request_id: CurrentRequest.current.request_id)
|
13
|
+
CurrentRequest.current.record.datetimei ||= now.to_i
|
14
|
+
CurrentRequest.current.record.datetime ||= now.strftime(RailsPerformance::FORMAT)
|
15
|
+
|
16
|
+
if ['endpoint_render.grape', 'endpoint_run.grape', 'format_response.grape'].include?(name)
|
17
|
+
CurrentRequest.current.record.send(name.gsub(".", "_") + "=", (finish - start) * 1000)
|
18
|
+
end
|
19
|
+
|
20
|
+
if payload[:env]
|
21
|
+
CurrentRequest.current.record.status = payload[:env]['api.endpoint'].status
|
22
|
+
CurrentRequest.current.record.format = payload[:env]["api.format"]
|
23
|
+
CurrentRequest.current.record.method = payload[:env]['REQUEST_METHOD']
|
24
|
+
CurrentRequest.current.record.path = payload[:env]["PATH_INFO"]
|
25
|
+
end
|
26
|
+
|
27
|
+
if name == 'format_response.grape'
|
28
|
+
CurrentRequest.current.record.save
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module RailsPerformance
|
2
|
+
module Gems
|
3
|
+
|
4
|
+
class RakeExt
|
5
|
+
def self.init
|
6
|
+
::Rake::Task.class_eval do
|
7
|
+
def invoke_with_rails_performance(*args)
|
8
|
+
begin
|
9
|
+
now = Time.now
|
10
|
+
status = 'success'
|
11
|
+
invoke_without_new_rails_performance(*args)
|
12
|
+
rescue Exception => ex
|
13
|
+
status = 'error'
|
14
|
+
raise(ex)
|
15
|
+
ensure
|
16
|
+
RailsPerformance::Models::RakeRecord.new(
|
17
|
+
task: RailsPerformance::Gems::RakeExt.find_task_name(*args),
|
18
|
+
datetime: now.strftime(RailsPerformance::FORMAT),
|
19
|
+
datetimei: now.to_i,
|
20
|
+
duration: (Time.now - now) * 1000,
|
21
|
+
status: status,
|
22
|
+
).save
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
alias_method :invoke_without_new_rails_performance, :invoke
|
27
|
+
alias_method :invoke, :invoke_with_rails_performance
|
28
|
+
|
29
|
+
def invoke(*args)
|
30
|
+
invoke_with_rails_performance(*args)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.find_task_name(*args)
|
36
|
+
(ARGV + args).compact
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,33 +1,34 @@
|
|
1
1
|
module RailsPerformance
|
2
2
|
module Gems
|
3
|
-
class
|
3
|
+
class SidekiqExt
|
4
4
|
|
5
5
|
def initialize(options=nil)
|
6
6
|
end
|
7
7
|
|
8
8
|
def call(worker, msg, queue)
|
9
|
-
now
|
10
|
-
|
9
|
+
now = Time.now
|
10
|
+
record = RailsPerformance::Models::SidekiqRecord.new(
|
11
11
|
enqueued_ati: msg['enqueued_at'].to_i,
|
12
|
-
|
12
|
+
datetimei: msg['created_at'].to_i,
|
13
13
|
jid: msg['jid'],
|
14
14
|
queue: queue,
|
15
15
|
start_timei: now.to_i,
|
16
16
|
datetime: now.strftime(RailsPerformance::FORMAT),
|
17
17
|
worker: msg['wrapped'.freeze] || worker.class.to_s
|
18
|
-
|
18
|
+
)
|
19
19
|
begin
|
20
|
-
yield
|
21
|
-
|
20
|
+
result = yield
|
21
|
+
record.status = "success"
|
22
|
+
result
|
22
23
|
rescue Exception => ex
|
23
|
-
|
24
|
-
|
24
|
+
record.status = "exception"
|
25
|
+
record.message = ex.message
|
25
26
|
raise ex
|
26
27
|
ensure
|
27
28
|
# store in ms instead of seconds
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
record.duration = (Time.now - now) * 1000
|
30
|
+
record.save
|
31
|
+
result
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
@@ -16,10 +16,11 @@ module RailsPerformance
|
|
16
16
|
# }
|
17
17
|
|
18
18
|
def call(event_name, started, finished, event_id, payload)
|
19
|
+
return if RailsPerformance.skip
|
20
|
+
# TODO do we need this new?
|
19
21
|
event = ActiveSupport::Notifications::Event.new(event_name, started, finished, event_id, payload)
|
20
22
|
|
21
|
-
|
22
|
-
return if event.payload[:path] =~ /^#{Regexp.escape(mount_url)}/
|
23
|
+
return if RailsPerformance.ignored_endpoints.include? "#{event.payload[:controller]}##{event.payload[:action]}"
|
23
24
|
|
24
25
|
record = {
|
25
26
|
controller: event.payload[:controller],
|
@@ -35,7 +36,7 @@ module RailsPerformance
|
|
35
36
|
duration: event.duration
|
36
37
|
}
|
37
38
|
|
38
|
-
CurrentRequest.current.
|
39
|
+
CurrentRequest.current.data = record
|
39
40
|
end
|
40
41
|
end
|
41
42
|
end
|
@@ -1,6 +1,18 @@
|
|
1
1
|
module RailsPerformance
|
2
2
|
module Models
|
3
3
|
class BaseRecord
|
4
|
+
def self.from_db(key, value)
|
5
|
+
raise 'implement me'
|
6
|
+
end
|
7
|
+
|
8
|
+
def save
|
9
|
+
raise 'implement me'
|
10
|
+
end
|
11
|
+
|
12
|
+
def record_hash
|
13
|
+
raise 'implement me'
|
14
|
+
end
|
15
|
+
|
4
16
|
def value
|
5
17
|
@value ||= JSON.parse(@json || "{}")
|
6
18
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module RailsPerformance
|
2
|
+
module Models
|
3
|
+
class CustomRecord < BaseRecord
|
4
|
+
attr_accessor :tag_name, :namespace_name, :duration, :datetime, :datetimei, :status, :json
|
5
|
+
|
6
|
+
def CustomRecord.from_db(key, value)
|
7
|
+
items = key.split("|")
|
8
|
+
|
9
|
+
CustomRecord.new(
|
10
|
+
tag_name: items[2],
|
11
|
+
namespace_name: items[4],
|
12
|
+
datetime: items[6],
|
13
|
+
datetimei: items[8],
|
14
|
+
status: items[10],
|
15
|
+
json: value
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(tag_name:, namespace_name: nil, duration: nil, datetime:, datetimei:, status:, json: '{}')
|
20
|
+
@tag_name = tag_name
|
21
|
+
@namespace_name = namespace_name
|
22
|
+
@duration = duration
|
23
|
+
@datetime = datetime
|
24
|
+
@datetimei = datetimei.to_i
|
25
|
+
@status = status
|
26
|
+
@json = json
|
27
|
+
end
|
28
|
+
|
29
|
+
def record_hash
|
30
|
+
{
|
31
|
+
tag_name: self.tag_name,
|
32
|
+
namespace_name: self.namespace_name,
|
33
|
+
status: self.status,
|
34
|
+
datetimei: datetimei,
|
35
|
+
datetime: Time.at(self.datetimei.to_i),
|
36
|
+
duration: self.value['duration'],
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def save
|
41
|
+
key = "custom|tag_name|#{tag_name}|namespace_name|#{namespace_name}|datetime|#{datetime}|datetimei|#{datetimei}|status|#{status}|END|#{RailsPerformance::SCHEMA}"
|
42
|
+
value = { duration: duration }
|
43
|
+
Utils.save_to_redis(key, value)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module RailsPerformance
|
2
|
+
module Models
|
3
|
+
class DelayedJobRecord < BaseRecord
|
4
|
+
attr_accessor :jid, :duration, :datetime, :datetimei, :source_type, :class_name, :method_name, :status, :json
|
5
|
+
|
6
|
+
# delayed_job
|
7
|
+
#|jid|22
|
8
|
+
#|datetime|20210415T0616
|
9
|
+
#|datetimei|1618492591
|
10
|
+
#|source_type|instance_method
|
11
|
+
#|class_name|User
|
12
|
+
#|method_name|say_hello_without_delay
|
13
|
+
#|status|success|END|1.0.0
|
14
|
+
def DelayedJobRecord.from_db(key, value)
|
15
|
+
items = key.split("|")
|
16
|
+
|
17
|
+
DelayedJobRecord.new(
|
18
|
+
jid: items[2],
|
19
|
+
datetime: items[4],
|
20
|
+
datetimei: items[6],
|
21
|
+
source_type: items[8],
|
22
|
+
class_name: items[10],
|
23
|
+
method_name: items[12],
|
24
|
+
status: items[14],
|
25
|
+
json: value
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(jid:, duration: nil, datetime:, datetimei:, source_type:, class_name:, method_name:, status:, json: '{}')
|
30
|
+
@jid = jid
|
31
|
+
@duration = duration
|
32
|
+
@datetime = datetime
|
33
|
+
@datetimei = datetimei.to_i
|
34
|
+
@source_type = source_type
|
35
|
+
@class_name = class_name
|
36
|
+
@method_name = method_name
|
37
|
+
@status = status
|
38
|
+
@json = json
|
39
|
+
end
|
40
|
+
|
41
|
+
def record_hash
|
42
|
+
{
|
43
|
+
jid: jid,
|
44
|
+
datetime: Time.at(datetimei),
|
45
|
+
datetimei: datetimei,
|
46
|
+
duration: value['duration'],
|
47
|
+
status: status,
|
48
|
+
source_type: source_type,
|
49
|
+
class_name: class_name,
|
50
|
+
method_name: method_name,
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def save
|
55
|
+
key = "delayed_job|jid|#{jid}|datetime|#{datetime}|datetimei|#{datetimei}|source_type|#{source_type}|class_name|#{class_name}|method_name|#{method_name}|status|#{status}|END|#{RailsPerformance::SCHEMA}"
|
56
|
+
value = { duration: duration }
|
57
|
+
Utils.save_to_redis(key, value)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|