rails_performance 1.4.1 → 1.4.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0a493c11a3166ec07cfc2b6f2d4956cc09b867c24d280ba97836d411df384679
4
- data.tar.gz: 700cf3ac2d08eed0fcd0a8549e8488b88e1b92b79019efc8db17c7f4a0370d6b
3
+ metadata.gz: 60fc2568e878c07db112623ef55985422852996f3eea18fd972c9ccc0288fd1a
4
+ data.tar.gz: 3ff0036deb507e91fd9ac1ab63cafac95e98aeb59646ca566dff403a2bcdc1d6
5
5
  SHA512:
6
- metadata.gz: 49eef9ff013e574007cea89b8fe288fa4d627e8a72cfc3770a4a8e291d599d99905f61eb5c103ad5c3f749158cffb4179f8fd39a40159f6ee759fee6422dc548
7
- data.tar.gz: 7d17c31b43b65a912c05f9de138959e19442412c9fe3dbecb70973c8133d6cf341ad409e1abf99f71a2511edec9664a71ed14cd39d2ac96ee113874f994818b3
6
+ metadata.gz: 28a6b7d3918eaabb97a4cabcdbc95e66cca45dba61c416f90ed083e6508df79333a0e2c209fb3d1d1654a1516587dda5387eb88a42518a4b602cb77b4c83922e
7
+ data.tar.gz: '0179ca0fdece9492e9053820aa025d0210391957d9eee2cac714bc0e8611003b467ff8d06bf91cb27d0eb8024bc4a8d77a56410aa7879a701be0d9df59ed8ee1'
data/README.md CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  [![Tests](https://github.com/igorkasyanchuk/rails_performance/actions/workflows/ruby.yml/badge.svg)](https://github.com/igorkasyanchuk/rails_performance/actions/workflows/ruby.yml)
4
4
  [![RailsJazz](https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/my_other.svg?raw=true)](https://www.railsjazz.com)
5
- [![Listed on OpenSource-Heroes.com](https://opensource-heroes.com/badge-v1.svg)](https://opensource-heroes.com/r/igorkasyanchuk/rails_performance)
5
+ [My Cheatsheets](https://cheatsheetshero.com/user/igor-kasyanchuk)
6
+
7
+ [!["Buy Me A Coffee"](https://github.com/igorkasyanchuk/get-smart/blob/main/docs/snapshot-bmc-button-small.png?raw=true)](https://buymeacoffee.com/igorkasyanchuk)
6
8
 
7
9
  A self-hosted tool to monitor the performance of your Ruby on Rails application.
8
10
 
@@ -43,7 +45,7 @@ All data are stored in `local` Redis and not sent to any 3rd party servers.
43
45
 
44
46
  ## Production
45
47
 
46
- Gem is production-ready. At least on my 2 applications with ~800 unique users per day it works perfectly.
48
+ Gem is production-ready. At least in my 2 applications with ~800 unique users per day it works perfectly.
47
49
 
48
50
  Just don't forget to protect performance dashboard with http basic auth or check of current_user.
49
51
 
@@ -66,7 +68,7 @@ RailsPerformance.setup do |config|
66
68
  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
67
69
  config.duration = 4.hours
68
70
 
69
- config.debug = false # currently not used>
71
+ config.debug = false # currently not used
70
72
  config.enabled = true
71
73
 
72
74
  # configure Recent tab (time window and limit of requests)
@@ -170,6 +172,7 @@ After installation and configuration, start your Rails application, make a few r
170
172
 
171
173
  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.
172
174
  You can skip the `mount_at` and `http_basic_authentication_*` configurations then, if you like.
175
+ Under certain constraints (i.e. subdomains) it may be necessary to set `url_options` in the config so that RailsPeformance can generate links correctly.
173
176
 
174
177
  ```ruby
175
178
  # config/routes.rb
@@ -221,10 +224,10 @@ env:
221
224
  RAILS_PERFORMANCE_SERVER_ID: "server"
222
225
  ```
223
226
 
224
- You can also specifify custom "context" and "role" for monitoring, by changing the env variables:
227
+ You can also specify custom "context" and "role" for monitoring, by changing the env variables:
225
228
 
226
229
  ```ruby
227
- RailsPerformance::Extensions::ResourceMonitor.new(
230
+ RailsPerformance::SystemMonitor::ResourcesMonitor.new(
228
231
  ENV["RAILS_PERFORMANCE_SERVER_CONTEXT"].presence || "rails",
229
232
  ENV["RAILS_PERFORMANCE_SERVER_ROLE"].presence || "web"
230
233
  )
@@ -242,7 +245,7 @@ RailsPerformance.measure("some label", "some namespace") do
242
245
  end
243
246
  ```
244
247
 
245
- ## Using with Rails Namespace
248
+ ## Using with Redis Namespace
246
249
 
247
250
  If you want to use Redis namespace (for example when you have multiple apps running on the same server), you can configure it like this:
248
251
 
@@ -321,6 +324,15 @@ The idea of this gem grew from curiosity how many RPM my app receiving per day.
321
324
  - sinatra?
322
325
  - tests to check what is actually stored in redis db after request
323
326
  - upgrade bulma
327
+ - optimize svg icons, remove inline svg (switch to base64 or assets)
328
+
329
+ ## Development
330
+
331
+ 1. Clone the repo
332
+ 2. Run `bundle install`
333
+ 3. Setup dummy app `cd test/dummy && bundle install && rails db:create && rails db:migrate`
334
+ 4. Run `rails s` in the root folder
335
+ 5. Run `rails test` to run tests
324
336
 
325
337
  ## Contributing
326
338
 
@@ -332,14 +344,11 @@ If "schema" how records are stored i Redis is changed, and this is a breaking ch
332
344
 
333
345
  https://github.com/igorkasyanchuk/rails_performance/graphs/contributors
334
346
 
335
- ## Other
336
-
337
- [<img src="https://opensource-heroes.com/svg/embed/igorkasyanchuk/rails_performance"
338
- />](https://opensource-heroes.com/r/igorkasyanchuk/rails_performance)
339
-
340
347
  ## License
341
348
 
342
349
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
343
350
 
344
351
  [<img src="https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/more_gems.png?raw=true"
345
352
  />](https://www.railsjazz.com/?utm_source=github&utm_medium=bottom&utm_campaign=rails_performance)
353
+
354
+ [!["Buy Me A Coffee"](https://github.com/igorkasyanchuk/get-smart/blob/main/docs/snapshot-bmc-button.png?raw=true)](https://buymeacoffee.com/igorkasyanchuk)
@@ -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>
@@ -4,6 +4,7 @@ module RailsPerformance
4
4
  layout "rails_performance/layouts/rails_performance"
5
5
 
6
6
  before_action :verify_access
7
+ after_action :set_permissive_csp
7
8
 
8
9
  if RailsPerformance.http_basic_authentication_enabled
9
10
  http_basic_authenticate_with \
@@ -11,11 +12,19 @@ module RailsPerformance
11
12
  password: RailsPerformance.http_basic_authentication_password
12
13
  end
13
14
 
15
+ def url_options
16
+ RailsPerformance.url_options.nil? ? super : RailsPerformance.url_options
17
+ end
18
+
14
19
  private
15
20
 
16
21
  def verify_access
17
22
  result = RailsPerformance.verify_access_proc.call(self)
18
23
  redirect_to("/", error: "Access Denied", status: 401) unless result
19
24
  end
25
+
26
+ def set_permissive_csp
27
+ response.headers["Content-Security-Policy"] = "default-src 'self' https:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https:; style-src 'self' 'unsafe-inline' https:"
28
+ end
20
29
  end
21
30
  end
@@ -23,6 +23,10 @@ module RailsPerformance
23
23
  end
24
24
 
25
25
  def extract_duration(str)
26
+ if str.is_a?(Hash)
27
+ return str[:duration].to_s if str[:duration]
28
+ end
29
+
26
30
  if str =~ /Duration: (\d+.?\d+?ms)/i
27
31
  $1
28
32
  else
@@ -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
  },
@@ -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 truncate(controller_action_info, length: 40), rails_performance.rails_performance_summary_path({controller_eq: e[:controller], action_eq: e[:action]}), remote: true, title: controller_action_info %>
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><%= link_to_path(e) %></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-9">
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><%= link_to_path(e) %></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><%= link_to e[:controller] + '#' + e[:action], rails_performance.rails_performance_summary_path({controller_eq: e[:controller], action_eq: e[:action]}), remote: true %></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><%= link_to_path(e) %></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><%= link_to groups[0], rails_performance.rails_performance_summary_path({controller_eq: c, action_eq: a}), remote: true %></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.data.keys.each do |server_key| %>
4
- <h1 class="title mt-8 pt-8"><%= server_key.split("///"). join(", ") %></h1>
5
-
6
- <div class="card">
7
- <div class="card-content">
8
- <h2 class="subtitle">CPU</h2>
9
- <div id="cpu_report_<%= server_key.parameterize %>" class="chart"></div>
10
- <p class="content is-small">CPU usage %, average per 1 minute</p>
11
- </div>
12
- </div>
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
- var data2 = <%= raw @resources_report.memory[server_key].to_json %>;
44
- showUsageChart('memory_report_<%= server_key.parameterize %>', data2, 'Usage', 2);
15
+ <br/>
45
16
 
46
- var data3 = <%= raw @resources_report.disk[server_key].to_json %>;
47
- showUsageChart('disk_report_<%= server_key.parameterize %>', data3, 'Available', 3);
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
- </script>
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: 800px;
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: 720px;
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 {
@@ -8,11 +8,11 @@ if defined?(RailsPerformance)
8
8
 
9
9
  # Recent Requests configuration
10
10
  config.recent_requests_time_window = 60.minutes
11
- # confog.recent_requests_limit = nil # numnber of recent requests
11
+ # config.recent_requests_limit = nil # number of recent requests
12
12
 
13
13
  # Slow Requests configuration
14
14
  config.slow_requests_time_window = 4.hours
15
- # config.slow_requests_limit = 500 # numnber of slow requests
15
+ # config.slow_requests_limit = 500 # number of slow requests
16
16
  config.slow_requests_threshold = 500 # ms
17
17
 
18
18
  config.debug = false # currently not used>
@@ -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
 
@@ -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 "extensions/resources_monitor"
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::Extensions::ResourceMonitor.new(
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::Extensions::ResourceMonitor.new(
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
  )
@@ -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
- cpu: value["cpu"],
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
@@ -1,40 +1,42 @@
1
1
  module RailsPerformance
2
2
  module Reports
3
3
  class ResourcesReport < BaseReport
4
- def data
5
- @data ||= db.data
6
- .collect { |e| e.record_hash }
7
- .group_by { |e| e[:server] + "///" + e[:context] + "///" + e[:role] }
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
- def cpu
13
- @cpu ||= data.transform_values do |v|
14
- prepare_report(v.each_with_object({}) do |e, res|
15
- res[e[:datetimei] * 1000] = e[:cpu]["one_min"].to_f.round(2)
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 memory
21
- @memory ||= data.transform_values do |v|
22
- prepare_report(v.each_with_object({}) do |e, res|
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 disk
29
- @disk ||= data.transform_values do |v|
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] = e[:disk]["available"].to_f.round(2)
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
@@ -10,7 +10,7 @@ module RailsPerformance
10
10
  .collect { |e| e.record_hash }
11
11
  .select { |e| e if e[sort] > RailsPerformance.slow_requests_time_window.ago.to_i }
12
12
  .sort { |a, b| b[sort] <=> a[sort] }
13
- .filter { |e| e[:duration] > RailsPerformance.slow_requests_threshold.to_i }
13
+ .filter { |e| e[:duration].to_f > RailsPerformance.slow_requests_threshold.to_i }
14
14
  .first(limit)
15
15
  end
16
16
 
@@ -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 Extensions
3
- class ResourceMonitor
4
+ module SystemMonitor
5
+ class ResourcesMonitor
4
6
  attr_reader :context, :role
5
7
 
6
8
  def initialize(context, role)
@@ -39,43 +41,20 @@ module RailsPerformance
39
41
  end
40
42
  end
41
43
 
42
- def run
43
- cpu = fetch_process_cpu_usage
44
- memory = fetch_process_memory_usage
45
- disk = fetch_disk_usage
46
-
47
- store_data({cpu:, memory:, 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 fetch_process_memory_usage
63
- GetProcessMem.new.bytes
64
- rescue => e
65
- ::Rails.logger.error "Error fetching memory usage: #{e.message}"
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 fetch_disk_usage(path = "/")
70
- stat = Sys::Filesystem.stat(path)
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)
@@ -1,4 +1,4 @@
1
1
  module RailsPerformance
2
- VERSION = "1.4.1"
2
+ VERSION = "1.4.3"
3
3
  SCHEMA = "1.0.2"
4
4
  end
@@ -119,10 +119,20 @@ module RailsPerformance
119
119
  mattr_accessor :ignore_trace_headers
120
120
  @@ignore_trace_headers = ["datetimei"]
121
121
 
122
+ mattr_accessor :url_options
123
+ @@url_options = nil
124
+
122
125
  # System monitor duration (expiration time)
123
126
  mattr_accessor :system_monitor_duration
124
127
  @@system_monitor_duration = 24.hours
125
128
 
129
+ mattr_accessor :system_monitors
130
+ @@system_monitors = [
131
+ "CPULoad",
132
+ "MemoryUsage",
133
+ "DiskUsage"
134
+ ]
135
+
126
136
  # -- internal usage --
127
137
  #
128
138
  #
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_performance
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.1
4
+ version: 1.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Kasyanchuk
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-11-27 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: railties
@@ -291,8 +290,10 @@ files:
291
290
  - app/assets/images/activity.svg
292
291
  - app/assets/images/bot.svg
293
292
  - app/assets/images/close.svg
293
+ - app/assets/images/details.svg
294
294
  - app/assets/images/download.svg
295
295
  - app/assets/images/export.svg
296
+ - app/assets/images/external.svg
296
297
  - app/assets/images/git.svg
297
298
  - app/assets/images/github.svg
298
299
  - app/assets/images/home.svg
@@ -346,7 +347,6 @@ files:
346
347
  - lib/rails_performance.rb
347
348
  - lib/rails_performance/data_source.rb
348
349
  - lib/rails_performance/engine.rb
349
- - lib/rails_performance/extensions/resources_monitor.rb
350
350
  - lib/rails_performance/extensions/trace.rb
351
351
  - lib/rails_performance/gems/custom_ext.rb
352
352
  - lib/rails_performance/gems/delayed_job_ext.rb
@@ -377,6 +377,8 @@ files:
377
377
  - lib/rails_performance/reports/slow_requests_report.rb
378
378
  - lib/rails_performance/reports/throughput_report.rb
379
379
  - lib/rails_performance/reports/trace_report.rb
380
+ - lib/rails_performance/system_monitor/resource_chart.rb
381
+ - lib/rails_performance/system_monitor/resources_monitor.rb
380
382
  - lib/rails_performance/thread/current_request.rb
381
383
  - lib/rails_performance/utils.rb
382
384
  - lib/rails_performance/version.rb
@@ -384,7 +386,6 @@ homepage: https://github.com/igorkasyanchuk/rails_performance
384
386
  licenses:
385
387
  - MIT
386
388
  metadata: {}
387
- post_install_message:
388
389
  rdoc_options: []
389
390
  require_paths:
390
391
  - lib
@@ -399,8 +400,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
399
400
  - !ruby/object:Gem::Version
400
401
  version: '0'
401
402
  requirements: []
402
- rubygems_version: 3.5.22
403
- signing_key:
403
+ rubygems_version: 3.6.9
404
404
  specification_version: 4
405
405
  summary: Simple Rails Performance tracker. Alternative to the NewRelic, Datadog or
406
406
  other services.