rails_performance 1.4.2 → 1.5.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.
- checksums.yaml +4 -4
- data/README.md +40 -4
- data/app/assets/images/details.svg +3 -0
- data/app/assets/images/external.svg +1 -0
- data/app/controllers/rails_performance/base_controller.rb +4 -0
- data/app/views/rails_performance/javascripts/app.js +4 -1
- data/app/views/rails_performance/layouts/rails_performance.html.erb +3 -0
- data/app/views/rails_performance/rails_performance/_recent_row.html.erb +14 -2
- data/app/views/rails_performance/rails_performance/_summary.html.erb +9 -2
- data/app/views/rails_performance/rails_performance/crashes.html.erb +16 -2
- data/app/views/rails_performance/rails_performance/requests.html.erb +8 -1
- data/app/views/rails_performance/rails_performance/resources.html.erb +16 -43
- data/app/views/rails_performance/stylesheets/panel.css +14 -2
- data/app/views/rails_performance/stylesheets/style.css +40 -1
- data/lib/generators/rails_performance/install/templates/initializer.rb +3 -0
- data/lib/rails_performance/data_source.rb +1 -1
- data/lib/rails_performance/engine.rb +3 -3
- data/lib/rails_performance/events/record.rb +62 -0
- data/lib/rails_performance/gems/custom_ext.rb +1 -0
- data/lib/rails_performance/gems/delayed_job_ext.rb +1 -0
- data/lib/rails_performance/gems/grape_ext.rb +1 -0
- data/lib/rails_performance/gems/rake_ext.rb +1 -0
- data/lib/rails_performance/gems/sidekiq_ext.rb +1 -0
- data/lib/rails_performance/interface.rb +7 -0
- data/lib/rails_performance/models/resource_record.rb +3 -6
- data/lib/rails_performance/reports/annotations_report.rb +13 -0
- data/lib/rails_performance/reports/base_report.rb +2 -2
- data/lib/rails_performance/reports/resources_report.rb +22 -20
- data/lib/rails_performance/system_monitor/resource_chart.rb +105 -0
- data/lib/rails_performance/{extensions → system_monitor}/resources_monitor.rb +15 -36
- data/lib/rails_performance/utils.rb +6 -0
- data/lib/rails_performance/version.rb +2 -1
- data/lib/rails_performance.rb +15 -0
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 10d13bdf7ddb45da1783c3665a64a89730639a3b13061c496e4502a59158c329
|
4
|
+
data.tar.gz: 738313bc41c6af0d1cfee519679519639acda8781dd4dd74602e3c55a6b1f6de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33dcfe7183c335961e7716acc301b7c38877c9409c911ff0b6dfa0f5046ed40fc3f36fd9994cb8053367e467cb8c427eb042c218731cf7ada935fc6857c70b8d
|
7
|
+
data.tar.gz: e5e07d09bac167200fda9ff4e5b8d1bcd2e9558e4bc4a7bc9bf9c78d15f76d829f049c83793e7ef89c057bd83eda2ee2613ef6a01265a92aa6834dd891721047
|
data/README.md
CHANGED
@@ -34,6 +34,7 @@ It allows you to track:
|
|
34
34
|
- total duration of time spent per request, views rendering, DB
|
35
35
|
- SQL queries, rendering logs in "Recent Requests" section
|
36
36
|
- simple 500-crashes reports
|
37
|
+
- deployment events (or custom events)
|
37
38
|
- Sidekiq jobs
|
38
39
|
- Delayed Job jobs
|
39
40
|
- Grape API inside Rails app
|
@@ -45,7 +46,7 @@ All data are stored in `local` Redis and not sent to any 3rd party servers.
|
|
45
46
|
|
46
47
|
## Production
|
47
48
|
|
48
|
-
Gem is production-ready. At least
|
49
|
+
Gem is production-ready. At least in my 2 applications with ~800 unique users per day it works perfectly.
|
49
50
|
|
50
51
|
Just don't forget to protect performance dashboard with http basic auth or check of current_user.
|
51
52
|
|
@@ -68,7 +69,7 @@ RailsPerformance.setup do |config|
|
|
68
69
|
config.redis = Redis.new(url: ENV["REDIS_URL"].presence || "redis://127.0.0.1:6379/0") # or Redis::Namespace.new("rails-performance", redis: Redis.new), see below in README
|
69
70
|
config.duration = 4.hours
|
70
71
|
|
71
|
-
config.debug = false # currently not used
|
72
|
+
config.debug = false # currently not used
|
72
73
|
config.enabled = true
|
73
74
|
|
74
75
|
# configure Recent tab (time window and limit of requests)
|
@@ -172,6 +173,7 @@ After installation and configuration, start your Rails application, make a few r
|
|
172
173
|
|
173
174
|
If you, for whatever reason (company policy, devise, ...) need to mount RailsPerformance yourself, feel free to do so by using the following snippet as inspiration.
|
174
175
|
You can skip the `mount_at` and `http_basic_authentication_*` configurations then, if you like.
|
176
|
+
Under certain constraints (i.e. subdomains) it may be necessary to set `url_options` in the config so that RailsPeformance can generate links correctly.
|
175
177
|
|
176
178
|
```ruby
|
177
179
|
# config/routes.rb
|
@@ -223,10 +225,10 @@ env:
|
|
223
225
|
RAILS_PERFORMANCE_SERVER_ID: "server"
|
224
226
|
```
|
225
227
|
|
226
|
-
You can also
|
228
|
+
You can also specify custom "context" and "role" for monitoring, by changing the env variables:
|
227
229
|
|
228
230
|
```ruby
|
229
|
-
RailsPerformance::
|
231
|
+
RailsPerformance::SystemMonitor::ResourcesMonitor.new(
|
230
232
|
ENV["RAILS_PERFORMANCE_SERVER_CONTEXT"].presence || "rails",
|
231
233
|
ENV["RAILS_PERFORMANCE_SERVER_ROLE"].presence || "web"
|
232
234
|
)
|
@@ -236,6 +238,31 @@ More information here: `lib/rails_performance/engine.rb`.
|
|
236
238
|
|
237
239
|
PS: right now it can only distinguish between web app servers and the sidekiq servers.
|
238
240
|
|
241
|
+
#### Deployment Events + Custom Events on the Charts
|
242
|
+
|
243
|
+

|
244
|
+
|
245
|
+
#### For Kamal
|
246
|
+
|
247
|
+
- edit `.kamal/hooks/post-deploy` (rename .sample if needed)
|
248
|
+
- add `kamal app exec -p './bin/rails runner "RailsPerformance.create_event(name: \"Deploy\")"'`
|
249
|
+
- kamal deploy
|
250
|
+
|
251
|
+
#### Custom Events on the Charts
|
252
|
+
|
253
|
+
You can specify colors, orientation for the event label.
|
254
|
+
|
255
|
+
```ruby
|
256
|
+
RailsPerformance.create_event(name: "Deploy", options: {
|
257
|
+
borderColor: "#00E396",
|
258
|
+
label: {
|
259
|
+
borderColor: "#00E396",
|
260
|
+
orientation: "horizontal",
|
261
|
+
text: "Deploy"
|
262
|
+
}
|
263
|
+
})
|
264
|
+
```
|
265
|
+
|
239
266
|
### Custom events
|
240
267
|
|
241
268
|
```ruby
|
@@ -323,6 +350,15 @@ The idea of this gem grew from curiosity how many RPM my app receiving per day.
|
|
323
350
|
- sinatra?
|
324
351
|
- tests to check what is actually stored in redis db after request
|
325
352
|
- upgrade bulma
|
353
|
+
- optimize svg icons, remove inline svg (switch to base64 or assets)
|
354
|
+
|
355
|
+
## Development
|
356
|
+
|
357
|
+
1. Clone the repo
|
358
|
+
2. Run `bundle install`
|
359
|
+
3. Setup dummy app `cd test/dummy && bundle install && rails db:create && rails db:migrate`
|
360
|
+
4. Run `rails s` in the root folder
|
361
|
+
5. Run `rails test` to run tests
|
326
362
|
|
327
363
|
## Contributing
|
328
364
|
|
@@ -0,0 +1,3 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
2
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607ZM10.5 7.5v6m3-3h-6" />
|
3
|
+
</svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<?xml version="1.0"?><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g><path d="M0 0h24v24H0z" fill="none"/><path d="M10 6v2H5v11h11v-5h2v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h6zm11-3v8h-2V6.413l-7.793 7.794-1.414-1.414L17.585 5H13V3h8z"/></g></svg>
|
@@ -6,6 +6,7 @@ function showChart(element_id, type, title, options) {
|
|
6
6
|
chart: {
|
7
7
|
type: type,
|
8
8
|
height: 300,
|
9
|
+
width: '100%',
|
9
10
|
zoom: {
|
10
11
|
type: 'x',
|
11
12
|
},
|
@@ -78,7 +79,8 @@ function showTIRChart(element_id, data, addon, name) {
|
|
78
79
|
series: [{
|
79
80
|
name: name,
|
80
81
|
data: data
|
81
|
-
}]
|
82
|
+
}],
|
83
|
+
annotations: window?.annotationsData || {}
|
82
84
|
});
|
83
85
|
}
|
84
86
|
|
@@ -89,6 +91,7 @@ function showRTChart(element_id, data) {
|
|
89
91
|
name: 'Response Time',
|
90
92
|
data: data,
|
91
93
|
}],
|
94
|
+
annotations: window?.annotationsData || {}
|
92
95
|
});
|
93
96
|
}
|
94
97
|
|
@@ -8,6 +8,9 @@
|
|
8
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
|
+
<script>
|
12
|
+
window.annotationsData = <%= raw RailsPerformance::Reports::AnnotationsReport.new.data.to_json %>;
|
13
|
+
</script>
|
11
14
|
</head>
|
12
15
|
|
13
16
|
<body class="has-sticky-footer">
|
@@ -9,12 +9,24 @@
|
|
9
9
|
<td><%= format_datetime e[:datetime] %></td>
|
10
10
|
<td>
|
11
11
|
<% controller_action_info = e[:controller] + '#' + e[:action]%>
|
12
|
-
<%= link_to
|
12
|
+
<%= link_to rails_performance.rails_performance_summary_path({controller_eq: e[:controller], action_eq: e[:action]}), remote: true, title: controller_action_info do %>
|
13
|
+
<span class="details_icon">
|
14
|
+
<%= icon 'details' %>
|
15
|
+
</span>
|
16
|
+
<%= truncate(controller_action_info, length: 40) %>
|
17
|
+
<% end %>
|
13
18
|
</td>
|
14
19
|
<td><%= e[:method] %></td>
|
15
20
|
<td><%= e[:format] %></td>
|
16
21
|
<td><%= bot_icon e["user_agent"] %></td>
|
17
|
-
<td
|
22
|
+
<td>
|
23
|
+
<span class="with_external_icon">
|
24
|
+
<%= link_to_path(e) %>
|
25
|
+
<span class="icon external_icon">
|
26
|
+
<%= icon('external') %>
|
27
|
+
</span>
|
28
|
+
</span>
|
29
|
+
</td>
|
18
30
|
<td><%= status_tag e[:status] %></td>
|
19
31
|
<td class="nowrap"><%= ms e[:duration] %></td>
|
20
32
|
<td class="nowrap"><%= ms e[:view_runtime] %></td>
|
@@ -5,7 +5,7 @@
|
|
5
5
|
<div id="response_time_report_chart_mini" class="chart_mini"></div>
|
6
6
|
|
7
7
|
<h2 class="subtitle"><%= title %></h2>
|
8
|
-
<table class="table is-fullwidth is-hoverable is-narrow is-size-
|
8
|
+
<table class="table is-fullwidth is-hoverable is-narrow is-size-8">
|
9
9
|
<thead>
|
10
10
|
<tr>
|
11
11
|
<th data-sort="string">Datetime</th>
|
@@ -25,7 +25,14 @@
|
|
25
25
|
<td><%= format_datetime e[:datetime] %></td>
|
26
26
|
<td><%= e[:method] %></td>
|
27
27
|
<td><%= bot_icon e["user_agent"] %></td>
|
28
|
-
<td
|
28
|
+
<td>
|
29
|
+
<span class="with_external_icon">
|
30
|
+
<%= link_to_path(e) %>
|
31
|
+
<span class="icon external_icon">
|
32
|
+
<%= icon('external') %>
|
33
|
+
</span>
|
34
|
+
</span>
|
35
|
+
</td>
|
29
36
|
<td><%= e[:format] %></td>
|
30
37
|
<td><%= status_tag e[:status] %></td>
|
31
38
|
<td class="nowrap"><%= ms e[:duration] %></td>
|
@@ -35,11 +35,25 @@
|
|
35
35
|
<% @data.each do |e| %>
|
36
36
|
<tr>
|
37
37
|
<td><%= format_datetime e[:datetime] %></td>
|
38
|
-
<td
|
38
|
+
<td>
|
39
|
+
<%= link_to rails_performance.rails_performance_summary_path({controller_eq: e[:controller], action_eq: e[:action]}), remote: true do %>
|
40
|
+
<span class="details_icon">
|
41
|
+
<%= icon 'details' %>
|
42
|
+
</span>
|
43
|
+
<%= e[:controller] + '#' + e[:action] %>
|
44
|
+
<% end %>
|
45
|
+
</td>
|
39
46
|
<td><%= e[:method] %></td>
|
40
47
|
<td><%= e[:format] %></td>
|
41
48
|
<td><%= bot_icon e["user_agent"] %></td>
|
42
|
-
<td
|
49
|
+
<td>
|
50
|
+
<span class="with_external_icon">
|
51
|
+
<%= link_to_path(e) %>
|
52
|
+
<span class="icon external_icon">
|
53
|
+
<%= icon('external') %>
|
54
|
+
</span>
|
55
|
+
</span>
|
56
|
+
</td>
|
43
57
|
<td><%= e[:exception] %></td>
|
44
58
|
<td class="very-small-text">
|
45
59
|
<%= raw e[:backtrace]&.join("<br/>") %>
|
@@ -36,7 +36,14 @@
|
|
36
36
|
<% groups = e[:group].split("|") %>
|
37
37
|
<% c, a = groups[0].split("#") %>
|
38
38
|
<tr>
|
39
|
-
<td
|
39
|
+
<td>
|
40
|
+
<%= link_to rails_performance.rails_performance_summary_path({controller_eq: c, action_eq: a}), remote: true do %>
|
41
|
+
<span class="details_icon">
|
42
|
+
<%= icon 'details' %>
|
43
|
+
</span>
|
44
|
+
<%= groups[0] %>
|
45
|
+
<% end %>
|
46
|
+
</td>
|
40
47
|
<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>
|
41
48
|
<td><%= e[:count] %></td>
|
42
49
|
<td class="nowrap attention"><%= ms e[:p50_duration] %></td>
|
@@ -1,50 +1,23 @@
|
|
1
1
|
<title>System Resources</title>
|
2
2
|
|
3
|
-
<% @resources_report.
|
4
|
-
<h1 class="title mt-8 pt-8"><%=
|
5
|
-
|
6
|
-
|
7
|
-
<div class="card
|
8
|
-
<
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
<br/>
|
15
|
-
|
16
|
-
<div class="card">
|
17
|
-
<div class="card-content">
|
18
|
-
<h2 class="subtitle">Memory</h2>
|
19
|
-
<div id="memory_report_<%= server_key.parameterize %>" class="chart"></div>
|
20
|
-
<p class="content is-small">App memory usage</p>
|
21
|
-
</div>
|
22
|
-
</div>
|
23
|
-
|
24
|
-
<br/>
|
25
|
-
|
26
|
-
<div class="card">
|
27
|
-
<div class="card-content">
|
28
|
-
<h2 class="subtitle">Storage</h2>
|
29
|
-
<div id="disk_report_<%= server_key.parameterize %>" class="chart"></div>
|
30
|
-
<p class="content is-small">Available storage size (local disk size)</p>
|
3
|
+
<% @resources_report.servers.each do |server| %>
|
4
|
+
<h1 class="title mt-8 pt-8"><%= server.name %></h1>
|
5
|
+
|
6
|
+
<% server.charts.each.with_index do |chart, index| %>
|
7
|
+
<div class="card">
|
8
|
+
<div class="card-content">
|
9
|
+
<h2 class="subtitle"><%= chart.subtitle %></h2>
|
10
|
+
<div class="chart" id="<%= chart.id %>"></div>
|
11
|
+
<p class="content is-small"><%= chart.description %></p>
|
12
|
+
</div>
|
31
13
|
</div>
|
32
|
-
</div>
|
33
|
-
|
34
|
-
<br>
|
35
|
-
<% end %>
|
36
|
-
|
37
|
-
<% content_for :on_load do %>
|
38
|
-
<script>
|
39
|
-
<% @resources_report.data.keys.each do |server_key| %>
|
40
|
-
var data1 = <%= raw @resources_report.cpu[server_key].to_json %>;
|
41
|
-
showPercentageChart('cpu_report_<%= server_key.parameterize %>', data1, 'CPU');
|
42
14
|
|
43
|
-
|
44
|
-
showUsageChart('memory_report_<%= server_key.parameterize %>', data2, 'Usage', 2);
|
15
|
+
<br/>
|
45
16
|
|
46
|
-
|
47
|
-
|
17
|
+
<% content_for :on_load do %>
|
18
|
+
<script>
|
19
|
+
show<%= chart.type %>Chart('<%= chart.id %>', <%= raw chart.data.to_json %>, '<%= chart.legend %>', <%= index %>);
|
20
|
+
</script>
|
48
21
|
<% end %>
|
49
|
-
|
22
|
+
<% end %>
|
50
23
|
<% end %>
|
@@ -32,13 +32,25 @@
|
|
32
32
|
.cd-panel__container {
|
33
33
|
z-index: 100;
|
34
34
|
position: fixed;
|
35
|
-
width:
|
35
|
+
width: 1200px;
|
36
36
|
height: 100%;
|
37
37
|
top: 0;
|
38
38
|
transition: transform 0.3s 0s;
|
39
39
|
overflow: scroll;
|
40
40
|
}
|
41
41
|
|
42
|
+
@media (max-width: 1200px) {
|
43
|
+
.cd-panel__container {
|
44
|
+
width: 800px;
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
@media (max-width: 768px) {
|
49
|
+
.cd-panel__container {
|
50
|
+
width: 500px;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
42
54
|
.cd-panel--from-right .cd-panel__container {
|
43
55
|
right: 0;
|
44
56
|
transform: translate3d(100%, 0, 0);
|
@@ -57,4 +69,4 @@
|
|
57
69
|
.cd-panel__container .panel {
|
58
70
|
background: white;
|
59
71
|
min-height: 100vh;
|
60
|
-
}
|
72
|
+
}
|
@@ -62,6 +62,33 @@
|
|
62
62
|
color: red;
|
63
63
|
}
|
64
64
|
|
65
|
+
.external_icon svg {
|
66
|
+
width: 12px;
|
67
|
+
height: 12px;
|
68
|
+
color: #eee;
|
69
|
+
margin-left: -4px;
|
70
|
+
opacity: 0;
|
71
|
+
}
|
72
|
+
|
73
|
+
.details_icon {
|
74
|
+
position: relative;
|
75
|
+
width: 14px;
|
76
|
+
height: 14px;
|
77
|
+
}
|
78
|
+
|
79
|
+
.details_icon svg {
|
80
|
+
position: relative;
|
81
|
+
top: 2px;
|
82
|
+
width: 14px;
|
83
|
+
height: 14px;
|
84
|
+
}
|
85
|
+
|
86
|
+
.with_external_icon:hover {
|
87
|
+
.external_icon svg {
|
88
|
+
opacity: 1;
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
65
92
|
.user-agent-icon svg {
|
66
93
|
width: 12px;
|
67
94
|
height: 12px;
|
@@ -77,7 +104,19 @@
|
|
77
104
|
|
78
105
|
.chart_mini {
|
79
106
|
height: 200px;
|
80
|
-
width:
|
107
|
+
width: 1160px;
|
108
|
+
}
|
109
|
+
|
110
|
+
@media (max-width: 1200px) {
|
111
|
+
.chart_mini {
|
112
|
+
width: 770px;
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
@media (max-width: 768px) {
|
117
|
+
.chart_mini {
|
118
|
+
width: 400px;
|
119
|
+
}
|
81
120
|
}
|
82
121
|
|
83
122
|
.red {
|
@@ -31,6 +31,9 @@ if defined?(RailsPerformance)
|
|
31
31
|
# for example when you have `current_user`
|
32
32
|
# config.verify_access_proc = proc { |controller| controller.current_user && controller.current_user.admin? }
|
33
33
|
|
34
|
+
# Override engine url options, necessary if hosting under a unique domain
|
35
|
+
# config.url_options = {host: "sub.example.com"}
|
36
|
+
|
34
37
|
# You can ignore endpoints with Rails standard notation controller#action
|
35
38
|
# config.ignored_endpoints = ['HomeController#contact']
|
36
39
|
|
@@ -22,7 +22,7 @@ module RailsPerformance
|
|
22
22
|
|
23
23
|
def db
|
24
24
|
result = RailsPerformance::Models::Collection.new
|
25
|
-
now = RailsPerformance::Utils.
|
25
|
+
now = RailsPerformance::Utils.kind_of_now
|
26
26
|
(0..days).to_a.reverse_each do |e|
|
27
27
|
RailsPerformance::DataSource.new(q: q.merge({on: (now - e.days).to_date}), type: type).add_to(result)
|
28
28
|
end
|
@@ -2,7 +2,7 @@ require "action_view/log_subscriber"
|
|
2
2
|
require_relative "rails/middleware"
|
3
3
|
require_relative "models/collection"
|
4
4
|
require_relative "instrument/metrics_collector"
|
5
|
-
require_relative "
|
5
|
+
require_relative "system_monitor/resources_monitor"
|
6
6
|
|
7
7
|
module RailsPerformance
|
8
8
|
class Engine < ::Rails::Engine
|
@@ -16,7 +16,7 @@ module RailsPerformance
|
|
16
16
|
next if $rails_performance_running_mode == :console # rubocop:disable Style/GlobalVars
|
17
17
|
|
18
18
|
# start monitoring
|
19
|
-
RailsPerformance._resource_monitor = RailsPerformance::
|
19
|
+
RailsPerformance._resource_monitor = RailsPerformance::SystemMonitor::ResourcesMonitor.new(
|
20
20
|
ENV["RAILS_PERFORMANCE_SERVER_CONTEXT"].presence || "rails",
|
21
21
|
ENV["RAILS_PERFORMANCE_SERVER_ROLE"].presence || "web"
|
22
22
|
)
|
@@ -44,7 +44,7 @@ module RailsPerformance
|
|
44
44
|
RailsPerformance._resource_monitor.stop_monitoring
|
45
45
|
RailsPerformance._resource_monitor = nil
|
46
46
|
# start background monitoring
|
47
|
-
RailsPerformance._resource_monitor = RailsPerformance::
|
47
|
+
RailsPerformance._resource_monitor = RailsPerformance::SystemMonitor::ResourcesMonitor.new(
|
48
48
|
ENV["RAILS_PERFORMANCE_SERVER_CONTEXT"].presence || "sidekiq",
|
49
49
|
ENV["RAILS_PERFORMANCE_SERVER_ROLE"].presence || "background"
|
50
50
|
)
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module RailsPerformance
|
2
|
+
module Events
|
3
|
+
class Record
|
4
|
+
attr_reader :name, :datetimei, :options
|
5
|
+
|
6
|
+
DEFAULT_COLOR = "#FF00FF"
|
7
|
+
DEFAULT_LABEL_COLOR = "#FF00FF"
|
8
|
+
DEFAULT_LABEL_ORIENTATION = "horizontal"
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def create(name:, datetimei: Time.now.to_i, options: {})
|
12
|
+
instance = new(name: name, datetimei: datetimei, options: options)
|
13
|
+
instance.save
|
14
|
+
instance
|
15
|
+
end
|
16
|
+
|
17
|
+
def all
|
18
|
+
_, values = RailsPerformance::Utils.fetch_from_redis("rails_performance:records:events:*")
|
19
|
+
Array(values).map do |value|
|
20
|
+
json = JSON.parse(value)
|
21
|
+
new(name: json["name"], datetimei: json["datetimei"], options: Hash(json["options"]))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(name:, datetimei:, options: {})
|
27
|
+
@name = name
|
28
|
+
@datetimei = datetimei
|
29
|
+
@options = options
|
30
|
+
end
|
31
|
+
|
32
|
+
def save
|
33
|
+
RailsPerformance::Utils.save_to_redis(rails_performance_key, value)
|
34
|
+
end
|
35
|
+
|
36
|
+
def rails_performance_key
|
37
|
+
"rails_performance:records:events:#{datetimei}|#{RailsPerformance::EVENTS_SCHEMA}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def value
|
41
|
+
{
|
42
|
+
name: name,
|
43
|
+
datetime: RailsPerformance::Utils.from_datetimei(datetimei.to_i),
|
44
|
+
datetimei: datetimei,
|
45
|
+
options: options
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_annotation
|
50
|
+
{
|
51
|
+
x: datetimei * 1000,
|
52
|
+
borderColor: options.dig("borderColor") || DEFAULT_COLOR,
|
53
|
+
label: {
|
54
|
+
borderColor: options.dig("label", "borderColor") || DEFAULT_LABEL_COLOR,
|
55
|
+
orientation: options.dig("label", "orientation") || DEFAULT_LABEL_ORIENTATION,
|
56
|
+
text: options.dig("label", "text") || name
|
57
|
+
}
|
58
|
+
}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -26,16 +26,13 @@ module RailsPerformance
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def record_hash
|
29
|
-
{
|
29
|
+
value.symbolize_keys.merge({
|
30
30
|
server: server,
|
31
31
|
role: role,
|
32
32
|
context: context,
|
33
33
|
datetime: datetime,
|
34
|
-
datetimei: RailsPerformance::Utils.from_datetimei(datetimei.to_i)
|
35
|
-
|
36
|
-
memory: value["memory"],
|
37
|
-
disk: value["disk"]
|
38
|
-
}
|
34
|
+
datetimei: RailsPerformance::Utils.from_datetimei(datetimei.to_i)
|
35
|
+
})
|
39
36
|
end
|
40
37
|
|
41
38
|
def save
|
@@ -32,7 +32,7 @@ module RailsPerformance
|
|
32
32
|
|
33
33
|
# TODO: simplify this method, and combine with nullify_data
|
34
34
|
def calculate_data
|
35
|
-
now = RailsPerformance::Utils.
|
35
|
+
now = RailsPerformance::Utils.kind_of_now
|
36
36
|
now = now.change(sec: 0, usec: 0)
|
37
37
|
stop = now # Time.at(60 * (now.to_i / 60), in:)
|
38
38
|
offset = 0 # RailsPerformance::Reports::BaseReport.time_in_app_time_zone(now).utc_offset
|
@@ -66,7 +66,7 @@ module RailsPerformance
|
|
66
66
|
def nil_data(duration = RailsPerformance.duration)
|
67
67
|
@nil_data ||= begin
|
68
68
|
result = {}
|
69
|
-
now = RailsPerformance::Utils.
|
69
|
+
now = RailsPerformance::Utils.kind_of_now
|
70
70
|
now = now.change(sec: 0, usec: 0)
|
71
71
|
stop = now # Time.at(60 * (now.to_i / 60))
|
72
72
|
offset = 0 # RailsPerformance::Reports::BaseReport.time_in_app_time_zone(now).utc_offset
|
@@ -1,40 +1,42 @@
|
|
1
1
|
module RailsPerformance
|
2
2
|
module Reports
|
3
3
|
class ResourcesReport < BaseReport
|
4
|
-
|
5
|
-
|
6
|
-
.
|
7
|
-
|
8
|
-
# .transform_values { |v| v.sort { |a, b| b[sort] <=> a[sort] } }
|
9
|
-
.transform_values { |v| v.map { |e| e.merge({datetimei: e[:datetimei].to_i}) } }
|
10
|
-
end
|
4
|
+
Server = Struct.new(:report, :key) do
|
5
|
+
def name
|
6
|
+
key.split("///").join(", ")
|
7
|
+
end
|
11
8
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end)
|
9
|
+
def charts
|
10
|
+
RailsPerformance.system_monitors.map do |class_name|
|
11
|
+
SystemMonitor.const_get(class_name).new(self)
|
12
|
+
end
|
17
13
|
end
|
18
14
|
end
|
19
15
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
res[e[:datetimei] * 1000] = e[:memory].to_f.round(2)
|
24
|
-
end)
|
16
|
+
def servers
|
17
|
+
data.keys.map do |key|
|
18
|
+
Server.new(self, key)
|
25
19
|
end
|
26
20
|
end
|
27
21
|
|
28
|
-
def
|
29
|
-
|
22
|
+
def extract_signal &block
|
23
|
+
data.transform_values do |v|
|
30
24
|
prepare_report(v.each_with_object({}) do |e, res|
|
31
|
-
res[e[:datetimei] * 1000] =
|
25
|
+
res[e[:datetimei] * 1000] = block.call(e)
|
32
26
|
end)
|
33
27
|
end
|
34
28
|
end
|
35
29
|
|
36
30
|
private
|
37
31
|
|
32
|
+
def data
|
33
|
+
@data ||= db.data
|
34
|
+
.collect { |e| e.record_hash }
|
35
|
+
.group_by { |e| e[:server] + "///" + e[:context] + "///" + e[:role] }
|
36
|
+
# .transform_values { |v| v.sort { |a, b| b[sort] <=> a[sort] } }
|
37
|
+
.transform_values { |v| v.map { |e| e.merge({datetimei: e[:datetimei].to_i}) } }
|
38
|
+
end
|
39
|
+
|
38
40
|
def prepare_report(input)
|
39
41
|
nullify_data(input, RailsPerformance.system_monitor_duration)
|
40
42
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module RailsPerformance
|
2
|
+
module SystemMonitor
|
3
|
+
ResourceChart = Struct.new(:server, :key, :type, :subtitle, :description, :legend, keyword_init: true) do
|
4
|
+
def id
|
5
|
+
[key, "report", server.key.parameterize].join("_")
|
6
|
+
end
|
7
|
+
|
8
|
+
def data
|
9
|
+
all_data = server.report.extract_signal { |e| signal(e) }
|
10
|
+
all_data[server.key]
|
11
|
+
end
|
12
|
+
|
13
|
+
def signal e
|
14
|
+
format(e[key])
|
15
|
+
end
|
16
|
+
|
17
|
+
def format measurement
|
18
|
+
measurement
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class CPULoad < ResourceChart
|
23
|
+
def initialize server
|
24
|
+
super(
|
25
|
+
server:,
|
26
|
+
key: :cpu,
|
27
|
+
type: "Percentage",
|
28
|
+
subtitle: "CPU",
|
29
|
+
description: "CPU load average (1 min), average per 1 minute",
|
30
|
+
legend: "CPU",
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def format measurement
|
35
|
+
measurement["one_min"].to_f.round(2)
|
36
|
+
end
|
37
|
+
|
38
|
+
def measure
|
39
|
+
load_averages = Sys::CPU.load_avg
|
40
|
+
{
|
41
|
+
one_min: load_averages[0],
|
42
|
+
five_min: load_averages[1],
|
43
|
+
fifteen_min: load_averages[2]
|
44
|
+
}
|
45
|
+
rescue => e
|
46
|
+
::Rails.logger.error "Error fetching CPU usage: #{e.message}"
|
47
|
+
{one_min: 0.0, five_min: 0.0, fifteen_min: 0.0}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class MemoryUsage < ResourceChart
|
52
|
+
def initialize server
|
53
|
+
super(
|
54
|
+
server:,
|
55
|
+
key: :memory,
|
56
|
+
type: "Usage",
|
57
|
+
subtitle: "Memory",
|
58
|
+
description: "App memory usage",
|
59
|
+
legend: "Usage",
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def format measurement
|
64
|
+
measurement.to_f.round(2)
|
65
|
+
end
|
66
|
+
|
67
|
+
def measure
|
68
|
+
GetProcessMem.new.bytes
|
69
|
+
rescue => e
|
70
|
+
::Rails.logger.error "Error fetching memory usage: #{e.message}"
|
71
|
+
0
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class DiskUsage < ResourceChart
|
76
|
+
def initialize server
|
77
|
+
super(
|
78
|
+
server:,
|
79
|
+
key: :disk,
|
80
|
+
type: "Usage",
|
81
|
+
subtitle: "Storage",
|
82
|
+
description: "Available storage size (local disk size)",
|
83
|
+
legend: "Usage",
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
def signal measurement
|
88
|
+
measurement["available"].to_f.round(2)
|
89
|
+
end
|
90
|
+
|
91
|
+
def measure
|
92
|
+
path = "/"
|
93
|
+
stat = Sys::Filesystem.stat(path)
|
94
|
+
{
|
95
|
+
available: stat.blocks_available * stat.block_size,
|
96
|
+
total: stat.blocks * stat.block_size,
|
97
|
+
used: (stat.blocks - stat.blocks_available) * stat.block_size
|
98
|
+
}
|
99
|
+
rescue => e
|
100
|
+
::Rails.logger.error "Error fetching disk space: #{e.message}"
|
101
|
+
{available: 0, total: 0, used: 0}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -1,6 +1,8 @@
|
|
1
|
+
require "rails_performance/system_monitor/resource_chart"
|
2
|
+
|
1
3
|
module RailsPerformance
|
2
|
-
module
|
3
|
-
class
|
4
|
+
module SystemMonitor
|
5
|
+
class ResourcesMonitor
|
4
6
|
attr_reader :context, :role
|
5
7
|
|
6
8
|
def initialize(context, role)
|
@@ -39,49 +41,26 @@ module RailsPerformance
|
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
store_data({cpu: cpu, memory: memory, disk: disk})
|
48
|
-
end
|
49
|
-
|
50
|
-
def fetch_process_cpu_usage
|
51
|
-
load_averages = Sys::CPU.load_avg
|
52
|
-
{
|
53
|
-
one_min: load_averages[0],
|
54
|
-
five_min: load_averages[1],
|
55
|
-
fifteen_min: load_averages[2]
|
56
|
-
}
|
57
|
-
rescue => e
|
58
|
-
::Rails.logger.error "Error fetching CPU usage: #{e.message}"
|
59
|
-
{one_min: 0.0, five_min: 0.0, fifteen_min: 0.0}
|
44
|
+
def payload
|
45
|
+
monitors.reduce({}) do |data, monitor|
|
46
|
+
data.merge(monitor.key => monitor.measure)
|
47
|
+
end
|
60
48
|
end
|
61
49
|
|
62
|
-
def
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
0
|
50
|
+
def monitors
|
51
|
+
@monitors ||= RailsPerformance.system_monitors.map do |class_name|
|
52
|
+
RailsPerformance::SystemMonitor.const_get(class_name).new(nil)
|
53
|
+
end
|
67
54
|
end
|
68
55
|
|
69
|
-
def
|
70
|
-
|
71
|
-
{
|
72
|
-
available: stat.blocks_available * stat.block_size,
|
73
|
-
total: stat.blocks * stat.block_size,
|
74
|
-
used: (stat.blocks - stat.blocks_available) * stat.block_size
|
75
|
-
}
|
76
|
-
rescue => e
|
77
|
-
::Rails.logger.error "Error fetching disk space: #{e.message}"
|
78
|
-
{available: 0, total: 0, used: 0}
|
56
|
+
def run
|
57
|
+
store_data(payload)
|
79
58
|
end
|
80
59
|
|
81
60
|
def store_data(data)
|
82
61
|
::Rails.logger.info("Server: #{server_id}, Context: #{context}, Role: #{role}, data: #{data}")
|
83
62
|
|
84
|
-
now = RailsPerformance::Utils.
|
63
|
+
now = RailsPerformance::Utils.kind_of_now
|
85
64
|
now = now.change(sec: 0, usec: 0)
|
86
65
|
RailsPerformance::Models::ResourceRecord.new(
|
87
66
|
server: server_id,
|
@@ -1,9 +1,15 @@
|
|
1
1
|
module RailsPerformance
|
2
2
|
class Utils
|
3
|
+
DEFAULT_TIME_OFFSET = 1.minute
|
4
|
+
|
3
5
|
def self.time
|
4
6
|
Time.now.utc
|
5
7
|
end
|
6
8
|
|
9
|
+
def self.kind_of_now
|
10
|
+
time + DEFAULT_TIME_OFFSET
|
11
|
+
end
|
12
|
+
|
7
13
|
def self.from_datetimei(datetimei)
|
8
14
|
Time.at(datetimei, in: "+00:00")
|
9
15
|
end
|
data/lib/rails_performance.rb
CHANGED
@@ -26,10 +26,15 @@ require_relative "rails_performance/reports/breakdown_report"
|
|
26
26
|
require_relative "rails_performance/reports/trace_report"
|
27
27
|
require_relative "rails_performance/reports/percentile_report"
|
28
28
|
require_relative "rails_performance/reports/resources_report"
|
29
|
+
require_relative "rails_performance/reports/annotations_report"
|
30
|
+
require_relative "rails_performance/events/record"
|
29
31
|
require_relative "rails_performance/extensions/trace"
|
30
32
|
require_relative "rails_performance/thread/current_request"
|
33
|
+
require_relative "rails_performance/interface"
|
31
34
|
|
32
35
|
module RailsPerformance
|
36
|
+
extend RailsPerformance::Interface
|
37
|
+
|
33
38
|
FORMAT = "%Y%m%dT%H%M"
|
34
39
|
|
35
40
|
mattr_accessor :redis
|
@@ -119,10 +124,20 @@ module RailsPerformance
|
|
119
124
|
mattr_accessor :ignore_trace_headers
|
120
125
|
@@ignore_trace_headers = ["datetimei"]
|
121
126
|
|
127
|
+
mattr_accessor :url_options
|
128
|
+
@@url_options = nil
|
129
|
+
|
122
130
|
# System monitor duration (expiration time)
|
123
131
|
mattr_accessor :system_monitor_duration
|
124
132
|
@@system_monitor_duration = 24.hours
|
125
133
|
|
134
|
+
mattr_accessor :system_monitors
|
135
|
+
@@system_monitors = [
|
136
|
+
"CPULoad",
|
137
|
+
"MemoryUsage",
|
138
|
+
"DiskUsage"
|
139
|
+
]
|
140
|
+
|
126
141
|
# -- internal usage --
|
127
142
|
#
|
128
143
|
#
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_performance
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Igor Kasyanchuk
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: railties
|
@@ -290,8 +290,10 @@ files:
|
|
290
290
|
- app/assets/images/activity.svg
|
291
291
|
- app/assets/images/bot.svg
|
292
292
|
- app/assets/images/close.svg
|
293
|
+
- app/assets/images/details.svg
|
293
294
|
- app/assets/images/download.svg
|
294
295
|
- app/assets/images/export.svg
|
296
|
+
- app/assets/images/external.svg
|
295
297
|
- app/assets/images/git.svg
|
296
298
|
- app/assets/images/github.svg
|
297
299
|
- app/assets/images/home.svg
|
@@ -345,7 +347,7 @@ files:
|
|
345
347
|
- lib/rails_performance.rb
|
346
348
|
- lib/rails_performance/data_source.rb
|
347
349
|
- lib/rails_performance/engine.rb
|
348
|
-
- lib/rails_performance/
|
350
|
+
- lib/rails_performance/events/record.rb
|
349
351
|
- lib/rails_performance/extensions/trace.rb
|
350
352
|
- lib/rails_performance/gems/custom_ext.rb
|
351
353
|
- lib/rails_performance/gems/delayed_job_ext.rb
|
@@ -353,6 +355,7 @@ files:
|
|
353
355
|
- lib/rails_performance/gems/rake_ext.rb
|
354
356
|
- lib/rails_performance/gems/sidekiq_ext.rb
|
355
357
|
- lib/rails_performance/instrument/metrics_collector.rb
|
358
|
+
- lib/rails_performance/interface.rb
|
356
359
|
- lib/rails_performance/models/base_record.rb
|
357
360
|
- lib/rails_performance/models/collection.rb
|
358
361
|
- lib/rails_performance/models/custom_record.rb
|
@@ -365,6 +368,7 @@ files:
|
|
365
368
|
- lib/rails_performance/models/trace_record.rb
|
366
369
|
- lib/rails_performance/rails/middleware.rb
|
367
370
|
- lib/rails_performance/rails/query_builder.rb
|
371
|
+
- lib/rails_performance/reports/annotations_report.rb
|
368
372
|
- lib/rails_performance/reports/base_report.rb
|
369
373
|
- lib/rails_performance/reports/breakdown_report.rb
|
370
374
|
- lib/rails_performance/reports/crash_report.rb
|
@@ -376,6 +380,8 @@ files:
|
|
376
380
|
- lib/rails_performance/reports/slow_requests_report.rb
|
377
381
|
- lib/rails_performance/reports/throughput_report.rb
|
378
382
|
- lib/rails_performance/reports/trace_report.rb
|
383
|
+
- lib/rails_performance/system_monitor/resource_chart.rb
|
384
|
+
- lib/rails_performance/system_monitor/resources_monitor.rb
|
379
385
|
- lib/rails_performance/thread/current_request.rb
|
380
386
|
- lib/rails_performance/utils.rb
|
381
387
|
- lib/rails_performance/version.rb
|
@@ -397,7 +403,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
397
403
|
- !ruby/object:Gem::Version
|
398
404
|
version: '0'
|
399
405
|
requirements: []
|
400
|
-
rubygems_version: 3.6.
|
406
|
+
rubygems_version: 3.6.9
|
401
407
|
specification_version: 4
|
402
408
|
summary: Simple Rails Performance tracker. Alternative to the NewRelic, Datadog or
|
403
409
|
other services.
|