rails_performance 1.2.3 → 1.3.1

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -13
  3. data/Rakefile +14 -14
  4. data/app/assets/images/download.svg +3 -0
  5. data/app/controllers/rails_performance/base_controller.rb +21 -21
  6. data/app/controllers/rails_performance/concerns/csv_exportable.rb +29 -0
  7. data/app/controllers/rails_performance/rails_performance_controller.rb +96 -49
  8. data/app/helpers/rails_performance/rails_performance_helper.rb +152 -156
  9. data/app/views/rails_performance/javascripts/app.js +2 -2
  10. data/app/views/rails_performance/rails_performance/_export.html.erb +3 -0
  11. data/app/views/rails_performance/rails_performance/crashes.html.erb +8 -1
  12. data/app/views/rails_performance/rails_performance/index.html.erb +29 -0
  13. data/app/views/rails_performance/rails_performance/recent.html.erb +8 -6
  14. data/app/views/rails_performance/rails_performance/requests.html.erb +15 -1
  15. data/app/views/rails_performance/rails_performance/slow.html.erb +3 -0
  16. data/app/views/rails_performance/shared/_header.html.erb +0 -1
  17. data/app/views/rails_performance/stylesheets/style.css +10 -0
  18. data/config/routes.rb +16 -18
  19. data/lib/generators/rails_performance/install/install_generator.rb +1 -1
  20. data/lib/generators/rails_performance/install/templates/initializer.rb +56 -42
  21. data/lib/rails_performance/data_source.rb +120 -121
  22. data/lib/rails_performance/engine.rb +8 -8
  23. data/lib/rails_performance/extensions/trace.rb +32 -33
  24. data/lib/rails_performance/gems/custom_ext.rb +31 -34
  25. data/lib/rails_performance/gems/delayed_job_ext.rb +50 -54
  26. data/lib/rails_performance/gems/grape_ext.rb +33 -35
  27. data/lib/rails_performance/gems/rake_ext.rb +41 -44
  28. data/lib/rails_performance/gems/sidekiq_ext.rb +34 -37
  29. data/lib/rails_performance/instrument/metrics_collector.rb +50 -50
  30. data/lib/rails_performance/models/base_record.rb +33 -36
  31. data/lib/rails_performance/models/collection.rb +35 -36
  32. data/lib/rails_performance/models/custom_record.rb +47 -48
  33. data/lib/rails_performance/models/delayed_job_record.rb +61 -62
  34. data/lib/rails_performance/models/grape_record.rb +60 -61
  35. data/lib/rails_performance/models/rake_record.rb +48 -49
  36. data/lib/rails_performance/models/request_record.rb +128 -120
  37. data/lib/rails_performance/models/sidekiq_record.rb +65 -66
  38. data/lib/rails_performance/models/trace_record.rb +18 -19
  39. data/lib/rails_performance/rails/middleware.rb +75 -76
  40. data/lib/rails_performance/rails/query_builder.rb +18 -20
  41. data/lib/rails_performance/reports/base_report.rb +60 -60
  42. data/lib/rails_performance/reports/breakdown_report.rb +15 -18
  43. data/lib/rails_performance/reports/crash_report.rb +15 -17
  44. data/lib/rails_performance/reports/percentile_report.rb +14 -0
  45. data/lib/rails_performance/reports/recent_requests_report.rb +24 -24
  46. data/lib/rails_performance/reports/requests_report.rb +30 -27
  47. data/lib/rails_performance/reports/response_time_report.rb +17 -17
  48. data/lib/rails_performance/reports/slow_requests_report.rb +4 -4
  49. data/lib/rails_performance/reports/throughput_report.rb +15 -17
  50. data/lib/rails_performance/reports/trace_report.rb +16 -18
  51. data/lib/rails_performance/thread/current_request.rb +33 -34
  52. data/lib/rails_performance/utils.rb +64 -54
  53. data/lib/rails_performance/version.rb +2 -2
  54. data/lib/rails_performance.rb +35 -36
  55. metadata +21 -17
@@ -1,66 +1,65 @@
1
- module RailsPerformance
2
- module Models
3
- class SidekiqRecord < BaseRecord
4
- attr_accessor :queue, :worker, :jid, :datetimei, :enqueued_ati, :datetime, :start_timei, :status, :duration, :message
5
-
6
- # key = job-performance
7
- # |queue|default
8
- # |worker|SimpleWorker
9
- # |jid|7d48fbf20976c224510dbc60
10
- # |datetime|20200124T0523
11
- # |datetimei|1583146613
12
- # |enqueued_ati|1583146613
13
- # |start_timei|1583146614
14
- # |status|success|END|1.0.0
15
- # value = JSON
16
- def SidekiqRecord.from_db(key, value)
17
- items = key.split("|")
18
-
19
- SidekiqRecord.new(
20
- queue: items[2],
21
- worker: items[4],
22
- jid: items[6],
23
- datetime: items[8],
24
- datetimei: items[10],
25
- enqueued_ati: items[12],
26
- start_timei: items[14],
27
- status: items[16],
28
- json: value
29
- )
30
- end
31
-
32
- def initialize(queue:, worker:, jid:, datetime:, datetimei:, enqueued_ati:, start_timei:, status: nil, duration: nil, json: "{}")
33
- @queue = queue
34
- @worker = worker
35
- @jid = jid
36
- @datetime = datetime
37
- @datetimei = datetimei.to_i
38
- @enqueued_ati = enqueued_ati
39
- @start_timei = start_timei
40
- @status = status
41
- @duration = duration
42
- @json = json
43
- end
44
-
45
- def record_hash
46
- {
47
- worker: self.worker,
48
- queue: self.queue,
49
- jid: self.jid,
50
- status: self.status,
51
- datetimei: datetimei,
52
- datetime: Time.at(self.start_timei.to_i),
53
- duration: self.value['duration'],
54
- message: value['message']
55
- }
56
- end
57
-
58
- def save
59
- key = "sidekiq|queue|#{queue}|worker|#{worker}|jid|#{jid}|datetime|#{datetime}|datetimei|#{datetimei}|enqueued_ati|#{enqueued_ati}|start_timei|#{start_timei}|status|#{status}|END|#{RailsPerformance::SCHEMA}"
60
- value = { message: message, duration: duration }
61
- Utils.save_to_redis(key, value)
62
- end
63
-
64
- end
65
- end
66
- end
1
+ module RailsPerformance
2
+ module Models
3
+ class SidekiqRecord < BaseRecord
4
+ attr_accessor :queue, :worker, :jid, :datetimei, :enqueued_ati, :datetime, :start_timei, :status, :duration, :message
5
+
6
+ # key = job-performance
7
+ # |queue|default
8
+ # |worker|SimpleWorker
9
+ # |jid|7d48fbf20976c224510dbc60
10
+ # |datetime|20200124T0523
11
+ # |datetimei|1583146613
12
+ # |enqueued_ati|1583146613
13
+ # |start_timei|1583146614
14
+ # |status|success|END|1.0.0
15
+ # value = JSON
16
+ def self.from_db(key, value)
17
+ items = key.split("|")
18
+
19
+ SidekiqRecord.new(
20
+ queue: items[2],
21
+ worker: items[4],
22
+ jid: items[6],
23
+ datetime: items[8],
24
+ datetimei: items[10],
25
+ enqueued_ati: items[12],
26
+ start_timei: items[14],
27
+ status: items[16],
28
+ json: value
29
+ )
30
+ end
31
+
32
+ def initialize(queue:, worker:, jid:, datetime:, datetimei:, enqueued_ati:, start_timei:, status: nil, duration: nil, json: "{}")
33
+ @queue = queue
34
+ @worker = worker
35
+ @jid = jid
36
+ @datetime = datetime
37
+ @datetimei = datetimei.to_i
38
+ @enqueued_ati = enqueued_ati
39
+ @start_timei = start_timei
40
+ @status = status
41
+ @duration = duration
42
+ @json = json
43
+ end
44
+
45
+ def record_hash
46
+ {
47
+ worker: worker,
48
+ queue: queue,
49
+ jid: jid,
50
+ status: status,
51
+ datetimei: datetimei,
52
+ datetime: Time.at(start_timei.to_i),
53
+ duration: value["duration"],
54
+ message: value["message"]
55
+ }
56
+ end
57
+
58
+ def save
59
+ key = "sidekiq|queue|#{queue}|worker|#{worker}|jid|#{jid}|datetime|#{datetime}|datetimei|#{datetimei}|enqueued_ati|#{enqueued_ati}|start_timei|#{start_timei}|status|#{status}|END|#{RailsPerformance::SCHEMA}"
60
+ value = {message: message, duration: duration}
61
+ Utils.save_to_redis(key, value)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,19 +1,18 @@
1
- module RailsPerformance
2
- module Models
3
- class TraceRecord < BaseRecord
4
- attr_accessor :request_id, :value
5
-
6
- def initialize(request_id:, value:)
7
- @request_id = request_id
8
- @value = value
9
- end
10
-
11
- def save
12
- return if value.empty?
13
-
14
- Utils.save_to_redis("trace|#{request_id}|END|#{RailsPerformance::SCHEMA}", value, RailsPerformance.recent_requests_time_window.to_i)
15
- end
16
-
17
- end
18
- end
19
- end
1
+ module RailsPerformance
2
+ module Models
3
+ class TraceRecord < BaseRecord
4
+ attr_accessor :request_id, :value
5
+
6
+ def initialize(request_id:, value:)
7
+ @request_id = request_id
8
+ @value = value
9
+ end
10
+
11
+ def save
12
+ return if value.empty?
13
+
14
+ Utils.save_to_redis("trace|#{request_id}|END|#{RailsPerformance::SCHEMA}", value, RailsPerformance.recent_requests_time_window.to_i)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,76 +1,75 @@
1
- module RailsPerformance
2
- module Rails
3
- class MiddlewareTraceStorerAndCleanup
4
- def initialize(app)
5
- @app = app
6
- end
7
-
8
- def call(env)
9
- dup.call!(env)
10
- end
11
-
12
- def call!(env)
13
- if %r{#{RailsPerformance.mount_at}}.match?(env["PATH_INFO"])
14
- RailsPerformance.skip = true
15
- end
16
-
17
- @status, @headers, @response = @app.call(env)
18
-
19
- if !RailsPerformance.skip
20
- RailsPerformance::Models::TraceRecord.new(
21
- request_id: CurrentRequest.current.request_id,
22
- value: CurrentRequest.current.tracings
23
- ).save
24
- end
25
-
26
- CurrentRequest.cleanup
27
-
28
- [@status, @headers, @response]
29
- end
30
- end
31
-
32
- class Middleware
33
- def initialize(app)
34
- @app = app
35
- end
36
-
37
- def call(env)
38
- dup.call!(env)
39
- end
40
-
41
- def call!(env)
42
- @status, @headers, @response = @app.call(env)
43
-
44
- #t = Time.current
45
- if !RailsPerformance.skip
46
- if !CurrentRequest.current.ignore.include?(:performance) # grape is executed first, and than ignore regular future storage of "controller"-like request
47
- if data = CurrentRequest.current.data
48
- record = RailsPerformance::Models::RequestRecord.new(**data.merge({request_id: CurrentRequest.current.request_id}))
49
-
50
- # for 500 errors
51
- record.status ||= @status
52
-
53
- # capture referer from where this page was opened
54
- record.http_referer = env["HTTP_REFERER"] if record.status == 404
55
-
56
- # we can add custom data, for example Http User-Agent
57
- # or even devise current_user
58
- if RailsPerformance.custom_data_proc
59
- # just to be sure it won't break format how we store in redis
60
- record.custom_data = RailsPerformance.custom_data_proc.call(env)
61
- end
62
-
63
- # store for section "recent requests"
64
- # store request information (regular rails request)
65
- record.save
66
- end
67
- end
68
- end
69
- #puts "==> store performance data: #{(Time.current - t).round(3)}ms"
70
-
71
- [@status, @headers, @response]
72
- end
73
-
74
- end
75
- end
76
- end
1
+ module RailsPerformance
2
+ module Rails
3
+ class MiddlewareTraceStorerAndCleanup
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ dup.call!(env)
10
+ end
11
+
12
+ def call!(env)
13
+ if %r{#{RailsPerformance.mount_at}}.match?(env["PATH_INFO"])
14
+ RailsPerformance.skip = true
15
+ end
16
+
17
+ @status, @headers, @response = @app.call(env)
18
+
19
+ if !RailsPerformance.skip
20
+ RailsPerformance::Models::TraceRecord.new(
21
+ request_id: CurrentRequest.current.request_id,
22
+ value: CurrentRequest.current.tracings
23
+ ).save
24
+ end
25
+
26
+ CurrentRequest.cleanup
27
+
28
+ [@status, @headers, @response]
29
+ end
30
+ end
31
+
32
+ class Middleware
33
+ def initialize(app)
34
+ @app = app
35
+ end
36
+
37
+ def call(env)
38
+ dup.call!(env)
39
+ end
40
+
41
+ def call!(env)
42
+ @status, @headers, @response = @app.call(env)
43
+
44
+ # t = Time.current
45
+ if !RailsPerformance.skip
46
+ if !CurrentRequest.current.ignore.include?(:performance) # grape is executed first, and than ignore regular future storage of "controller"-like request
47
+ if (data = CurrentRequest.current.data)
48
+ record = RailsPerformance::Models::RequestRecord.new(**data.merge({request_id: CurrentRequest.current.request_id}))
49
+
50
+ # for 500 errors
51
+ record.status ||= @status
52
+
53
+ # capture referer from where this page was opened
54
+ record.http_referer = env["HTTP_REFERER"] if record.status == 404
55
+
56
+ # we can add custom data, for example Http User-Agent
57
+ # or even devise current_user
58
+ if RailsPerformance.custom_data_proc
59
+ # just to be sure it won't break format how we store in redis
60
+ record.custom_data = RailsPerformance.custom_data_proc.call(env)
61
+ end
62
+
63
+ # store for section "recent requests"
64
+ # store request information (regular rails request)
65
+ record.save
66
+ end
67
+ end
68
+ end
69
+ # puts "==> store performance data: #{(Time.current - t).round(3)}ms"
70
+
71
+ [@status, @headers, @response]
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,20 +1,18 @@
1
- module RailsPerformance
2
- module Rails
3
- class QueryBuilder
4
-
5
- def QueryBuilder.compose_from(params)
6
- result = {}
7
-
8
- result[:controller] = params[:controller_eq]
9
- result[:action] = params[:action_eq]
10
- result[:format] = params[:format_eq]
11
- result[:status] = params[:status_eq]
12
-
13
- result.delete_if {|k, v| v.nil?}
14
-
15
- { q: result }
16
- end
17
-
18
- end
19
- end
20
- end
1
+ module RailsPerformance
2
+ module Rails
3
+ class QueryBuilder
4
+ def self.compose_from(params)
5
+ result = {}
6
+
7
+ result[:controller] = params[:controller_eq]
8
+ result[:action] = params[:action_eq]
9
+ result[:format] = params[:format_eq]
10
+ result[:status] = params[:status_eq]
11
+
12
+ result.delete_if { |k, v| v.nil? }
13
+
14
+ {q: result}
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,60 +1,60 @@
1
- module RailsPerformance
2
- module Reports
3
- class BaseReport
4
- attr_reader :db, :group, :sort, :title
5
-
6
- def initialize(db, group: nil, sort: nil, title: nil)
7
- @db = db
8
- @group = group
9
- @sort = sort
10
- @title = title
11
-
12
- set_defaults
13
- end
14
-
15
- def collect
16
- db.group_by(group).inject([]) do |res, (k,v)|
17
- res << yield(k, v)
18
- res
19
- end
20
- end
21
-
22
- def set_defaults; end
23
-
24
- def self.time_in_app_time_zone(time)
25
- app_time_zone = ::Rails.application.config.time_zone
26
- if app_time_zone.present?
27
- time.in_time_zone(app_time_zone)
28
- else
29
- time
30
- end
31
- end
32
-
33
- def calculate_data
34
- now = Time.current
35
- stop = Time.at(60 * ((now.to_i)/ 60))
36
- offset = RailsPerformance::Reports::BaseReport::time_in_app_time_zone(now).utc_offset
37
- current = stop - RailsPerformance.duration
38
-
39
- @data = []
40
- all = {}
41
-
42
- # read current values
43
- db.group_by(group).each do |(k, v)|
44
- yield(all, k, v)
45
- end
46
-
47
- # add blank columns
48
- while current <= stop
49
- key = (current).strftime(RailsPerformance::FORMAT)
50
- views = all[key].presence || 0
51
- @data << [(current.to_i + offset) * 1000, views.round(2)]
52
- current += 1.minute
53
- end
54
-
55
- # sort by time
56
- @data.sort!
57
- end
58
- end
59
- end
60
- end
1
+ module RailsPerformance
2
+ module Reports
3
+ class BaseReport
4
+ attr_reader :db, :group, :sort, :title
5
+
6
+ def initialize(db, group: nil, sort: nil, title: nil)
7
+ @db = db
8
+ @group = group
9
+ @sort = sort
10
+ @title = title
11
+
12
+ set_defaults
13
+ end
14
+
15
+ def set_defaults
16
+ end
17
+
18
+ def collect
19
+ db.group_by(group).each_with_object([]) do |(k, v), res|
20
+ res << yield(k, v)
21
+ end
22
+ end
23
+
24
+ def self.time_in_app_time_zone(time)
25
+ app_time_zone = ::Rails.application.config.time_zone
26
+ if app_time_zone.present?
27
+ time.in_time_zone(app_time_zone)
28
+ else
29
+ time
30
+ end
31
+ end
32
+
33
+ def calculate_data
34
+ now = Time.current
35
+ stop = Time.at(60 * (now.to_i / 60))
36
+ offset = RailsPerformance::Reports::BaseReport.time_in_app_time_zone(now).utc_offset
37
+ current = stop - RailsPerformance.duration
38
+
39
+ @data = []
40
+ all = {}
41
+
42
+ # read current values
43
+ db.group_by(group).each do |(k, v)|
44
+ yield(all, k, v)
45
+ end
46
+
47
+ # add blank columns
48
+ while current <= stop
49
+ key = current.strftime(RailsPerformance::FORMAT)
50
+ views = all[key].presence || 0
51
+ @data << [(current.to_i + offset) * 1000, views.round(2)]
52
+ current += 1.minute
53
+ end
54
+
55
+ # sort by time
56
+ @data.sort!
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,18 +1,15 @@
1
- module RailsPerformance
2
- module Reports
3
- class BreakdownReport < BaseReport
4
-
5
- def set_defaults
6
- @sort ||= :datetimei
7
- end
8
-
9
- def data
10
- db.data
11
- .collect{|e| e.record_hash}
12
- .sort{|a, b| b[sort] <=> a[sort]}
13
- end
14
- end
15
-
16
-
17
- end
18
- end
1
+ module RailsPerformance
2
+ module Reports
3
+ class BreakdownReport < BaseReport
4
+ def set_defaults
5
+ @sort ||= :datetimei
6
+ end
7
+
8
+ def data
9
+ db.data
10
+ .collect { |e| e.record_hash }
11
+ .sort { |a, b| b[sort] <=> a[sort] }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,17 +1,15 @@
1
- module RailsPerformance
2
- module Reports
3
- class CrashReport < BaseReport
4
- def set_defaults
5
- @sort ||= :datetimei
6
- end
7
-
8
- def data
9
- db.data
10
- .collect{|e| e.record_hash}
11
- .sort{|a, b| b[sort] <=> a[sort]}
12
- end
13
- end
14
-
15
-
16
- end
17
- end
1
+ module RailsPerformance
2
+ module Reports
3
+ class CrashReport < BaseReport
4
+ def set_defaults
5
+ @sort ||= :datetimei
6
+ end
7
+
8
+ def data
9
+ db.data
10
+ .collect { |e| e.record_hash }
11
+ .sort { |a, b| b[sort] <=> a[sort] }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module RailsPerformance
2
+ module Reports
3
+ class PercentileReport < BaseReport
4
+ def data
5
+ durations = db.data.collect(&:duration)
6
+ {
7
+ p50: RailsPerformance::Utils.percentile(durations, 50),
8
+ p95: RailsPerformance::Utils.percentile(durations, 95),
9
+ p99: RailsPerformance::Utils.percentile(durations, 99)
10
+ }
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,24 +1,24 @@
1
- module RailsPerformance
2
- module Reports
3
- class RecentRequestsReport < BaseReport
4
- def set_defaults
5
- @sort ||= :datetimei
6
- end
7
-
8
- def data(from_timei = nil)
9
- time_agoi = [RailsPerformance.recent_requests_time_window.ago.to_i, from_timei.to_i].reject(&:blank?).max
10
- db.data
11
- .collect{|e| e.record_hash}
12
- .select{|e| e if e[sort] > time_agoi}
13
- .sort{|a, b| b[sort] <=> a[sort]}
14
- .first(limit)
15
- end
16
-
17
- private
18
-
19
- def limit
20
- RailsPerformance.recent_requests_limit ? RailsPerformance.recent_requests_limit.to_i : 100_000
21
- end
22
- end
23
- end
24
- end
1
+ module RailsPerformance
2
+ module Reports
3
+ class RecentRequestsReport < BaseReport
4
+ def set_defaults
5
+ @sort ||= :datetimei
6
+ end
7
+
8
+ def data(from_timei = nil)
9
+ time_agoi = [RailsPerformance.recent_requests_time_window.ago.to_i, from_timei.to_i].reject(&:blank?).max
10
+ db.data
11
+ .collect { |e| e.record_hash }
12
+ .select { |e| e if e[sort] > time_agoi }
13
+ .sort { |a, b| b[sort] <=> a[sort] }
14
+ .first(limit)
15
+ end
16
+
17
+ private
18
+
19
+ def limit
20
+ RailsPerformance.recent_requests_limit ? RailsPerformance.recent_requests_limit.to_i : 100_000
21
+ end
22
+ end
23
+ end
24
+ end