rails_performance 0.9.4 → 0.9.9
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 +28 -7
- data/app/controllers/rails_performance/base_controller.rb +21 -0
- data/app/controllers/rails_performance/rails_performance_controller.rb +91 -0
- data/app/helpers/rails_performance/application_helper.rb +116 -0
- data/app/views/rails_performance/layouts/rails_performance.html.erb +2 -2
- data/app/views/rails_performance/{_summary.html.erb → rails_performance/_summary.html.erb} +0 -0
- data/app/views/rails_performance/{_trace.html.erb → rails_performance/_trace.html.erb} +0 -0
- data/app/views/rails_performance/{crashes.html.erb → rails_performance/crashes.html.erb} +0 -0
- data/app/views/rails_performance/{index.html.erb → rails_performance/index.html.erb} +0 -0
- data/app/views/rails_performance/{jobs.html.erb → rails_performance/jobs.html.erb} +0 -0
- data/app/views/rails_performance/{recent.html.erb → rails_performance/recent.html.erb} +0 -0
- data/app/views/rails_performance/{requests.html.erb → rails_performance/requests.html.erb} +1 -1
- data/app/views/rails_performance/{summary.js.erb → rails_performance/summary.js.erb} +1 -1
- data/app/views/rails_performance/{trace.js.erb → rails_performance/trace.js.erb} +1 -1
- data/config/routes.rb +2 -2
- 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 +11 -1
- data/lib/rails_performance/engine.rb +21 -16
- data/lib/rails_performance/gems/sidekiq.rb +1 -0
- data/lib/rails_performance/instrument/metrics_collector.rb +3 -2
- data/lib/rails_performance/utils.rb +1 -2
- data/lib/rails_performance/version.rb +1 -1
- metadata +31 -14
- data/app/controllers/base_controller.rb +0 -19
- data/app/controllers/rails_performance_controller.rb +0 -88
- data/app/helpers/rails_performance_helper.rb +0 -114
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6efdeeeb71789d08c284f21165138c13bdcb094cd0d48fcbaad521a946fec6f
|
4
|
+
data.tar.gz: 81b265d6bf3555fb2868504983ec3b01cf1efe9d99b4b4e74b08533d08ff2023
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f59e689a2b57a486b857cc910fccdf84fdc2a7afeee08f691a912aa3affcea5138f1be4f693699c06d9ac0c126c07f4b479e1434242d427581a413818d13fc7
|
7
|
+
data.tar.gz: 591b8b1334668e285fb8a23fdf01498e90b0f5d172378cce8abfd67af2d81f31046ee7520b1d1aad6c125cc61f13ef83f86dd6f2591eeb0fb1a2f89697518349
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# Rails Performance
|
2
2
|
|
3
|
-
[](https://github.com/igorkasyanchuk/rails_performance/actions/workflows/ruby.yml)
|
4
|
+
[](https://www.railsjazz.com)
|
5
|
+
[](https://www.patreon.com/igorkasyanchuk)
|
4
6
|
|
5
7
|
A self-hosted tool to monitor the performance of your Ruby on Rails application.
|
6
8
|
|
@@ -17,13 +19,13 @@ It allows you to track:
|
|
17
19
|
- SQL queries, rendering logs in "Recent Requests" section
|
18
20
|
- simple 500-crashes reports
|
19
21
|
- track Sidekiq jobs performance
|
20
|
-
- works with Rails 4.2+ (and probably 4.1, 4.0 too)
|
22
|
+
- works with Rails 4.2+ (and probably 4.1, 4.0 too) and Ruby 2.2+
|
21
23
|
|
22
24
|
All data are stored in `local` Redis and not sent to any 3rd party servers.
|
23
25
|
|
24
26
|
## Production
|
25
27
|
|
26
|
-
Gem is production-ready. At least on my 2 applications with ~800 unique users per day it works perfectly.
|
28
|
+
Gem is production-ready. At least on my 2 applications with ~800 unique users per day it works perfectly.
|
27
29
|
|
28
30
|
Just don't forget to protect performance dashboard with http basic auth or check of current_user.
|
29
31
|
|
@@ -49,6 +51,9 @@ RailsPerformance.setup do |config|
|
|
49
51
|
config.debug = false # currently not used>
|
50
52
|
config.enabled = true
|
51
53
|
|
54
|
+
# default path where to mount gem
|
55
|
+
config.mount_at = '/rails/performance'
|
56
|
+
|
52
57
|
# protect your Performance Dashboard with HTTP BASIC password
|
53
58
|
config.http_basic_authentication_enabled = false
|
54
59
|
config.http_basic_authentication_user_name = 'rails_performance'
|
@@ -62,12 +67,13 @@ end if defined?(RailsPerformance)
|
|
62
67
|
```
|
63
68
|
|
64
69
|
## Installation
|
70
|
+
|
65
71
|
Add this line to your application's Gemfile:
|
66
72
|
|
67
73
|
```ruby
|
68
74
|
gem 'rails_performance'
|
69
75
|
|
70
|
-
# or
|
76
|
+
# or
|
71
77
|
|
72
78
|
group :development, :production do
|
73
79
|
gem 'rails_performance'
|
@@ -75,10 +81,19 @@ end
|
|
75
81
|
```
|
76
82
|
|
77
83
|
And then execute:
|
84
|
+
|
78
85
|
```bash
|
79
86
|
$ bundle
|
80
87
|
```
|
81
88
|
|
89
|
+
Create default configuration file:
|
90
|
+
|
91
|
+
```bash
|
92
|
+
$ rails generate rails_performance:install
|
93
|
+
```
|
94
|
+
|
95
|
+
Have a look at `config/initializers/rails_performance.rb` and adjust the configuration to your needs.
|
96
|
+
|
82
97
|
You must also have installed Redis server, because this gem is storing data into it.
|
83
98
|
|
84
99
|
After installation and configuration, start your Rails application, make a few requests, and open `https://localhost:3000/rails/performance` URL.
|
@@ -121,10 +136,8 @@ The idea of this gem grew from curriosity how many RPM my app receiving per day.
|
|
121
136
|
|
122
137
|
## TODO
|
123
138
|
|
124
|
-
- documentation in Readme
|
139
|
+
- documentation in Readme?
|
125
140
|
- capture stacktrace of 500 errors and show in side panel
|
126
|
-
- generator for initial config
|
127
|
-
- CI for tests
|
128
141
|
- time/zone config?
|
129
142
|
- connected charts on dashboard, when zoom, when hover?
|
130
143
|
- ability to zoom to see requests withing specific datetime range
|
@@ -149,7 +162,15 @@ You are welcome to contribute. I've a big list of TODO.
|
|
149
162
|
## Big thanks to contributors
|
150
163
|
|
151
164
|
- https://github.com/synth
|
165
|
+
- https://github.com/alagos
|
166
|
+
- https://github.com/klondaiker
|
167
|
+
- https://github.com/jules2689
|
168
|
+
- https://github.com/PedroAugustoRamalhoDuarte
|
169
|
+
- https://github.com/haffla
|
152
170
|
|
153
171
|
## License
|
154
172
|
|
155
173
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
174
|
+
|
175
|
+
[<img src="https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/more_gems.png?raw=true"
|
176
|
+
/>](https://www.railsjazz.com/)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RailsPerformance
|
2
|
+
class BaseController < ActionController::Base
|
3
|
+
layout 'rails_performance/layouts/rails_performance'
|
4
|
+
|
5
|
+
before_action :verify_access
|
6
|
+
|
7
|
+
if RailsPerformance.http_basic_authentication_enabled
|
8
|
+
http_basic_authenticate_with \
|
9
|
+
name: RailsPerformance.http_basic_authentication_user_name,
|
10
|
+
password: RailsPerformance.http_basic_authentication_password
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def verify_access
|
16
|
+
result = RailsPerformance.verify_access_proc.call(self)
|
17
|
+
redirect_to('/', error: 'Access Denied', status: 401) unless result
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require_relative './base_controller.rb'
|
2
|
+
|
3
|
+
module RailsPerformance
|
4
|
+
class RailsPerformanceController < RailsPerformance::BaseController
|
5
|
+
include RailsPerformance::ApplicationHelper
|
6
|
+
|
7
|
+
if RailsPerformance.enabled
|
8
|
+
def index
|
9
|
+
@datasource = RP::DataSource.new(**prepare_query, type: :requests, klass: RP::Models::Record)
|
10
|
+
db = @datasource.db
|
11
|
+
|
12
|
+
@throughput_report = RP::Reports::ThroughputReport.new(db)
|
13
|
+
@throughput_report_data = @throughput_report.data
|
14
|
+
|
15
|
+
@response_time_report = RP::Reports::ResponseTimeReport.new(db)
|
16
|
+
@response_time_report_data = @response_time_report.data
|
17
|
+
end
|
18
|
+
|
19
|
+
def summary
|
20
|
+
@datasource = RP::DataSource.new(**prepare_query, type: :requests, klass: RP::Models::Record)
|
21
|
+
db = @datasource.db
|
22
|
+
|
23
|
+
@throughput_report = RP::Reports::ThroughputReport.new(db)
|
24
|
+
@throughput_report_data = @throughput_report.data
|
25
|
+
|
26
|
+
@response_time_report = RP::Reports::ResponseTimeReport.new(db)
|
27
|
+
@response_time_report_data = @response_time_report.data
|
28
|
+
|
29
|
+
@report = RP::Reports::BreakdownReport.new(db, title: "Requests")
|
30
|
+
@data = @report.data
|
31
|
+
|
32
|
+
respond_to do |format|
|
33
|
+
format.js {}
|
34
|
+
format.any { render plain: "Doesn't open in new window. Wait until full page load." }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def trace
|
39
|
+
@record = RP::Models::Record.find_by(request_id: params[:id])
|
40
|
+
@report = RP::Reports::TraceReport.new(request_id: params[:id])
|
41
|
+
@data = @report.data
|
42
|
+
respond_to do |format|
|
43
|
+
format.js {}
|
44
|
+
format.any { render plain: "Doesn't open in new window. Wait until full page load." }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def crashes
|
49
|
+
@datasource = RP::DataSource.new(**prepare_query({status_eq: 500}), type: :requests, klass: RP::Models::Record)
|
50
|
+
db = @datasource.db
|
51
|
+
@report = RP::Reports::CrashReport.new(db)
|
52
|
+
@data = @report.data
|
53
|
+
end
|
54
|
+
|
55
|
+
def requests
|
56
|
+
@datasource = RP::DataSource.new(**prepare_query, type: :requests, klass: RP::Models::Record)
|
57
|
+
db = @datasource.db
|
58
|
+
@report = RP::Reports::RequestsReport.new(db, group: :controller_action_format, sort: :count)
|
59
|
+
@data = @report.data
|
60
|
+
end
|
61
|
+
|
62
|
+
def recent
|
63
|
+
@datasource = RP::DataSource.new(**prepare_query, type: :requests, klass: RP::Models::Record)
|
64
|
+
db = @datasource.db
|
65
|
+
@report = RP::Reports::RecentRequestsReport.new(db)
|
66
|
+
@data = @report.data
|
67
|
+
end
|
68
|
+
|
69
|
+
def jobs
|
70
|
+
@datasource = RP::DataSource.new(**prepare_query, type: :jobs, klass: RP::Models::JobRecord)
|
71
|
+
db = @datasource.db
|
72
|
+
|
73
|
+
@throughput_report = RP::Reports::ThroughputReport.new(db)
|
74
|
+
@throughput_report_data = @throughput_report.data
|
75
|
+
|
76
|
+
@response_time_report = RP::Reports::ResponseTimeReport.new(db)
|
77
|
+
@response_time_report_data = @response_time_report.data
|
78
|
+
|
79
|
+
@recent_report = RP::Reports::RecentRequestsReport.new(db)
|
80
|
+
@recent_report_data = @recent_report.data(:jobs)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def prepare_query(query = params)
|
86
|
+
RP::Rails::QueryBuilder.compose_from(query)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module RailsPerformance
|
2
|
+
module ApplicationHelper
|
3
|
+
def round_it(value)
|
4
|
+
return nil unless value
|
5
|
+
return value if value.is_a?(Integer)
|
6
|
+
|
7
|
+
value.nan? ? nil : value.round(1)
|
8
|
+
end
|
9
|
+
|
10
|
+
def duraction_alert_class(duration_str)
|
11
|
+
if duration_str.to_s =~ /(\d+.?\d+?)/
|
12
|
+
duration = $1.to_f
|
13
|
+
if duration >= 500
|
14
|
+
'has-background-danger has-text-white-bis'
|
15
|
+
elsif duration >= 200
|
16
|
+
'has-background-warning has-text-black-ter'
|
17
|
+
else
|
18
|
+
'has-background-success has-text-white-bis'
|
19
|
+
end
|
20
|
+
else
|
21
|
+
'has-background-light'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def extract_duration(str)
|
26
|
+
if (str =~ /Duration: (\d+.?\d+?ms)/i)
|
27
|
+
$1
|
28
|
+
else
|
29
|
+
'-'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def ms(value)
|
34
|
+
result = round_it(value)
|
35
|
+
result && result != 0 ? "#{result} ms" : '-'
|
36
|
+
end
|
37
|
+
|
38
|
+
def short_path(path, length: 60)
|
39
|
+
content_tag :span, title: path do
|
40
|
+
truncate(path, length: length)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def link_to_path(e)
|
45
|
+
if e[:method] == 'GET'
|
46
|
+
link_to(short_path(e[:path]), e[:path], target: '_blank')
|
47
|
+
else
|
48
|
+
short_path(e[:path])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def report_name(h)
|
53
|
+
h.except(:on).collect do |k, v|
|
54
|
+
next if v.blank?
|
55
|
+
|
56
|
+
%Q{
|
57
|
+
<div class="control">
|
58
|
+
<span class="tags has-addons">
|
59
|
+
<span class="tag">#{k}</span>
|
60
|
+
<span class="tag is-info is-light">#{v}</span>
|
61
|
+
</span>
|
62
|
+
</div>}
|
63
|
+
end.compact.join.html_safe
|
64
|
+
end
|
65
|
+
|
66
|
+
def status_tag(status)
|
67
|
+
klass = case status.to_s
|
68
|
+
when /^5/
|
69
|
+
"tag is-danger"
|
70
|
+
when /^4/
|
71
|
+
"tag is-warning"
|
72
|
+
when /^3/
|
73
|
+
"tag is-info"
|
74
|
+
when /^2/
|
75
|
+
"tag is-success"
|
76
|
+
else
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
content_tag(:span, class: klass) do
|
80
|
+
status
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def icon(name)
|
85
|
+
# https://www.iconfinder.com/iconsets/vivid
|
86
|
+
raw File.read(File.expand_path(File.dirname(__FILE__) + "/../../assets/images/#{name}.svg"))
|
87
|
+
end
|
88
|
+
|
89
|
+
def insert_css_file(file)
|
90
|
+
raw "<style>#{raw File.read File.expand_path(File.dirname(__FILE__) + "/../../views/rails_performance/stylesheets/#{file}")}</style>"
|
91
|
+
end
|
92
|
+
|
93
|
+
def insert_js_file(file)
|
94
|
+
raw "<script>#{raw File.read File.expand_path(File.dirname(__FILE__) + "/../../views/rails_performance/javascripts/#{file}")}</script>"
|
95
|
+
end
|
96
|
+
|
97
|
+
def format_datetime(e)
|
98
|
+
e.strftime("%Y-%m-%d %H:%M:%S")
|
99
|
+
end
|
100
|
+
|
101
|
+
def active?(section)
|
102
|
+
case section
|
103
|
+
when :dashboard
|
104
|
+
"is-active" if controller_name == "rails_performance" && action_name == "index"
|
105
|
+
when :crashes
|
106
|
+
"is-active" if controller_name == "rails_performance" && action_name == "crashes"
|
107
|
+
when :requests
|
108
|
+
"is-active" if controller_name == "rails_performance" && action_name == "requests"
|
109
|
+
when :recent
|
110
|
+
"is-active" if controller_name == "rails_performance" && action_name == "recent"
|
111
|
+
when :jobs
|
112
|
+
"is-active" if controller_name == "rails_performance" && action_name == "jobs"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -5,7 +5,7 @@
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
6
|
<title>Rails Performance</title>
|
7
7
|
<%= csrf_meta_tags %>
|
8
|
-
<%= csp_meta_tag if ::Rails::VERSION::
|
8
|
+
<%= csp_meta_tag if ::Rails::VERSION::STRING.to_f >= 5.2 %>
|
9
9
|
<%= render '/rails_performance/stylesheets/stylesheets' %>
|
10
10
|
<link rel="shortcut icon" href="/favicon.ico">
|
11
11
|
</head>
|
@@ -29,4 +29,4 @@
|
|
29
29
|
<%= yield :on_load %>
|
30
30
|
|
31
31
|
</body>
|
32
|
-
</html>
|
32
|
+
</html>
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -26,7 +26,7 @@
|
|
26
26
|
<% c, a = groups[0].split("#") %>
|
27
27
|
<tr>
|
28
28
|
<td><%= link_to groups[0], rails_performance.rails_performance_summary_path({controller_eq: c, action_eq: a}), remote: true %></td>
|
29
|
-
<td><%= link_to groups[1]
|
29
|
+
<td><%= link_to groups[1].try(:upcase), rails_performance.rails_performance_summary_path({controller_eq: c, action_eq: a, format_eq: groups[1]}), remote: true %></td>
|
30
30
|
<td><%= e[:count] %></td>
|
31
31
|
<td class="nowrap"><%= ms e[:duration_average] %></td>
|
32
32
|
<td class="nowrap"><%= ms e[:view_runtime_average] %></td>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
window.panel.header.html(window.panel.close + '<%= j report_name(@datasource.q) %>');
|
2
|
-
window.panel.content.html("<%= j render '/rails_performance/summary' %>");
|
2
|
+
window.panel.content.html("<%= j render '/rails_performance/rails_performance/summary' %>");
|
3
3
|
|
4
4
|
var data1 = <%= raw @throughput_report_data.to_json %>;
|
5
5
|
showTIRChart('throughput_report_chart_mini', data1, ' rpm', 'RPM');
|
data/config/routes.rb
CHANGED
@@ -13,9 +13,9 @@ end
|
|
13
13
|
|
14
14
|
Rails.application.routes.draw do
|
15
15
|
begin
|
16
|
-
mount RailsPerformance::Engine =>
|
16
|
+
mount RailsPerformance::Engine => RailsPerformance.mount_at, as: 'rails_performance'
|
17
17
|
rescue ArgumentError
|
18
18
|
# already added
|
19
|
-
# this
|
19
|
+
# this code exist here because engine not includes routing automatically
|
20
20
|
end
|
21
21
|
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class RailsPerformance::InstallGenerator < Rails::Generators::Base
|
2
|
+
source_root File.expand_path('templates', __dir__)
|
3
|
+
desc "Generates initial config for rails_performance gem"
|
4
|
+
|
5
|
+
def copy_initializer_file
|
6
|
+
copy_file "initializer.rb", "config/initializers/rails_performance.rb"
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
RailsPerformance.setup do |config|
|
2
|
+
config.redis = Redis::Namespace.new("#{Rails.env}-rails-performance", redis: Redis.new)
|
3
|
+
config.duration = 4.hours
|
4
|
+
|
5
|
+
config.debug = false # currently not used>
|
6
|
+
config.enabled = true
|
7
|
+
|
8
|
+
# default path where to mount gem
|
9
|
+
config.mount_at = '/rails/performance'
|
10
|
+
|
11
|
+
# protect your Performance Dashboard with HTTP BASIC password
|
12
|
+
config.http_basic_authentication_enabled = false
|
13
|
+
config.http_basic_authentication_user_name = 'rails_performance'
|
14
|
+
config.http_basic_authentication_password = 'password12'
|
15
|
+
|
16
|
+
# if you need an additional rules to check user permissions
|
17
|
+
config.verify_access_proc = proc { |controller| true }
|
18
|
+
# for example when you have `current_user`
|
19
|
+
# config.verify_access_proc = proc { |controller| controller.current_user && controller.current_user.admin? }
|
20
|
+
|
21
|
+
# You can ignore endpoints with Rails standard notation controller#action
|
22
|
+
# config.ignored_endpoints = ['HomeController#contact']
|
23
|
+
end if defined?(RailsPerformance)
|
data/lib/rails_performance.rb
CHANGED
@@ -34,6 +34,10 @@ module RailsPerformance
|
|
34
34
|
mattr_accessor :enabled
|
35
35
|
@@enabled = true
|
36
36
|
|
37
|
+
# default path where to mount gem
|
38
|
+
mattr_accessor :mount_at
|
39
|
+
@@mount_at = "/rails/performance"
|
40
|
+
|
37
41
|
# Enable http basic authentication
|
38
42
|
mattr_accessor :http_basic_authentication_enabled
|
39
43
|
@@http_basic_authentication_enabled = false
|
@@ -50,6 +54,12 @@ module RailsPerformance
|
|
50
54
|
mattr_accessor :verify_access_proc
|
51
55
|
@@verify_access_proc = proc { |controller| true }
|
52
56
|
|
57
|
+
mattr_reader :ignored_endpoints
|
58
|
+
def RailsPerformance.ignored_endpoints=(endpoints)
|
59
|
+
@@ignored_endpoints = Set.new(endpoints)
|
60
|
+
end
|
61
|
+
@@ignored_endpoints = []
|
62
|
+
|
53
63
|
def self.setup
|
54
64
|
yield(self)
|
55
65
|
end
|
@@ -58,4 +68,4 @@ end
|
|
58
68
|
|
59
69
|
RP = RailsPerformance
|
60
70
|
|
61
|
-
require "rails_performance/engine"
|
71
|
+
require "rails_performance/engine"
|
@@ -1,31 +1,22 @@
|
|
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'
|
4
5
|
|
5
6
|
module RailsPerformance
|
6
7
|
class Engine < ::Rails::Engine
|
8
|
+
isolate_namespace RailsPerformance
|
7
9
|
|
8
|
-
|
10
|
+
initializer "rails_performance.middleware" do |app|
|
11
|
+
next unless RailsPerformance.enabled
|
9
12
|
|
10
13
|
if ::Rails::VERSION::MAJOR.to_i >= 5
|
11
|
-
|
14
|
+
app.middleware.insert_after ActionDispatch::Executor, RailsPerformance::Rails::Middleware
|
12
15
|
else
|
13
|
-
|
16
|
+
app.middleware.insert_after ActionDispatch::Static, RailsPerformance::Rails::Middleware
|
14
17
|
end
|
15
18
|
|
16
|
-
|
17
|
-
ActiveSupport::Notifications.subscribe(
|
18
|
-
"process_action.action_controller",
|
19
|
-
RailsPerformance::Instrument::MetricsCollector.new
|
20
|
-
)
|
21
|
-
|
22
|
-
config.after_initialize do |app|
|
23
|
-
ActionView::LogSubscriber.send :prepend, RailsPerformance::Extensions::View
|
24
|
-
ActiveRecord::LogSubscriber.send :prepend, RailsPerformance::Extensions::Db
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
if const_defined?("Sidekiq")
|
19
|
+
if defined?(Sidekiq)
|
29
20
|
require_relative './gems/sidekiq.rb'
|
30
21
|
Sidekiq.configure_server do |config|
|
31
22
|
config.server_middleware do |chain|
|
@@ -33,8 +24,22 @@ module RailsPerformance
|
|
33
24
|
end
|
34
25
|
end
|
35
26
|
end
|
27
|
+
end
|
28
|
+
|
29
|
+
initializer :configure_metrics, after: :initialize_logger do
|
30
|
+
next unless RailsPerformance.enabled
|
36
31
|
|
32
|
+
ActiveSupport::Notifications.subscribe(
|
33
|
+
"process_action.action_controller",
|
34
|
+
RailsPerformance::Instrument::MetricsCollector.new
|
35
|
+
)
|
37
36
|
end
|
38
37
|
|
38
|
+
config.after_initialize do
|
39
|
+
next unless RailsPerformance.enabled
|
40
|
+
|
41
|
+
ActionView::LogSubscriber.send :prepend, RailsPerformance::Extensions::View
|
42
|
+
ActiveRecord::LogSubscriber.send :prepend, RailsPerformance::Extensions::Db
|
43
|
+
end
|
39
44
|
end
|
40
45
|
end
|
@@ -18,7 +18,8 @@ module RailsPerformance
|
|
18
18
|
def call(event_name, started, finished, event_id, payload)
|
19
19
|
event = ActiveSupport::Notifications::Event.new(event_name, started, finished, event_id, payload)
|
20
20
|
|
21
|
-
return if event.payload[:path]
|
21
|
+
return if %r{#{RailsPerformance.mount_at}}.match? event.payload[:path]
|
22
|
+
return if RailsPerformance.ignored_endpoints.include? "#{event.payload[:controller]}##{event.payload[:action]}"
|
22
23
|
|
23
24
|
record = {
|
24
25
|
controller: event.payload[:controller],
|
@@ -38,4 +39,4 @@ module RailsPerformance
|
|
38
39
|
end
|
39
40
|
end
|
40
41
|
end
|
41
|
-
end
|
42
|
+
end
|
@@ -60,8 +60,7 @@ module RailsPerformance
|
|
60
60
|
def Utils.save_to_redis(key, value, expire = RP.duration.to_i)
|
61
61
|
# puts " [SAVE] key ---> #{key}\n"
|
62
62
|
# puts " value ---> #{value.to_json}\n\n"
|
63
|
-
RP.redis.set(key, value.to_json)
|
64
|
-
RP.redis.expire(key, expire.to_i)
|
63
|
+
RP.redis.set(key, value.to_json, ex: expire.to_i)
|
65
64
|
end
|
66
65
|
|
67
66
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_performance
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Igor Kasyanchuk
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-04-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: mimemagic
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
description: 3rd party dependency-free solution how to monitor performance of your
|
98
112
|
Rails applications.
|
99
113
|
email:
|
@@ -115,14 +129,10 @@ files:
|
|
115
129
|
- app/assets/images/import.svg
|
116
130
|
- app/assets/images/menu.svg
|
117
131
|
- app/assets/images/stat.svg
|
118
|
-
- app/controllers/base_controller.rb
|
119
|
-
- app/controllers/rails_performance_controller.rb
|
120
|
-
- app/helpers/
|
132
|
+
- app/controllers/rails_performance/base_controller.rb
|
133
|
+
- app/controllers/rails_performance/rails_performance_controller.rb
|
134
|
+
- app/helpers/rails_performance/application_helper.rb
|
121
135
|
- app/views/rails_performance/_panel.html.erb
|
122
|
-
- app/views/rails_performance/_summary.html.erb
|
123
|
-
- app/views/rails_performance/_trace.html.erb
|
124
|
-
- app/views/rails_performance/crashes.html.erb
|
125
|
-
- app/views/rails_performance/index.html.erb
|
126
136
|
- app/views/rails_performance/javascripts/_javascripts.html.erb
|
127
137
|
- app/views/rails_performance/javascripts/app.js
|
128
138
|
- app/views/rails_performance/javascripts/jquery-3.4.1.min.js
|
@@ -130,18 +140,25 @@ files:
|
|
130
140
|
- app/views/rails_performance/javascripts/rails.js
|
131
141
|
- app/views/rails_performance/javascripts/stupidtable.min.js
|
132
142
|
- app/views/rails_performance/javascripts/table.js
|
133
|
-
- app/views/rails_performance/jobs.html.erb
|
134
143
|
- app/views/rails_performance/layouts/rails_performance.html.erb
|
135
|
-
- app/views/rails_performance/
|
136
|
-
- app/views/rails_performance/
|
144
|
+
- app/views/rails_performance/rails_performance/_summary.html.erb
|
145
|
+
- app/views/rails_performance/rails_performance/_trace.html.erb
|
146
|
+
- app/views/rails_performance/rails_performance/crashes.html.erb
|
147
|
+
- app/views/rails_performance/rails_performance/index.html.erb
|
148
|
+
- app/views/rails_performance/rails_performance/jobs.html.erb
|
149
|
+
- app/views/rails_performance/rails_performance/recent.html.erb
|
150
|
+
- app/views/rails_performance/rails_performance/requests.html.erb
|
151
|
+
- app/views/rails_performance/rails_performance/summary.js.erb
|
152
|
+
- app/views/rails_performance/rails_performance/trace.js.erb
|
137
153
|
- app/views/rails_performance/shared/_header.html.erb
|
138
154
|
- app/views/rails_performance/stylesheets/_stylesheets.html.erb
|
139
155
|
- app/views/rails_performance/stylesheets/bulma.min.css
|
140
156
|
- app/views/rails_performance/stylesheets/panel.css
|
141
157
|
- app/views/rails_performance/stylesheets/style.css
|
142
|
-
- app/views/rails_performance/summary.js.erb
|
143
|
-
- app/views/rails_performance/trace.js.erb
|
144
158
|
- config/routes.rb
|
159
|
+
- lib/generators/rails_performance/install/USAGE
|
160
|
+
- lib/generators/rails_performance/install/install_generator.rb
|
161
|
+
- lib/generators/rails_performance/install/templates/initializer.rb
|
145
162
|
- lib/rails_performance.rb
|
146
163
|
- lib/rails_performance/data_source.rb
|
147
164
|
- lib/rails_performance/engine.rb
|
@@ -1,19 +0,0 @@
|
|
1
|
-
class BaseController < ActionController::Base
|
2
|
-
layout 'rails_performance/layouts/rails_performance'
|
3
|
-
|
4
|
-
before_action :verify_access
|
5
|
-
|
6
|
-
if RailsPerformance.http_basic_authentication_enabled
|
7
|
-
http_basic_authenticate_with \
|
8
|
-
name: RailsPerformance.http_basic_authentication_user_name,
|
9
|
-
password: RailsPerformance.http_basic_authentication_password
|
10
|
-
end
|
11
|
-
|
12
|
-
private
|
13
|
-
|
14
|
-
def verify_access
|
15
|
-
result = RailsPerformance.verify_access_proc.call(self)
|
16
|
-
redirect_to('/', error: 'Access Denied', status: 401) unless result
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
@@ -1,88 +0,0 @@
|
|
1
|
-
require_relative './base_controller.rb'
|
2
|
-
|
3
|
-
class RailsPerformanceController < BaseController
|
4
|
-
|
5
|
-
if RailsPerformance.enabled
|
6
|
-
def index
|
7
|
-
@datasource = RP::DataSource.new(**prepare_query, type: :requests, klass: RP::Models::Record)
|
8
|
-
db = @datasource.db
|
9
|
-
|
10
|
-
@throughput_report = RP::Reports::ThroughputReport.new(db)
|
11
|
-
@throughput_report_data = @throughput_report.data
|
12
|
-
|
13
|
-
@response_time_report = RP::Reports::ResponseTimeReport.new(db)
|
14
|
-
@response_time_report_data = @response_time_report.data
|
15
|
-
end
|
16
|
-
|
17
|
-
def summary
|
18
|
-
@datasource = RP::DataSource.new(**prepare_query, type: :requests, klass: RP::Models::Record)
|
19
|
-
db = @datasource.db
|
20
|
-
|
21
|
-
@throughput_report = RP::Reports::ThroughputReport.new(db)
|
22
|
-
@throughput_report_data = @throughput_report.data
|
23
|
-
|
24
|
-
@response_time_report = RP::Reports::ResponseTimeReport.new(db)
|
25
|
-
@response_time_report_data = @response_time_report.data
|
26
|
-
|
27
|
-
@report = RP::Reports::BreakdownReport.new(db, title: "Requests")
|
28
|
-
@data = @report.data
|
29
|
-
|
30
|
-
respond_to do |format|
|
31
|
-
format.js {}
|
32
|
-
format.any { render plain: "Doesn't open in new window. Wait until full page load." }
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def trace
|
37
|
-
@record = RP::Models::Record.find_by(request_id: params[:id])
|
38
|
-
@report = RP::Reports::TraceReport.new(request_id: params[:id])
|
39
|
-
@data = @report.data
|
40
|
-
respond_to do |format|
|
41
|
-
format.js {}
|
42
|
-
format.any { render plain: "Doesn't open in new window. Wait until full page load." }
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def crashes
|
47
|
-
@datasource = RP::DataSource.new(**prepare_query({status_eq: 500}), type: :requests, klass: RP::Models::Record)
|
48
|
-
db = @datasource.db
|
49
|
-
@report = RP::Reports::CrashReport.new(db)
|
50
|
-
@data = @report.data
|
51
|
-
end
|
52
|
-
|
53
|
-
def requests
|
54
|
-
@datasource = RP::DataSource.new(**prepare_query, type: :requests, klass: RP::Models::Record)
|
55
|
-
db = @datasource.db
|
56
|
-
@report = RP::Reports::RequestsReport.new(db, group: :controller_action_format, sort: :count)
|
57
|
-
@data = @report.data
|
58
|
-
end
|
59
|
-
|
60
|
-
def recent
|
61
|
-
@datasource = RP::DataSource.new(**prepare_query, type: :requests, klass: RP::Models::Record)
|
62
|
-
db = @datasource.db
|
63
|
-
@report = RP::Reports::RecentRequestsReport.new(db)
|
64
|
-
@data = @report.data
|
65
|
-
end
|
66
|
-
|
67
|
-
def jobs
|
68
|
-
@datasource = RP::DataSource.new(**prepare_query, type: :jobs, klass: RP::Models::JobRecord)
|
69
|
-
db = @datasource.db
|
70
|
-
|
71
|
-
@throughput_report = RP::Reports::ThroughputReport.new(db)
|
72
|
-
@throughput_report_data = @throughput_report.data
|
73
|
-
|
74
|
-
@response_time_report = RP::Reports::ResponseTimeReport.new(db)
|
75
|
-
@response_time_report_data = @response_time_report.data
|
76
|
-
|
77
|
-
@recent_report = RP::Reports::RecentRequestsReport.new(db)
|
78
|
-
@recent_report_data = @recent_report.data(:jobs)
|
79
|
-
end
|
80
|
-
|
81
|
-
private
|
82
|
-
|
83
|
-
def prepare_query(query = params)
|
84
|
-
RP::Rails::QueryBuilder.compose_from(query)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
end
|
@@ -1,114 +0,0 @@
|
|
1
|
-
module RailsPerformanceHelper
|
2
|
-
def round_it(value)
|
3
|
-
return nil unless value
|
4
|
-
return value if value.is_a?(Integer)
|
5
|
-
|
6
|
-
value.nan? ? nil : value.round(1)
|
7
|
-
end
|
8
|
-
|
9
|
-
def duraction_alert_class(duration_str)
|
10
|
-
if duration_str.to_s =~ /(\d+.?\d+?)/
|
11
|
-
duration = $1.to_f
|
12
|
-
if duration >= 500
|
13
|
-
'has-background-danger has-text-white-bis'
|
14
|
-
elsif duration >= 200
|
15
|
-
'has-background-warning has-text-black-ter'
|
16
|
-
else
|
17
|
-
'has-background-success has-text-white-bis'
|
18
|
-
end
|
19
|
-
else
|
20
|
-
'has-background-light'
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def extract_duration(str)
|
25
|
-
if (str =~ /Duration: (\d+.?\d+?ms)/i)
|
26
|
-
$1
|
27
|
-
else
|
28
|
-
'-'
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def ms(value)
|
33
|
-
result = round_it(value)
|
34
|
-
result && result != 0 ? "#{result} ms" : '-'
|
35
|
-
end
|
36
|
-
|
37
|
-
def short_path(path, length: 60)
|
38
|
-
content_tag :span, title: path do
|
39
|
-
truncate(path, length: length)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def link_to_path(e)
|
44
|
-
if e[:method] == 'GET'
|
45
|
-
link_to(short_path(e[:path]), e[:path], target: '_blank')
|
46
|
-
else
|
47
|
-
short_path(e[:path])
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def report_name(h)
|
52
|
-
h.except(:on).collect do |k, v|
|
53
|
-
next if v.blank?
|
54
|
-
|
55
|
-
%Q{
|
56
|
-
<div class="control">
|
57
|
-
<span class="tags has-addons">
|
58
|
-
<span class="tag">#{k}</span>
|
59
|
-
<span class="tag is-info is-light">#{v}</span>
|
60
|
-
</span>
|
61
|
-
</div>}
|
62
|
-
end.compact.join.html_safe
|
63
|
-
end
|
64
|
-
|
65
|
-
def status_tag(status)
|
66
|
-
klass = case status.to_s
|
67
|
-
when /^5/
|
68
|
-
"tag is-danger"
|
69
|
-
when /^4/
|
70
|
-
"tag is-warning"
|
71
|
-
when /^3/
|
72
|
-
"tag is-info"
|
73
|
-
when /^2/
|
74
|
-
"tag is-success"
|
75
|
-
else
|
76
|
-
nil
|
77
|
-
end
|
78
|
-
content_tag(:span, class: klass) do
|
79
|
-
status
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def icon(name)
|
84
|
-
# https://www.iconfinder.com/iconsets/vivid
|
85
|
-
raw File.read(File.expand_path(File.dirname(__FILE__) + "/../assets/images/#{name}.svg"))
|
86
|
-
end
|
87
|
-
|
88
|
-
def insert_css_file(file)
|
89
|
-
raw "<style>#{raw File.read File.expand_path(File.dirname(__FILE__) + "/../views/rails_performance/stylesheets/#{file}")}</style>"
|
90
|
-
end
|
91
|
-
|
92
|
-
def insert_js_file(file)
|
93
|
-
raw "<script>#{raw File.read File.expand_path(File.dirname(__FILE__) + "/../views/rails_performance/javascripts/#{file}")}</script>"
|
94
|
-
end
|
95
|
-
|
96
|
-
def format_datetime(e)
|
97
|
-
e.strftime("%Y-%m-%d %H:%M:%S")
|
98
|
-
end
|
99
|
-
|
100
|
-
def active?(section)
|
101
|
-
case section
|
102
|
-
when :dashboard
|
103
|
-
"is-active" if controller_name == "rails_performance" && action_name == "index"
|
104
|
-
when :crashes
|
105
|
-
"is-active" if controller_name == "rails_performance" && action_name == "crashes"
|
106
|
-
when :requests
|
107
|
-
"is-active" if controller_name == "rails_performance" && action_name == "requests"
|
108
|
-
when :recent
|
109
|
-
"is-active" if controller_name == "rails_performance" && action_name == "recent"
|
110
|
-
when :jobs
|
111
|
-
"is-active" if controller_name == "rails_performance" && action_name == "jobs"
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|