rails_performance 1.2.3 → 1.3.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.
Files changed (53) 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 +95 -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/recent.html.erb +8 -6
  13. data/app/views/rails_performance/rails_performance/requests.html.erb +8 -1
  14. data/app/views/rails_performance/rails_performance/slow.html.erb +3 -0
  15. data/app/views/rails_performance/shared/_header.html.erb +0 -1
  16. data/app/views/rails_performance/stylesheets/style.css +6 -0
  17. data/config/routes.rb +16 -18
  18. data/lib/generators/rails_performance/install/install_generator.rb +1 -1
  19. data/lib/generators/rails_performance/install/templates/initializer.rb +56 -42
  20. data/lib/rails_performance/data_source.rb +120 -121
  21. data/lib/rails_performance/engine.rb +8 -8
  22. data/lib/rails_performance/extensions/trace.rb +32 -33
  23. data/lib/rails_performance/gems/custom_ext.rb +31 -34
  24. data/lib/rails_performance/gems/delayed_job_ext.rb +50 -54
  25. data/lib/rails_performance/gems/grape_ext.rb +33 -35
  26. data/lib/rails_performance/gems/rake_ext.rb +41 -44
  27. data/lib/rails_performance/gems/sidekiq_ext.rb +34 -37
  28. data/lib/rails_performance/instrument/metrics_collector.rb +50 -50
  29. data/lib/rails_performance/models/base_record.rb +33 -36
  30. data/lib/rails_performance/models/collection.rb +35 -36
  31. data/lib/rails_performance/models/custom_record.rb +47 -48
  32. data/lib/rails_performance/models/delayed_job_record.rb +61 -62
  33. data/lib/rails_performance/models/grape_record.rb +60 -61
  34. data/lib/rails_performance/models/rake_record.rb +48 -49
  35. data/lib/rails_performance/models/request_record.rb +123 -120
  36. data/lib/rails_performance/models/sidekiq_record.rb +65 -66
  37. data/lib/rails_performance/models/trace_record.rb +18 -19
  38. data/lib/rails_performance/rails/middleware.rb +75 -76
  39. data/lib/rails_performance/rails/query_builder.rb +18 -20
  40. data/lib/rails_performance/reports/base_report.rb +60 -60
  41. data/lib/rails_performance/reports/breakdown_report.rb +15 -18
  42. data/lib/rails_performance/reports/crash_report.rb +15 -17
  43. data/lib/rails_performance/reports/recent_requests_report.rb +24 -24
  44. data/lib/rails_performance/reports/requests_report.rb +27 -27
  45. data/lib/rails_performance/reports/response_time_report.rb +17 -17
  46. data/lib/rails_performance/reports/slow_requests_report.rb +4 -4
  47. data/lib/rails_performance/reports/throughput_report.rb +15 -17
  48. data/lib/rails_performance/reports/trace_report.rb +16 -18
  49. data/lib/rails_performance/thread/current_request.rb +33 -34
  50. data/lib/rails_performance/utils.rb +53 -54
  51. data/lib/rails_performance/version.rb +2 -2
  52. data/lib/rails_performance.rb +33 -34
  53. metadata +20 -3
@@ -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 collect
16
+ db.group_by(group).each_with_object([]) do |(k, v), res|
17
+ res << yield(k, v)
18
+ end
19
+ end
20
+
21
+ def set_defaults
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
@@ -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
@@ -1,27 +1,27 @@
1
- module RailsPerformance
2
- module Reports
3
- class RequestsReport < BaseReport
4
- def set_defaults
5
- @sort ||= :count
6
- end
7
-
8
- def data
9
- collect do |k, v|
10
- durations = v.collect{|e| e["duration"]}.compact
11
- view_runtimes = v.collect{|e| e["view_runtime"]}.compact
12
- db_runtimes = v.collect{|e| e["db_runtime"]}.compact
13
- {
14
- group: k,
15
- count: v.size,
16
- duration_average: durations.sum.to_f / durations.size,
17
- view_runtime_average: view_runtimes.sum.to_f / view_runtimes.size,
18
- db_runtime_average: db_runtimes.sum.to_f / db_runtimes.size,
19
- duration_slowest: durations.max,
20
- view_runtime_slowest: view_runtimes.max,
21
- db_runtime_slowest: db_runtimes.max,
22
- }
23
- end.sort_by{|e| -e[sort].to_f} # to_f because could ne NaN or nil
24
- end
25
- end
26
- end
27
- end
1
+ module RailsPerformance
2
+ module Reports
3
+ class RequestsReport < BaseReport
4
+ def set_defaults
5
+ @sort ||= :count
6
+ end
7
+
8
+ def data
9
+ collect do |k, v|
10
+ durations = v.collect { |e| e["duration"] }.compact
11
+ view_runtimes = v.collect { |e| e["view_runtime"] }.compact
12
+ db_runtimes = v.collect { |e| e["db_runtime"] }.compact
13
+ {
14
+ group: k,
15
+ count: v.size,
16
+ duration_average: durations.sum.to_f / durations.size,
17
+ view_runtime_average: view_runtimes.sum.to_f / view_runtimes.size,
18
+ db_runtime_average: db_runtimes.sum.to_f / db_runtimes.size,
19
+ duration_slowest: durations.max,
20
+ view_runtime_slowest: view_runtimes.max,
21
+ db_runtime_slowest: db_runtimes.max
22
+ }
23
+ end.sort_by { |e| -e[sort].to_f } # to_f because could ne NaN or nil
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,17 +1,17 @@
1
- module RailsPerformance
2
- module Reports
3
- class ResponseTimeReport < BaseReport
4
- def set_defaults
5
- @group ||= :datetime
6
- end
7
-
8
- def data
9
- calculate_data do |all, k, v|
10
- durations = v.collect{|e| e["duration"]}.compact
11
- next if durations.empty?
12
- all[k] = durations.sum.to_f / durations.count
13
- end
14
- end
15
- end
16
- end
17
- end
1
+ module RailsPerformance
2
+ module Reports
3
+ class ResponseTimeReport < BaseReport
4
+ def set_defaults
5
+ @group ||= :datetime
6
+ end
7
+
8
+ def data
9
+ calculate_data do |all, k, v|
10
+ durations = v.collect { |e| e["duration"] }.compact
11
+ next if durations.empty?
12
+ all[k] = durations.sum.to_f / durations.count
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -7,10 +7,10 @@ module RailsPerformance
7
7
 
8
8
  def data
9
9
  db.data
10
- .collect{|e| e.record_hash}
11
- .select{|e| e if e[sort] > RailsPerformance.slow_requests_time_window.ago.to_i}
12
- .sort{|a, b| b[sort] <=> a[sort]}
13
- .filter{|e| e[:duration] > RailsPerformance.slow_requests_threshold.to_i}
10
+ .collect { |e| e.record_hash }
11
+ .select { |e| e if e[sort] > RailsPerformance.slow_requests_time_window.ago.to_i }
12
+ .sort { |a, b| b[sort] <=> a[sort] }
13
+ .filter { |e| e[:duration] > RailsPerformance.slow_requests_threshold.to_i }
14
14
  .first(limit)
15
15
  end
16
16
 
@@ -1,17 +1,15 @@
1
- module RailsPerformance
2
- module Reports
3
- class ThroughputReport < BaseReport
4
-
5
- def set_defaults
6
- @group ||= :datetime
7
- end
8
-
9
- def data
10
- calculate_data do |all, k, v|
11
- all[k] = v.count
12
- end
13
- end
14
-
15
- end
16
- end
17
- end
1
+ module RailsPerformance
2
+ module Reports
3
+ class ThroughputReport < BaseReport
4
+ def set_defaults
5
+ @group ||= :datetime
6
+ end
7
+
8
+ def data
9
+ calculate_data do |all, k, v|
10
+ all[k] = v.count
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,18 +1,16 @@
1
- module RailsPerformance
2
- module Reports
3
- class TraceReport
4
- attr_reader :request_id
5
-
6
- def initialize(request_id:)
7
- @request_id = request_id
8
- end
9
-
10
- def data
11
- key = "trace|#{request_id}|END|#{RailsPerformance::SCHEMA}"
12
- JSON.parse(RailsPerformance.redis.get(key).presence || '[]')
13
- end
14
- end
15
-
16
-
17
- end
18
- end
1
+ module RailsPerformance
2
+ module Reports
3
+ class TraceReport
4
+ attr_reader :request_id
5
+
6
+ def initialize(request_id:)
7
+ @request_id = request_id
8
+ end
9
+
10
+ def data
11
+ key = "trace|#{request_id}|END|#{RailsPerformance::SCHEMA}"
12
+ JSON.parse(RailsPerformance.redis.get(key).presence || "[]")
13
+ end
14
+ end
15
+ end
16
+ end