rails_performance 0.9.0.1 → 0.9.5
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 +39 -5
- 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/javascripts/app.js +3 -3
- data/app/views/rails_performance/layouts/rails_performance.html.erb +1 -1
- 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} +1 -1
- data/app/views/rails_performance/rails_performance/jobs.html.erb +73 -0
- data/app/views/rails_performance/{recent.html.erb → rails_performance/recent.html.erb} +8 -1
- data/app/views/rails_performance/{requests.html.erb → rails_performance/requests.html.erb} +0 -0
- data/app/views/rails_performance/{summary.js.erb → rails_performance/summary.js.erb} +2 -2
- data/app/views/rails_performance/{trace.js.erb → rails_performance/trace.js.erb} +1 -1
- data/app/views/rails_performance/shared/_header.html.erb +3 -0
- data/config/routes.rb +2 -0
- data/lib/rails_performance.rb +2 -0
- data/lib/rails_performance/data_source.rb +27 -20
- data/lib/rails_performance/engine.rb +14 -1
- data/lib/rails_performance/gems/sidekiq.rb +35 -0
- data/lib/rails_performance/models/base_record.rb +24 -0
- data/lib/rails_performance/models/job_record.rb +48 -0
- data/lib/rails_performance/models/record.rb +2 -17
- data/lib/rails_performance/rails/middleware.rb +4 -0
- data/lib/rails_performance/reports/recent_requests_report.rb +37 -14
- data/lib/rails_performance/utils.rb +17 -17
- data/lib/rails_performance/version.rb +1 -1
- metadata +32 -14
- data/app/controllers/base_controller.rb +0 -19
- data/app/controllers/rails_performance_controller.rb +0 -74
- data/app/helpers/rails_performance_helper.rb +0 -112
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91dc41ef53b796c8c0755afd109fc7b7684ea9a5567828ab0e7f9e6c80cf9e79
|
4
|
+
data.tar.gz: 434e5e19a7abb0fc86b094d591623d626ab4b8a8e2ad28b70d2dfe70583e510b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dffbb9eab0930f214009a8717c97a9fe82b7ea5fea22cd7a18d9a34426811d8467a19600e38e5fbdc1eb4c1aee1308b7819a8b79ff77d721907a644b9f2facea
|
7
|
+
data.tar.gz: 69e08649d416bf680f203ee801534c1c5dbedd5ee956e85cca5439db3eaa8062ca830800454eb37335059732239946f6a39ad0a1d756b7efa40864fc5ebb9dc9
|
data/README.md
CHANGED
@@ -1,29 +1,45 @@
|
|
1
1
|
# Rails Performance
|
2
2
|
|
3
|
-
|
3
|
+
[](https://travis-ci.org/igorkasyanchuk/rails_performance)
|
4
|
+
|
5
|
+
A self-hosted tool to monitor the performance of your Ruby on Rails application.
|
4
6
|
|
5
7
|
This is **simple and free alternative** to the New Relic APM, Datadog or other similar services.
|
6
8
|
|
9
|
+

|
10
|
+
|
7
11
|
It allows you to track:
|
8
12
|
|
9
13
|
- throughput report (see amount of RPM (requests per minute))
|
10
14
|
- an average response time
|
11
15
|
- the slowest controllers & actions
|
12
16
|
- total duration of time spent per request, views rendering, DB
|
13
|
-
- SQL queries, rendering
|
17
|
+
- SQL queries, rendering logs in "Recent Requests" section
|
14
18
|
- simple 500-crashes reports
|
19
|
+
- track Sidekiq jobs performance
|
20
|
+
- works with Rails 4.2+ (and probably 4.1, 4.0 too)
|
15
21
|
|
16
22
|
All data are stored in `local` Redis and not sent to any 3rd party servers.
|
17
23
|
|
18
24
|
## Production
|
19
25
|
|
20
|
-
Gem is production-ready. At least on my 2 applications with ~800 unique users per day it works
|
26
|
+
Gem is production-ready. At least on my 2 applications with ~800 unique users per day it works perfectly.
|
27
|
+
|
28
|
+
Just don't forget to protect performance dashboard with http basic auth or check of current_user.
|
21
29
|
|
22
30
|
## Usage
|
23
31
|
|
32
|
+
```
|
33
|
+
1. Add gem to the Gemfile (in appropriate group if needed)
|
34
|
+
2. Start rails server
|
35
|
+
3. Make a few requests to your app
|
36
|
+
4. open localhost:3000/rails/performance
|
37
|
+
5. Tune the configuration and deploy to production
|
38
|
+
```
|
39
|
+
|
24
40
|
Default configulation is listed below. But you can overide it.
|
25
41
|
|
26
|
-
Create `config/initializers/rails_performance.rb`
|
42
|
+
Create `config/initializers/rails_performance.rb` in your app:
|
27
43
|
|
28
44
|
```ruby
|
29
45
|
RailsPerformance.setup do |config|
|
@@ -50,6 +66,12 @@ Add this line to your application's Gemfile:
|
|
50
66
|
|
51
67
|
```ruby
|
52
68
|
gem 'rails_performance'
|
69
|
+
|
70
|
+
# or
|
71
|
+
|
72
|
+
group :development, :production do
|
73
|
+
gem 'rails_performance'
|
74
|
+
end
|
53
75
|
```
|
54
76
|
|
55
77
|
And then execute:
|
@@ -61,6 +83,10 @@ You must also have installed Redis server, because this gem is storing data into
|
|
61
83
|
|
62
84
|
After installation and configuration, start your Rails application, make a few requests, and open `https://localhost:3000/rails/performance` URL.
|
63
85
|
|
86
|
+
## How it works
|
87
|
+
|
88
|
+

|
89
|
+
|
64
90
|
## Limitations
|
65
91
|
|
66
92
|
- it doesn't track params of POST/PUT requests
|
@@ -70,6 +96,8 @@ After installation and configuration, start your Rails application, make a few r
|
|
70
96
|
|
71
97
|
## Redis
|
72
98
|
|
99
|
+
Gem is using Redis. This is the only one dependency.
|
100
|
+
|
73
101
|
All information is stored into Redis. The default expiration time is set to `config.duration` from the configuration.
|
74
102
|
|
75
103
|
## Development & Testing
|
@@ -79,7 +107,7 @@ Just clone the repo, setup dummy app (`rails db:migrate`).
|
|
79
107
|
After this:
|
80
108
|
|
81
109
|
- rails s
|
82
|
-
- rake
|
110
|
+
- rake test
|
83
111
|
|
84
112
|
Like a regular web development.
|
85
113
|
|
@@ -111,11 +139,17 @@ The idea of this gem grew from curriosity how many RPM my app receiving per day.
|
|
111
139
|
- collect deprecation.rails
|
112
140
|
- fix misspellings?
|
113
141
|
- show "loading banner" until jquery is loaded?
|
142
|
+
- better UI on smaller screens? Recent requests when URL's are long? Truncate with CSS?
|
143
|
+
- rules for highlighting durations? how many ms to show warning, alert
|
114
144
|
|
115
145
|
## Contributing
|
116
146
|
|
117
147
|
You are welcome to contribute. I've a big list of TODO.
|
118
148
|
|
149
|
+
## Big thanks to contributors
|
150
|
+
|
151
|
+
- https://github.com/synth
|
152
|
+
|
119
153
|
## License
|
120
154
|
|
121
155
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -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
|
@@ -1,4 +1,4 @@
|
|
1
|
-
function showTIRChart(div, data) {
|
1
|
+
function showTIRChart(div, data, addon, name) {
|
2
2
|
Highcharts.chart(div, {
|
3
3
|
time: {
|
4
4
|
timezone: 'Europe/Kiev'
|
@@ -24,7 +24,7 @@ function showTIRChart(div, data) {
|
|
24
24
|
if (this.y == 0) {
|
25
25
|
return '';
|
26
26
|
}
|
27
|
-
|
27
|
+
return this.y + addon;
|
28
28
|
}
|
29
29
|
},
|
30
30
|
xAxis: {
|
@@ -69,7 +69,7 @@ function showTIRChart(div, data) {
|
|
69
69
|
},
|
70
70
|
series: [{
|
71
71
|
type: 'area',
|
72
|
-
name:
|
72
|
+
name: name,
|
73
73
|
data: data,
|
74
74
|
fillOpacity: 0.3,
|
75
75
|
lineWidth: 1,
|
@@ -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 %>
|
8
|
+
<%= csp_meta_tag if ::Rails::VERSION::MAJOR.to_i >= 5 %>
|
9
9
|
<%= render '/rails_performance/stylesheets/stylesheets' %>
|
10
10
|
<link rel="shortcut icon" href="/favicon.ico">
|
11
11
|
</head>
|
File without changes
|
File without changes
|
File without changes
|
@@ -25,7 +25,7 @@
|
|
25
25
|
<% content_for :on_load do %>
|
26
26
|
<script>
|
27
27
|
var data1 = <%= raw @throughput_report_data.to_json %>;
|
28
|
-
showTIRChart('throughput_report_chart', data1);
|
28
|
+
showTIRChart('throughput_report_chart', data1, ' rpm', 'RPM');
|
29
29
|
|
30
30
|
var data2 = <%= raw @response_time_report_data.to_json %>;
|
31
31
|
showRTChart('response_time_report_chart', data2);
|
@@ -0,0 +1,73 @@
|
|
1
|
+
<title>Number of Requests to the Application</title>
|
2
|
+
|
3
|
+
<% unless @datasource.default? %>
|
4
|
+
<%#= link_to raw("← Back"), rails_performance_path, class: "back_link" %>
|
5
|
+
<% end %>
|
6
|
+
|
7
|
+
<div class="card">
|
8
|
+
<div class="card-content">
|
9
|
+
<h2 class="subtitle">Sidekiq Workers Throughput Report</h2>
|
10
|
+
<div id="throughput_report_chart" class="chart"></div>
|
11
|
+
<p class="content is-small">All workers in the application</p>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<br/>
|
16
|
+
|
17
|
+
<div class="card">
|
18
|
+
<div class="card-content">
|
19
|
+
<h2 class="subtitle">Average Execution Time</h2>
|
20
|
+
<div id="response_time_report_chart" class="chart"></div>
|
21
|
+
<p class="content is-small">All workers in the application</p>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
<div class="card">
|
26
|
+
<div class="card-content">
|
27
|
+
<h2 class="subtitle">Recent Jobs (last <%= RailsPerformance::Reports::RecentRequestsReport::TIME_WINDOW / 60 %> minutes)<h2>
|
28
|
+
|
29
|
+
<table class="table is-fullwidth is-hoverable is-narrow">
|
30
|
+
<thead>
|
31
|
+
<tr>
|
32
|
+
<th data-sort="string">Datetime</th>
|
33
|
+
<th data-sort="string">Queue</th>
|
34
|
+
<th data-sort="string">Worker</th>
|
35
|
+
<th data-sort="string">Job ID</th>
|
36
|
+
<th data-sort="string">Status</th>
|
37
|
+
<th data-sort="float">Duration</th>
|
38
|
+
<th>Message</th>
|
39
|
+
<th></th>
|
40
|
+
</tr>
|
41
|
+
</thead>
|
42
|
+
<tbody>
|
43
|
+
<% if @recent_report_data.empty? %>
|
44
|
+
<tr>
|
45
|
+
<td colspan="10">Nothing to show here. Try to make a few requests in main app.</td>
|
46
|
+
</tr>
|
47
|
+
<% end %>
|
48
|
+
<% @recent_report_data.each do |e| %>
|
49
|
+
<tr>
|
50
|
+
<td><%= format_datetime e[:datetime] %></td>
|
51
|
+
<td><%= e[:queue] %></td>
|
52
|
+
<td><%= e[:worker] %></td>
|
53
|
+
<td><%= e[:jid] %></td>
|
54
|
+
<td><%= status_tag e[:status] %></td>
|
55
|
+
<td class="nowrap"><%= ms e[:duration] %></td>
|
56
|
+
<td><%= e[:message] %></td>
|
57
|
+
</tr>
|
58
|
+
<% end %>
|
59
|
+
</tbody>
|
60
|
+
</table>
|
61
|
+
</div>
|
62
|
+
</div>
|
63
|
+
|
64
|
+
|
65
|
+
<% content_for :on_load do %>
|
66
|
+
<script>
|
67
|
+
var data1 = <%= raw @throughput_report_data.to_json %>;
|
68
|
+
showTIRChart('throughput_report_chart', data1, ' jobs / minute', 'Jobs');
|
69
|
+
|
70
|
+
var data2 = <%= raw @response_time_report_data.to_json %>;
|
71
|
+
showRTChart('response_time_report_chart', data2);
|
72
|
+
</script>
|
73
|
+
<% end %>
|
@@ -1,11 +1,11 @@
|
|
1
1
|
<div class="card">
|
2
2
|
<div class="card-content">
|
3
|
-
|
4
3
|
<h2 class="subtitle">Recent Requests (last <%= RailsPerformance::Reports::RecentRequestsReport::TIME_WINDOW / 60 %> minutes)<h2>
|
5
4
|
|
6
5
|
<table class="table is-fullwidth is-hoverable is-narrow">
|
7
6
|
<thead>
|
8
7
|
<tr>
|
8
|
+
<th data-sort="string"></th>
|
9
9
|
<th data-sort="string">Datetime</th>
|
10
10
|
<th data-sort="string">Controller#action</th>
|
11
11
|
<th data-sort="string">Method</th>
|
@@ -26,6 +26,13 @@
|
|
26
26
|
<% end %>
|
27
27
|
<% @data.each do |e| %>
|
28
28
|
<tr>
|
29
|
+
<td>
|
30
|
+
<% if e[:request_id].present? %>
|
31
|
+
<%= link_to rails_performance.rails_performance_trace_path(id: e[:request_id]), remote: true do %>
|
32
|
+
<span class="stats_icon_max"><%= icon 'activity' %></span>
|
33
|
+
<% end %>
|
34
|
+
<% end %>
|
35
|
+
</td>
|
29
36
|
<td><%= format_datetime e[:datetime] %></td>
|
30
37
|
<td><%= link_to e[:controller] + '#' + e[:action], rails_performance.rails_performance_summary_path({controller_eq: e[:controller], action_eq: e[:action]}), remote: true %></td>
|
31
38
|
<td><%= e[:method] %></td>
|
File without changes
|
@@ -1,8 +1,8 @@
|
|
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
|
-
showTIRChart('throughput_report_chart_mini', data1);
|
5
|
+
showTIRChart('throughput_report_chart_mini', data1, ' rpm', 'RPM');
|
6
6
|
|
7
7
|
var data2 = <%= raw @response_time_report_data.to_json %>;
|
8
8
|
showRTChart('response_time_report_chart_mini', data2);
|
@@ -18,6 +18,9 @@
|
|
18
18
|
<%= link_to 'Requests Analysis', rails_performance.rails_performance_requests_url, class: "navbar-item #{active?(:requests)}" %>
|
19
19
|
<%= link_to '500 Errors', rails_performance.rails_performance_crashes_url, class: "navbar-item #{active?(:crashes)}" %>
|
20
20
|
<%= link_to 'Recent Requests', rails_performance.rails_performance_recent_url, class: "navbar-item #{active?(:recent)}" %>
|
21
|
+
<% if defined?(Sidekiq) %>
|
22
|
+
<%= link_to 'Sidekiq', rails_performance.rails_performance_jobs_url, class: "navbar-item #{active?(:jobs)}" %>
|
23
|
+
<% end %>
|
21
24
|
</div>
|
22
25
|
|
23
26
|
<div class="navbar-end">
|
data/config/routes.rb
CHANGED
@@ -7,6 +7,8 @@ RailsPerformance::Engine.routes.draw do
|
|
7
7
|
|
8
8
|
get '/trace/:id' => 'rails_performance#trace', as: :rails_performance_trace
|
9
9
|
get '/summary' => 'rails_performance#summary', as: :rails_performance_summary
|
10
|
+
|
11
|
+
get '/jobs' => 'rails_performance#jobs', as: :rails_performance_jobs
|
10
12
|
end
|
11
13
|
|
12
14
|
Rails.application.routes.draw do
|
data/lib/rails_performance.rb
CHANGED
@@ -4,7 +4,9 @@ require_relative "./rails_performance/version.rb"
|
|
4
4
|
require_relative "rails_performance/rails/query_builder.rb"
|
5
5
|
require_relative "rails_performance/rails/middleware.rb"
|
6
6
|
require_relative "rails_performance/data_source.rb"
|
7
|
+
require_relative "rails_performance/models/base_record.rb"
|
7
8
|
require_relative "rails_performance/models/record.rb"
|
9
|
+
require_relative "rails_performance/models/job_record.rb"
|
8
10
|
require_relative "rails_performance/utils.rb"
|
9
11
|
require_relative "rails_performance/reports/base_report.rb"
|
10
12
|
require_relative "rails_performance/reports/requests_report.rb"
|
@@ -1,8 +1,10 @@
|
|
1
1
|
module RailsPerformance
|
2
2
|
class DataSource
|
3
|
-
attr_reader :q
|
3
|
+
attr_reader :q, :klass, :type
|
4
4
|
|
5
|
-
def initialize(q: {})
|
5
|
+
def initialize(q: {}, type:, klass:)
|
6
|
+
@klass = klass
|
7
|
+
@type = type
|
6
8
|
q[:on] ||= Date.today
|
7
9
|
@q = q
|
8
10
|
|
@@ -12,7 +14,7 @@ module RailsPerformance
|
|
12
14
|
def db
|
13
15
|
result = RP::Models::Collection.new
|
14
16
|
(RP::Utils.days + 1).times do |e|
|
15
|
-
RP::DataSource.new(q: self.q.merge({ on: e.days.ago.to_date })).add_to(result)
|
17
|
+
RP::DataSource.new(q: self.q.merge({ on: e.days.ago.to_date }), klass: klass, type: type).add_to(result)
|
16
18
|
end
|
17
19
|
result
|
18
20
|
end
|
@@ -34,41 +36,46 @@ module RailsPerformance
|
|
34
36
|
return [] if keys.blank?
|
35
37
|
|
36
38
|
keys.each_with_index do |key, index|
|
37
|
-
yield
|
39
|
+
yield klass.new(key, values[index])
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
41
43
|
private
|
42
44
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
def
|
45
|
+
def query
|
46
|
+
case type
|
47
|
+
when :requests
|
48
|
+
"performance|*#{compile_requests_query}*|END"
|
49
|
+
when :jobs
|
50
|
+
"jobs|*#{compile_jobs_query}*|END"
|
51
|
+
else
|
52
|
+
raise "wrong type for datasource query builder"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def compile_requests_query
|
55
57
|
str = []
|
56
58
|
|
57
59
|
str << "controller|#{q[:controller]}|" if q[:controller].present?
|
58
60
|
str << "action|#{q[:action]}|" if q[:action].present?
|
59
61
|
str << "format|#{q[:format]}|" if q[:format].present?
|
60
62
|
str << "status|#{q[:status]}|" if q[:status].present?
|
61
|
-
|
62
63
|
str << "datetime|#{q[:on].strftime('%Y%m%d')}*|" if q[:on].present?
|
63
|
-
|
64
64
|
str << "method|#{q[:method]}|" if q[:method].present?
|
65
65
|
str << "path|#{q[:path]}|" if q[:path].present?
|
66
66
|
|
67
67
|
str.join("*")
|
68
68
|
end
|
69
69
|
|
70
|
-
def
|
71
|
-
|
70
|
+
def compile_jobs_query
|
71
|
+
str = []
|
72
|
+
|
73
|
+
str << "queue|#{q[:queue]}|" if q[:queue].present?
|
74
|
+
str << "worker|#{q[:worker]}|" if q[:worker].present?
|
75
|
+
str << "datetime|#{q[:on].strftime('%Y%m%d')}*|" if q[:on].present?
|
76
|
+
str << "status|#{q[:status]}|" if q[:status].present?
|
77
|
+
|
78
|
+
str.join("*")
|
72
79
|
end
|
73
80
|
|
74
81
|
end
|
@@ -4,10 +4,15 @@ require_relative './instrument/metrics_collector.rb'
|
|
4
4
|
|
5
5
|
module RailsPerformance
|
6
6
|
class Engine < ::Rails::Engine
|
7
|
+
isolate_namespace RailsPerformance
|
7
8
|
|
8
9
|
if RailsPerformance.try(:enabled) # for rails c
|
9
10
|
|
10
|
-
|
11
|
+
if ::Rails::VERSION::MAJOR.to_i >= 5
|
12
|
+
config.app_middleware.insert_after ActionDispatch::Executor, RailsPerformance::Rails::Middleware
|
13
|
+
else
|
14
|
+
config.app_middleware.insert_after ActionDispatch::Static, RailsPerformance::Rails::Middleware
|
15
|
+
end
|
11
16
|
|
12
17
|
initializer :configure_metrics, after: :initialize_logger do
|
13
18
|
ActiveSupport::Notifications.subscribe(
|
@@ -19,7 +24,15 @@ module RailsPerformance
|
|
19
24
|
ActionView::LogSubscriber.send :prepend, RailsPerformance::Extensions::View
|
20
25
|
ActiveRecord::LogSubscriber.send :prepend, RailsPerformance::Extensions::Db
|
21
26
|
end
|
27
|
+
end
|
22
28
|
|
29
|
+
if const_defined?("Sidekiq")
|
30
|
+
require_relative './gems/sidekiq.rb'
|
31
|
+
Sidekiq.configure_server do |config|
|
32
|
+
config.server_middleware do |chain|
|
33
|
+
chain.add RailsPerformance::Gems::Sidekiq
|
34
|
+
end
|
35
|
+
end
|
23
36
|
end
|
24
37
|
|
25
38
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module RailsPerformance
|
2
|
+
module Gems
|
3
|
+
class Sidekiq
|
4
|
+
|
5
|
+
def initialize(options=nil)
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(worker, msg, queue)
|
9
|
+
now = Time.now
|
10
|
+
data = {
|
11
|
+
enqueued_ati: msg['enqueued_at'].to_i,
|
12
|
+
created_ati: msg['created_at'].to_i,
|
13
|
+
jid: msg['jid'],
|
14
|
+
queue: queue,
|
15
|
+
start_timei: now.to_i,
|
16
|
+
datetime: now.strftime(RailsPerformance::FORMAT),
|
17
|
+
worker: msg['wrapped'.freeze] || worker.class.to_s
|
18
|
+
}
|
19
|
+
begin
|
20
|
+
yield
|
21
|
+
data[:status] = "success"
|
22
|
+
rescue Exception => ex
|
23
|
+
data[:status] = "exception"
|
24
|
+
data[:message] = ex.message
|
25
|
+
ensure
|
26
|
+
# store in ms instead of seconds
|
27
|
+
data[:duration] = (Time.now - now) * 1000
|
28
|
+
#puts data
|
29
|
+
RailsPerformance::Utils.log_job_in_redis(data)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RailsPerformance
|
2
|
+
module Models
|
3
|
+
class BaseRecord
|
4
|
+
def value
|
5
|
+
@value ||= JSON.parse(@json || "{}")
|
6
|
+
end
|
7
|
+
|
8
|
+
def duration
|
9
|
+
value['duration']
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def ms(e)
|
15
|
+
if e
|
16
|
+
e.to_f.round(1).to_s + " ms"
|
17
|
+
else
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module RailsPerformance
|
2
|
+
module Models
|
3
|
+
class JobRecord < BaseRecord
|
4
|
+
attr_reader :queue, :worker, :jid, :created_ati, :enqueued_ati, :datetime, :start_timei, :status
|
5
|
+
|
6
|
+
# key = job-performance
|
7
|
+
# |queue|default
|
8
|
+
# |worker|SimpleWorker
|
9
|
+
# |jid|7d48fbf20976c224510dbc60
|
10
|
+
# |datetime|20200124T0523
|
11
|
+
# |created_ati|1583146613
|
12
|
+
# |enqueued_ati|1583146613
|
13
|
+
# |start_timei|1583146614
|
14
|
+
# |status|success|END
|
15
|
+
# value = JSON
|
16
|
+
def initialize(key, value)
|
17
|
+
@json = value
|
18
|
+
|
19
|
+
items = key.split("|")
|
20
|
+
|
21
|
+
@queue = items[2]
|
22
|
+
@worker = items[4]
|
23
|
+
@jid = items[6]
|
24
|
+
@datetime = items[8]
|
25
|
+
@created_ati = items[10]
|
26
|
+
@enqueued_ati = items[12]
|
27
|
+
@start_timei = items[14]
|
28
|
+
@status = items[16]
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_h
|
32
|
+
{
|
33
|
+
queue: queue,
|
34
|
+
worker: worker,
|
35
|
+
jid: jid,
|
36
|
+
datetime: datetime,
|
37
|
+
created_ati: created_ati,
|
38
|
+
enqueued_ati: enqueued_ati,
|
39
|
+
start_timei: start_timei,
|
40
|
+
duration: duration,
|
41
|
+
status: status,
|
42
|
+
message: value['message']
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -1,9 +1,8 @@
|
|
1
1
|
module RailsPerformance
|
2
2
|
module Models
|
3
|
-
class Record
|
3
|
+
class Record < BaseRecord
|
4
4
|
attr_reader :controller, :action, :format, :status, :datetime, :datetimei, :method, :path, :request_id
|
5
5
|
|
6
|
-
|
7
6
|
def Record.find_by(request_id:)
|
8
7
|
keys, values = RP::Utils.fetch_from_redis("performance|*|request_id|#{request_id}|*")
|
9
8
|
|
@@ -42,10 +41,6 @@ module RailsPerformance
|
|
42
41
|
@request_id = items[18]
|
43
42
|
end
|
44
43
|
|
45
|
-
def value
|
46
|
-
@value ||= JSON.parse(@json || "{}")
|
47
|
-
end
|
48
|
-
|
49
44
|
def controller_action
|
50
45
|
"#{controller}##{action}"
|
51
46
|
end
|
@@ -61,23 +56,13 @@ module RailsPerformance
|
|
61
56
|
format: format,
|
62
57
|
method: method,
|
63
58
|
path: path,
|
64
|
-
duration: ms(
|
59
|
+
duration: ms(duration),
|
65
60
|
view_runtime: ms(value['view_runtime']),
|
66
61
|
db_runtime: ms(value['db_runtime']),
|
67
62
|
HTTP_REFERER: value['HTTP_REFERER']
|
68
63
|
}
|
69
64
|
end
|
70
65
|
|
71
|
-
private
|
72
|
-
|
73
|
-
def ms(e)
|
74
|
-
if e
|
75
|
-
e.to_f.round(1).to_s + " ms"
|
76
|
-
else
|
77
|
-
nil
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
66
|
end
|
82
67
|
end
|
83
68
|
end
|
@@ -7,25 +7,48 @@ module RailsPerformance
|
|
7
7
|
@sort ||= :datetime
|
8
8
|
end
|
9
9
|
|
10
|
-
def data
|
10
|
+
def data(type = :requests) # most popular type
|
11
11
|
db.data.collect do |record|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
path: record.path,
|
19
|
-
request_id: record.request_id,
|
20
|
-
datetime: Time.at(record.datetimei.to_i),
|
21
|
-
duration: record.value['duration'],
|
22
|
-
db_runtime: record.value['db_runtime'],
|
23
|
-
view_runtime: record.value['view_runtime'],
|
24
|
-
}
|
12
|
+
case type
|
13
|
+
when :requests
|
14
|
+
record_hash(record)
|
15
|
+
when :jobs
|
16
|
+
job_hash(record)
|
17
|
+
end
|
25
18
|
end
|
26
19
|
.select{|e| e if e[:datetime] >= TIME_WINDOW.ago}
|
27
20
|
.sort{|a, b| b[sort] <=> a[sort]}
|
28
21
|
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def record_hash(record)
|
26
|
+
{
|
27
|
+
controller: record.controller,
|
28
|
+
action: record.action,
|
29
|
+
format: record.format,
|
30
|
+
status: record.status,
|
31
|
+
method: record.method,
|
32
|
+
path: record.path,
|
33
|
+
request_id: record.request_id,
|
34
|
+
datetime: Time.at(record.datetimei.to_i),
|
35
|
+
duration: record.value['duration'],
|
36
|
+
db_runtime: record.value['db_runtime'],
|
37
|
+
view_runtime: record.value['view_runtime'],
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def job_hash(record)
|
42
|
+
{
|
43
|
+
worker: record.worker,
|
44
|
+
queue: record.queue,
|
45
|
+
|
46
|
+
jid: record.jid,
|
47
|
+
status: record.status,
|
48
|
+
datetime: Time.at(record.start_timei.to_i),
|
49
|
+
duration: record.value['duration'],
|
50
|
+
}
|
51
|
+
end
|
29
52
|
end
|
30
53
|
|
31
54
|
|
@@ -12,28 +12,21 @@ module RailsPerformance
|
|
12
12
|
now.strftime("%H:%M")
|
13
13
|
end
|
14
14
|
|
15
|
+
def Utils.log_job_in_redis(e)
|
16
|
+
key = "jobs|queue|#{e[:queue]}|worker|#{e[:worker]}|jid|#{e[:jid]}|datetime|#{e[:datetime]}|created_ati|#{e[:created_ati]}|enqueued_ati|#{e[:enqueued_ati]}|start_timei|#{e[:start_timei]}|status|#{e[:status]}|END"
|
17
|
+
value = { message: e[:message], duration: e[:duration] }
|
18
|
+
Utils.save_to_redis(key, value)
|
19
|
+
end
|
20
|
+
|
15
21
|
def Utils.log_request_in_redis(e)
|
16
22
|
value = e.slice(:view_runtime, :db_runtime, :duration, :HTTP_REFERER)
|
17
23
|
key = "performance|controller|#{e[:controller]}|action|#{e[:action]}|format|#{e[:format]}|status|#{e[:status]}|datetime|#{e[:datetime]}|datetimei|#{e[:datetimei]}|method|#{e[:method]}|path|#{e[:path]}|request_id|#{e[:request_id]}|END"
|
18
|
-
|
19
|
-
# puts " [SAVE] key ---> #{key}\n"
|
20
|
-
# puts " value ---> #{value.to_json}\n\n"
|
21
|
-
|
22
|
-
RP.redis.set(key, value.to_json)
|
23
|
-
RP.redis.expire(key, RP.duration.to_i)
|
24
|
-
|
25
|
-
true
|
24
|
+
Utils.save_to_redis(key, value)
|
26
25
|
end
|
27
26
|
|
28
27
|
def Utils.log_trace_in_redis(request_id, value)
|
29
28
|
key = "trace|#{request_id}"
|
30
|
-
|
31
|
-
# puts " [SAVE] key ---> #{key}\n"
|
32
|
-
# puts " value ---> #{value.to_json}\n\n"
|
33
|
-
# pp value
|
34
|
-
|
35
|
-
RP.redis.set(key, value.to_json)
|
36
|
-
RP.redis.expire(key, RailsPerformance::Reports::RecentRequestsReport::TIME_WINDOW.to_i)
|
29
|
+
Utils.save_to_redis(key, value, RailsPerformance::Reports::RecentRequestsReport::TIME_WINDOW.to_i)
|
37
30
|
end
|
38
31
|
|
39
32
|
def Utils.fetch_from_redis(query)
|
@@ -47,7 +40,7 @@ module RailsPerformance
|
|
47
40
|
end
|
48
41
|
|
49
42
|
def Utils.days
|
50
|
-
(RP.duration
|
43
|
+
(RP.duration / 1.day) + 1
|
51
44
|
end
|
52
45
|
|
53
46
|
def Utils.median(array)
|
@@ -64,5 +57,12 @@ module RailsPerformance
|
|
64
57
|
end
|
65
58
|
end
|
66
59
|
|
60
|
+
def Utils.save_to_redis(key, value, expire = RP.duration.to_i)
|
61
|
+
# puts " [SAVE] key ---> #{key}\n"
|
62
|
+
# puts " value ---> #{value.to_json}\n\n"
|
63
|
+
RP.redis.set(key, value.to_json)
|
64
|
+
RP.redis.expire(key, expire.to_i)
|
65
|
+
end
|
66
|
+
|
67
67
|
end
|
68
|
-
end
|
68
|
+
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.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Igor Kasyanchuk
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: sidekiq
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
83
97
|
description: 3rd party dependency-free solution how to monitor performance of your
|
84
98
|
Rails applications.
|
85
99
|
email:
|
@@ -101,14 +115,10 @@ files:
|
|
101
115
|
- app/assets/images/import.svg
|
102
116
|
- app/assets/images/menu.svg
|
103
117
|
- app/assets/images/stat.svg
|
104
|
-
- app/controllers/base_controller.rb
|
105
|
-
- app/controllers/rails_performance_controller.rb
|
106
|
-
- app/helpers/
|
118
|
+
- app/controllers/rails_performance/base_controller.rb
|
119
|
+
- app/controllers/rails_performance/rails_performance_controller.rb
|
120
|
+
- app/helpers/rails_performance/application_helper.rb
|
107
121
|
- app/views/rails_performance/_panel.html.erb
|
108
|
-
- app/views/rails_performance/_summary.html.erb
|
109
|
-
- app/views/rails_performance/_trace.html.erb
|
110
|
-
- app/views/rails_performance/crashes.html.erb
|
111
|
-
- app/views/rails_performance/index.html.erb
|
112
122
|
- app/views/rails_performance/javascripts/_javascripts.html.erb
|
113
123
|
- app/views/rails_performance/javascripts/app.js
|
114
124
|
- app/views/rails_performance/javascripts/jquery-3.4.1.min.js
|
@@ -117,23 +127,31 @@ files:
|
|
117
127
|
- app/views/rails_performance/javascripts/stupidtable.min.js
|
118
128
|
- app/views/rails_performance/javascripts/table.js
|
119
129
|
- app/views/rails_performance/layouts/rails_performance.html.erb
|
120
|
-
- app/views/rails_performance/
|
121
|
-
- app/views/rails_performance/
|
130
|
+
- app/views/rails_performance/rails_performance/_summary.html.erb
|
131
|
+
- app/views/rails_performance/rails_performance/_trace.html.erb
|
132
|
+
- app/views/rails_performance/rails_performance/crashes.html.erb
|
133
|
+
- app/views/rails_performance/rails_performance/index.html.erb
|
134
|
+
- app/views/rails_performance/rails_performance/jobs.html.erb
|
135
|
+
- app/views/rails_performance/rails_performance/recent.html.erb
|
136
|
+
- app/views/rails_performance/rails_performance/requests.html.erb
|
137
|
+
- app/views/rails_performance/rails_performance/summary.js.erb
|
138
|
+
- app/views/rails_performance/rails_performance/trace.js.erb
|
122
139
|
- app/views/rails_performance/shared/_header.html.erb
|
123
140
|
- app/views/rails_performance/stylesheets/_stylesheets.html.erb
|
124
141
|
- app/views/rails_performance/stylesheets/bulma.min.css
|
125
142
|
- app/views/rails_performance/stylesheets/panel.css
|
126
143
|
- app/views/rails_performance/stylesheets/style.css
|
127
|
-
- app/views/rails_performance/summary.js.erb
|
128
|
-
- app/views/rails_performance/trace.js.erb
|
129
144
|
- config/routes.rb
|
130
145
|
- lib/rails_performance.rb
|
131
146
|
- lib/rails_performance/data_source.rb
|
132
147
|
- lib/rails_performance/engine.rb
|
133
148
|
- lib/rails_performance/extensions/capture_everything.rb
|
149
|
+
- lib/rails_performance/gems/sidekiq.rb
|
134
150
|
- lib/rails_performance/instrument/metrics_collector.rb
|
151
|
+
- lib/rails_performance/models/base_record.rb
|
135
152
|
- lib/rails_performance/models/collection.rb
|
136
153
|
- lib/rails_performance/models/current_request.rb
|
154
|
+
- lib/rails_performance/models/job_record.rb
|
137
155
|
- lib/rails_performance/models/record.rb
|
138
156
|
- lib/rails_performance/rails/middleware.rb
|
139
157
|
- lib/rails_performance/rails/query_builder.rb
|
@@ -166,7 +184,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
166
184
|
- !ruby/object:Gem::Version
|
167
185
|
version: '0'
|
168
186
|
requirements: []
|
169
|
-
rubygems_version: 3.0.
|
187
|
+
rubygems_version: 3.0.3
|
170
188
|
signing_key:
|
171
189
|
specification_version: 4
|
172
190
|
summary: Simple Rails Performance tracker. Alternative to the NewRelic, Datadog or
|
@@ -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,74 +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)
|
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)
|
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}))
|
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)
|
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)
|
62
|
-
db = @datasource.db
|
63
|
-
@report = RP::Reports::RecentRequestsReport.new(db)
|
64
|
-
@data = @report.data
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
|
69
|
-
def prepare_query(query = params)
|
70
|
-
RP::Rails::QueryBuilder.compose_from(query)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
end
|
@@ -1,112 +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
|
-
end
|
111
|
-
end
|
112
|
-
end
|