railswatch 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +485 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/config/railswatch_manifest.js +0 -0
  6. data/app/assets/images/activity.svg +13 -0
  7. data/app/assets/images/bot.svg +1 -0
  8. data/app/assets/images/close.svg +13 -0
  9. data/app/assets/images/details.svg +3 -0
  10. data/app/assets/images/download.svg +3 -0
  11. data/app/assets/images/export.svg +13 -0
  12. data/app/assets/images/external.svg +1 -0
  13. data/app/assets/images/git.svg +1 -0
  14. data/app/assets/images/github.svg +1 -0
  15. data/app/assets/images/home.svg +16 -0
  16. data/app/assets/images/import.svg +13 -0
  17. data/app/assets/images/menu.svg +16 -0
  18. data/app/assets/images/moon.svg +3 -0
  19. data/app/assets/images/stat.svg +1 -0
  20. data/app/assets/images/sun.svg +4 -0
  21. data/app/assets/images/user.svg +1 -0
  22. data/app/controllers/railswatch/base_controller.rb +35 -0
  23. data/app/controllers/railswatch/concerns/csv_exportable.rb +31 -0
  24. data/app/controllers/railswatch/railswatch_controller.rb +183 -0
  25. data/app/engine_assets/javascripts/apex_ext.js +30 -0
  26. data/app/engine_assets/javascripts/application.js +9 -0
  27. data/app/engine_assets/javascripts/autoupdate.js +79 -0
  28. data/app/engine_assets/javascripts/charts.js +279 -0
  29. data/app/engine_assets/javascripts/navbar.js +11 -0
  30. data/app/engine_assets/javascripts/panel.js +43 -0
  31. data/app/engine_assets/javascripts/table.js +12 -0
  32. data/app/engine_assets/javascripts/theme.js +43 -0
  33. data/app/engine_assets/stylesheets/panel.css +111 -0
  34. data/app/engine_assets/stylesheets/responsive.css +102 -0
  35. data/app/engine_assets/stylesheets/style.css +960 -0
  36. data/app/helpers/railswatch/railswatch_helper.rb +338 -0
  37. data/app/views/railswatch/_panel.html.erb +15 -0
  38. data/app/views/railswatch/layouts/railswatch.html.erb +81 -0
  39. data/app/views/railswatch/railswatch/_card.html.erb +7 -0
  40. data/app/views/railswatch/railswatch/_chart.html.erb +13 -0
  41. data/app/views/railswatch/railswatch/_crashes_table_content.html.erb +62 -0
  42. data/app/views/railswatch/railswatch/_custom_events_table_content.html.erb +27 -0
  43. data/app/views/railswatch/railswatch/_delayed_job_table_content.html.erb +52 -0
  44. data/app/views/railswatch/railswatch/_export.html.erb +4 -0
  45. data/app/views/railswatch/railswatch/_grape_requests_table_content.html.erb +31 -0
  46. data/app/views/railswatch/railswatch/_overview.html.erb +124 -0
  47. data/app/views/railswatch/railswatch/_rake_tasks_table_content.html.erb +25 -0
  48. data/app/views/railswatch/railswatch/_recent_requests_table_content.html.erb +28 -0
  49. data/app/views/railswatch/railswatch/_recent_row.html.erb +41 -0
  50. data/app/views/railswatch/railswatch/_requests_table_content.html.erb +51 -0
  51. data/app/views/railswatch/railswatch/_sidekiq_jobs_table_content.html.erb +50 -0
  52. data/app/views/railswatch/railswatch/_summary.html.erb +50 -0
  53. data/app/views/railswatch/railswatch/_table.html.erb +30 -0
  54. data/app/views/railswatch/railswatch/_trace.html.erb +78 -0
  55. data/app/views/railswatch/railswatch/crashes.html.erb +2 -0
  56. data/app/views/railswatch/railswatch/custom.html.erb +6 -0
  57. data/app/views/railswatch/railswatch/delayed_job.html.erb +6 -0
  58. data/app/views/railswatch/railswatch/grape.html.erb +6 -0
  59. data/app/views/railswatch/railswatch/index.html.erb +9 -0
  60. data/app/views/railswatch/railswatch/rake.html.erb +6 -0
  61. data/app/views/railswatch/railswatch/recent.html.erb +2 -0
  62. data/app/views/railswatch/railswatch/requests.html.erb +2 -0
  63. data/app/views/railswatch/railswatch/resources.html.erb +28 -0
  64. data/app/views/railswatch/railswatch/sidekiq.html.erb +6 -0
  65. data/app/views/railswatch/railswatch/slow.html.erb +2 -0
  66. data/app/views/railswatch/railswatch/summary.js.erb +3 -0
  67. data/app/views/railswatch/railswatch/trace.js.erb +9 -0
  68. data/app/views/railswatch/shared/_header.html.erb +39 -0
  69. data/app/views/railswatch/shared/_page_header.html.erb +23 -0
  70. data/config/routes.rb +27 -0
  71. data/lib/generators/railswatch/install/USAGE +19 -0
  72. data/lib/generators/railswatch/install/install_generator.rb +46 -0
  73. data/lib/generators/railswatch/install/templates/create_railswatch_tables.rb +140 -0
  74. data/lib/generators/railswatch/install/templates/initializer.rb +87 -0
  75. data/lib/railswatch/data_source.rb +106 -0
  76. data/lib/railswatch/engine.rb +103 -0
  77. data/lib/railswatch/events/record.rb +63 -0
  78. data/lib/railswatch/extensions/trace.rb +14 -0
  79. data/lib/railswatch/extensions/trace_db.rb +21 -0
  80. data/lib/railswatch/gems/custom_ext.rb +38 -0
  81. data/lib/railswatch/gems/delayed_job_ext.rb +70 -0
  82. data/lib/railswatch/gems/grape_ext.rb +64 -0
  83. data/lib/railswatch/gems/rake_ext.rb +69 -0
  84. data/lib/railswatch/gems/sidekiq_ext.rb +55 -0
  85. data/lib/railswatch/instrument/metrics_collector.rb +70 -0
  86. data/lib/railswatch/interface.rb +9 -0
  87. data/lib/railswatch/models/application_record.rb +31 -0
  88. data/lib/railswatch/models/base_record.rb +59 -0
  89. data/lib/railswatch/models/collection.rb +37 -0
  90. data/lib/railswatch/models/custom_record.rb +32 -0
  91. data/lib/railswatch/models/delayed_job_record.rb +39 -0
  92. data/lib/railswatch/models/event_record.rb +11 -0
  93. data/lib/railswatch/models/grape_record.rb +61 -0
  94. data/lib/railswatch/models/rake_record.rb +33 -0
  95. data/lib/railswatch/models/request_record.rb +105 -0
  96. data/lib/railswatch/models/resource_record.rb +33 -0
  97. data/lib/railswatch/models/sidekiq_record.rb +41 -0
  98. data/lib/railswatch/models/trace_record.rb +21 -0
  99. data/lib/railswatch/pruner.rb +47 -0
  100. data/lib/railswatch/rails/middleware.rb +117 -0
  101. data/lib/railswatch/rails/query_builder.rb +20 -0
  102. data/lib/railswatch/reports/annotations_report.rb +13 -0
  103. data/lib/railswatch/reports/base_report.rb +48 -0
  104. data/lib/railswatch/reports/breakdown_report.rb +11 -0
  105. data/lib/railswatch/reports/crash_report.rb +11 -0
  106. data/lib/railswatch/reports/overview_report.rb +88 -0
  107. data/lib/railswatch/reports/percentile_report.rb +16 -0
  108. data/lib/railswatch/reports/recent_requests_report.rb +21 -0
  109. data/lib/railswatch/reports/requests_report.rb +75 -0
  110. data/lib/railswatch/reports/resources_report.rb +42 -0
  111. data/lib/railswatch/reports/response_time_report.rb +27 -0
  112. data/lib/railswatch/reports/slow_requests_report.rb +21 -0
  113. data/lib/railswatch/reports/throughput_report.rb +16 -0
  114. data/lib/railswatch/reports/trace_report.rb +17 -0
  115. data/lib/railswatch/system_monitor/resources_monitor.rb +88 -0
  116. data/lib/railswatch/thread/current_request.rb +37 -0
  117. data/lib/railswatch/utils.rb +58 -0
  118. data/lib/railswatch/version.rb +7 -0
  119. data/lib/railswatch/widgets/base.rb +17 -0
  120. data/lib/railswatch/widgets/card.rb +19 -0
  121. data/lib/railswatch/widgets/chart.rb +33 -0
  122. data/lib/railswatch/widgets/crashes_table.rb +27 -0
  123. data/lib/railswatch/widgets/custom_events_table.rb +48 -0
  124. data/lib/railswatch/widgets/delayed_job_table.rb +31 -0
  125. data/lib/railswatch/widgets/grape_requests_table.rb +31 -0
  126. data/lib/railswatch/widgets/percentile_card.rb +23 -0
  127. data/lib/railswatch/widgets/rake_tasks_table.rb +31 -0
  128. data/lib/railswatch/widgets/recent_requests_table.rb +35 -0
  129. data/lib/railswatch/widgets/requests_table.rb +27 -0
  130. data/lib/railswatch/widgets/resource_chart.rb +116 -0
  131. data/lib/railswatch/widgets/response_time_chart.rb +29 -0
  132. data/lib/railswatch/widgets/sidekiq_jobs_table.rb +31 -0
  133. data/lib/railswatch/widgets/slow_requests_table.rb +33 -0
  134. data/lib/railswatch/widgets/table.rb +43 -0
  135. data/lib/railswatch/widgets/throughput_chart.rb +29 -0
  136. data/lib/railswatch.rb +184 -0
  137. data/lib/tasks/railswatch.rake +9 -0
  138. metadata +445 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fa66ff41888554ca29ee2d634bbf31701298ca13dd94973a9700e97ac8d26375
4
+ data.tar.gz: 1548b8107dae00b59b9cef9c2effea9223e724486bf619752dc4c8d681a68217
5
+ SHA512:
6
+ metadata.gz: 9bd95a2eddaa365ca48eda575e0ad0371ad2d90a0167deb8beff351f198f4b4b993ccfad1f7dd9abc5c9377da666836f839a00f88cf2eae2ef3b13f99f9ea355
7
+ data.tar.gz: e87f1c5cec9055f9caff00b13007b64f4858f04052666f2f90a95b1b48458284a39590f1fd95e78ff623821dd07366ebdbf34dbc4164bfc76ffeceae2730bb44
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2020 Sourabh Patware
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,485 @@
1
+ # Railswatch
2
+
3
+ [![Tests](https://github.com/100rabhg/railswatch/actions/workflows/ruby.yml/badge.svg)](https://github.com/100rabhg/railswatch/actions/workflows/ruby.yml)
4
+
5
+ A self-hosted tool to monitor the performance of your Ruby on Rails application.
6
+
7
+ This is a **simple and free alternative** to the New Relic APM, Datadog or other similar services.
8
+
9
+ ![Home](docs/railswatch_home.png)
10
+
11
+ P50, P90, P99, throughput, and more is available.
12
+
13
+ Detailed p50, p90, p99 response time information.
14
+
15
+ ![Recent Requests](docs/railswatch_recent_requests.png)
16
+
17
+ Per-controller breakdown with response time percentiles.
18
+
19
+ ![Requests Breakdown](docs/recent_requests.png)
20
+
21
+ (more screenshots below)
22
+
23
+ It allows you to track:
24
+
25
+ - real-time monitoring on the Recent tab
26
+ - see your p50, p90, p99 response time
27
+ - monitor system resources (CPU, memory, disk)
28
+ - monitor slow requests
29
+ - throughput report (see amount of RPM (requests per minute))
30
+ - an average response time
31
+ - the slowest controllers & actions
32
+ - total duration of time spent per request, views rendering, DB
33
+ - SQL queries, rendering logs in "Recent Requests" section
34
+ - simple 500-crashes reports
35
+ - deployment events (or custom events)
36
+ - Sidekiq jobs
37
+ - Delayed Job jobs
38
+ - Grape API inside Rails app
39
+ - Rake tasks performance
40
+ - Custom events wrapped with `Railswatch.measure do .. end` block
41
+ - Active Record-backed storage, with optional separate database support on modern Rails multi-database setups
42
+
43
+ All data are stored in your application's database and not sent to any 3rd party servers.
44
+
45
+ ## Production
46
+
47
+ Gem is production-ready. At least in my 2 applications with ~1000 unique users per day it works perfectly.
48
+
49
+ Just don't forget to protect performance dashboard with http basic auth or check of current_user.
50
+
51
+ ## Quick Start
52
+
53
+ 1. Add the gem to your application's `Gemfile`
54
+ 2. Run `bundle install`
55
+ 3. Run `bin/rails generate railswatch:install`
56
+ 4. Run your database migrations for the storage option you chose
57
+ 5. Start the app, make a few requests, and open `/railswatch`
58
+
59
+ Railswatch stores monitoring data in your application's database by default.
60
+ You do not need Redis for Railswatch storage.
61
+
62
+ If your application uses Sidekiq, Sidekiq itself still uses Redis, but Railswatch stores the captured job metrics in your database.
63
+
64
+ ## Installation
65
+
66
+ Add this line to your application's Gemfile:
67
+
68
+ ```ruby
69
+ gem 'railswatch'
70
+
71
+ # or
72
+
73
+ group :development, :production do
74
+ gem 'railswatch'
75
+ end
76
+ ```
77
+
78
+ Install dependencies:
79
+
80
+ ```bash
81
+ $ bundle install
82
+ ```
83
+
84
+ Generate the initializer and migration:
85
+
86
+ ```bash
87
+ $ bin/rails generate railswatch:install
88
+ ```
89
+
90
+ That generator creates:
91
+
92
+ - `config/initializers/railswatch.rb`
93
+ - `db/migrate/*_create_railswatch_tables.rb`
94
+
95
+ ## Setup Guide
96
+
97
+ ### 1. Choose where monitoring data is stored
98
+
99
+ #### Option A: Use your primary application database
100
+
101
+ This is the default and simplest setup:
102
+
103
+ ```ruby
104
+ config.database_connection_name = nil
105
+ ```
106
+
107
+ Then run:
108
+
109
+ ```bash
110
+ $ bin/rails db:migrate
111
+ ```
112
+
113
+ #### Option B: Use a separate monitoring database
114
+
115
+ Add a dedicated database to `config/database.yml`:
116
+
117
+ ```yaml
118
+ development:
119
+ primary:
120
+ <<: *default
121
+ database: storage/development.sqlite3
122
+ railswatch:
123
+ <<: *default
124
+ database: storage/development_railswatch.sqlite3
125
+
126
+ test:
127
+ primary:
128
+ <<: *default
129
+ database: storage/test.sqlite3
130
+ railswatch:
131
+ <<: *default
132
+ database: storage/test_railswatch.sqlite3
133
+ ```
134
+
135
+ Then set:
136
+
137
+ ```ruby
138
+ config.database_connection_name = :railswatch
139
+ ```
140
+
141
+ Run migrations for both databases:
142
+
143
+ ```bash
144
+ $ bin/rails db:migrate
145
+ $ bin/rails db:migrate:railswatch
146
+ ```
147
+
148
+ You can use any connection name you prefer. `config.database_connection_name` must match the database name in `config/database.yml`.
149
+
150
+ ### 2. Start the app
151
+
152
+ After installation and configuration:
153
+
154
+ ```bash
155
+ $ bin/rails server
156
+ ```
157
+
158
+ Make a few requests to your app, then open:
159
+
160
+ ```text
161
+ http://localhost:3000/railswatch
162
+ ```
163
+
164
+ ### 3. Protect the dashboard
165
+
166
+ The dashboard should not be publicly accessible in production.
167
+
168
+ You can protect it with HTTP Basic auth:
169
+
170
+ ```ruby
171
+ config.http_basic_authentication_enabled = true
172
+ config.http_basic_authentication_user_name = 'railswatch'
173
+ config.http_basic_authentication_password = ENV.fetch('RAILSWATCH_PASSWORD')
174
+ ```
175
+
176
+ Or with your own app-level authorization:
177
+
178
+ ```ruby
179
+ config.verify_access_proc = proc do |controller|
180
+ controller.current_user&.admin?
181
+ end
182
+ ```
183
+
184
+ ## Configuration
185
+
186
+ Default configuration is listed below. You can override it in `config/initializers/railswatch.rb`:
187
+
188
+ ```ruby
189
+ Railswatch.setup do |config|
190
+ # Use the primary application database by default.
191
+ # To use a dedicated database, define it in config/database.yml and set:
192
+ # config.database_connection_name = :railswatch
193
+ config.database_connection_name = nil
194
+
195
+ config.duration = 4.hours
196
+
197
+ config.debug = false
198
+ config.enabled = true
199
+
200
+ # configure Recent tab (time window and limit of requests)
201
+ # config.recent_requests_time_window = 60.minutes
202
+ # config.recent_requests_limit = nil # or 1000
203
+
204
+ # configure Slow Requests tab (time window, limit of requests and threshold)
205
+ # config.slow_requests_time_window = 4.hours # time window for slow requests
206
+ # config.slow_requests_limit = 500 # number of max rows
207
+ # config.slow_requests_threshold = 500 # number of ms
208
+
209
+ # default path where to mount gem,
210
+ # alternatively you can mount the Railswatch::Engine in your routes.rb
211
+ config.mount_at = '/railswatch'
212
+
213
+ # protect your Performance Dashboard with HTTP BASIC password
214
+ config.http_basic_authentication_enabled = false
215
+ config.http_basic_authentication_user_name = 'railswatch'
216
+ config.http_basic_authentication_password = 'password12'
217
+
218
+ # if you need an additional rules to check user permissions
219
+ config.verify_access_proc = proc { |controller| true }
220
+ # for example when you have `current_user`
221
+ # config.verify_access_proc = proc { |controller| controller.current_user && controller.current_user.admin? }
222
+
223
+ # You can ignore endpoints with Rails standard notation controller#action
224
+ # config.ignored_endpoints = ['HomeController#contact']
225
+
226
+ # You can ignore request paths by specifying the beginning of the path.
227
+ # For example, all routes starting with '/admin' can be ignored:
228
+ config.ignored_paths = ['/railswatch', '/admin']
229
+
230
+ # store custom data for the request
231
+ # config.custom_data_proc = proc do |env|
232
+ # request = Rack::Request.new(env)
233
+ # {
234
+ # email: request.env['warden'].user&.email, # if you are using Devise for example
235
+ # user_agent: request.env['HTTP_USER_AGENT']
236
+ # }
237
+ # end
238
+
239
+ # config home button link
240
+ config.home_link = '/'
241
+
242
+ # To skip some Rake tasks from monitoring
243
+ config.skipable_rake_tasks = ['webpacker:compile']
244
+
245
+ # To monitor rake tasks performance, you need to include rake tasks
246
+ # config.include_rake_tasks = false
247
+
248
+ # To monitor custom events with `Railswatch.measure` block
249
+ # config.include_custom_events = true
250
+
251
+ # To monitor system resources (CPU, memory, disk)
252
+ # to enabled add required gems (see README)
253
+ # config.system_monitor_duration = 24.hours
254
+
255
+ config.retention = {
256
+ requests: config.duration,
257
+ sidekiq: config.duration,
258
+ delayed_job: config.duration,
259
+ grape: config.duration,
260
+ rake: config.duration,
261
+ custom: config.duration,
262
+ traces: config.recent_requests_time_window,
263
+ resources: 24.hours,
264
+ events: nil
265
+ }
266
+ end if defined?(Railswatch)
267
+ ```
268
+
269
+ Additionally you might need to configure app time zone. You can do it in `config/application.rb`:
270
+
271
+ ```ruby
272
+ config.time_zone = 'Eastern Time (US & Canada)'
273
+ ```
274
+
275
+ Gem will present charts/tables in the app timezone. If it's not set, it will use UTC.
276
+
277
+ ## Optional Integrations
278
+
279
+ ### Sidekiq
280
+
281
+ If your app uses Sidekiq, Railswatch will capture Sidekiq job metrics automatically when Sidekiq is present.
282
+
283
+ Railswatch does not require Redis for its own storage, but Sidekiq still requires Redis as usual.
284
+
285
+ ![Sidekiq](docs/sidekiq.png)
286
+
287
+ ### Delayed Job
288
+
289
+ If your app uses Delayed Job, add the adapter gem to your app:
290
+
291
+ ```ruby
292
+ gem 'delayed_job_active_record'
293
+ ```
294
+
295
+ Railswatch stores captured Delayed Job metrics in your database.
296
+
297
+ ### System resource monitoring
298
+
299
+ To show CPU, memory, and disk charts on the dashboard, add:
300
+
301
+ ```ruby
302
+ gem 'sys-filesystem'
303
+ gem 'sys-cpu'
304
+ gem 'get_process_mem'
305
+ ```
306
+
307
+ Once these gems are installed, Railswatch will collect and display system resource metrics.
308
+
309
+ ### Retention and pruning
310
+
311
+ Retention is configured per record type with `config.retention`. Set a duration to prune old records, or `nil` to keep that record type forever.
312
+
313
+ ```ruby
314
+ config.retention = {
315
+ requests: 4.hours,
316
+ sidekiq: 4.hours,
317
+ delayed_job: 4.hours,
318
+ grape: 4.hours,
319
+ rake: 4.hours,
320
+ custom: 4.hours,
321
+ traces: 60.minutes,
322
+ resources: 24.hours,
323
+ events: nil
324
+ }
325
+ ```
326
+
327
+ Prune old records manually or from a scheduler:
328
+
329
+ ```bash
330
+ $ bin/rails railswatch:prune
331
+ ```
332
+
333
+ ### Alternative: Mounting the engine yourself
334
+
335
+ If you, for whatever reason (company policy, devise, ...) need to mount Railswatch yourself, feel free to do so by using the following snippet as inspiration.
336
+ You can skip the `mount_at` and `http_basic_authentication_*` configurations then, if you like.
337
+ Under certain constraints (i.e. subdomains) it may be necessary to set `url_options` in the config so that Railswatch can generate links correctly.
338
+
339
+ ```ruby
340
+ # config/routes.rb
341
+ Rails.application.routes.draw do
342
+ ...
343
+ # example for usage with Devise
344
+ authenticate :user, -> (user) { user.admin? } do
345
+ mount Railswatch::Engine, at: 'railswatch'
346
+ end
347
+ end
348
+ ```
349
+
350
+ ### 500 Errors and request context
351
+
352
+ Railswatch captures every 500 error with a full backtrace, the request path, controller/action, and per-request context: client IP, user-agent, and filtered request params.
353
+
354
+ ![Errors](docs/errors.png)
355
+
356
+ ### Custom data
357
+
358
+ You can configure `config.custom_data_proc` to capture additional data per request (e.g. `current_user`, email). This proc is executed inside middleware and has access to the Rack `env`.
359
+
360
+
361
+ ### Server Monitoring
362
+
363
+ You can monitor system resources (CPU, memory, disk) by adding these gems to your Gemfile:
364
+
365
+ ```ruby
366
+ gem "sys-filesystem"
367
+ gem "sys-cpu"
368
+ gem "get_process_mem"
369
+ ```
370
+
371
+ Once you add these gems, Railswatch will track and show system resources on the dashboard.
372
+
373
+ If you have multiple servers running the same app, it will use store metrics per server. You can configure the env variable `ENV["RAILSWATCH_SERVER_ID"]` or use `hostname`.
374
+
375
+ Basically using this code:
376
+
377
+ ```ruby
378
+ def server_id
379
+ @server_id ||= ENV["RAILSWATCH_SERVER_ID"] || `hostname`.strip
380
+ end
381
+ ```
382
+
383
+ For Kamal for example:
384
+
385
+ ```yaml
386
+ env:
387
+ clear:
388
+ RAILSWATCH_SERVER_ID: "server"
389
+ ```
390
+
391
+ You can also specify custom "context" and "role" for monitoring, by changing the env variables:
392
+
393
+ ```ruby
394
+ Railswatch::SystemMonitor::ResourcesMonitor.new(
395
+ ENV["RAILSWATCH_SERVER_CONTEXT"].presence || "rails",
396
+ ENV["RAILSWATCH_SERVER_ROLE"].presence || "web"
397
+ )
398
+ ```
399
+
400
+ More information here: `lib/railswatch/engine.rb`.
401
+
402
+ PS: right now it can only distinguish between web app servers and the sidekiq servers.
403
+
404
+ #### Deployment Events + Custom Events on the Charts
405
+
406
+ #### For Kamal
407
+
408
+ - edit `.kamal/hooks/post-deploy` (rename .sample if needed)
409
+ - add `kamal app exec -p './bin/rails runner "Railswatch.create_event(name: \"Deploy\")"'`
410
+ - kamal deploy
411
+
412
+ #### Custom Events on the Charts
413
+
414
+ You can specify colors, orientation for the event label.
415
+
416
+ ```ruby
417
+ Railswatch.create_event(name: "Deploy", options: {
418
+ borderColor: "#00E396",
419
+ label: {
420
+ borderColor: "#00E396",
421
+ orientation: "horizontal",
422
+ text: "Deploy"
423
+ }
424
+ })
425
+ ```
426
+
427
+ ### Custom events
428
+
429
+ ```ruby
430
+ Railswatch.measure("some label", "some namespace") do
431
+ # your code
432
+ end
433
+ ```
434
+
435
+ ## How it works
436
+
437
+ ![Schema](docs/railswatch.png)
438
+
439
+ In addition it's wrapping gems internal methods and collecting performance information. See `./lib/railswatch/gems/*` for more information.
440
+
441
+ ## Limitations
442
+
443
+ - it doesn't track params of POST/PUT requests
444
+ - it doesn't track ElasticSearch or other apps
445
+ - it can't compare historical data
446
+ - depending on your load you may need to reduce how long you keep data, especially if you retain large request volumes for reporting
447
+
448
+ ## Development & Testing
449
+
450
+ Just clone the repo, setup dummy app (`rails db:migrate`).
451
+
452
+ After this:
453
+
454
+ - rails s
455
+ - rake test
456
+
457
+ If you need to clear collected data during development, you can delete rows from the `railswatch_*` tables or run a short retention window with `bin/rails railswatch:prune`.
458
+
459
+ Like a regular web development.
460
+
461
+ Please note that to simplify integration with other apps all CSS/JS are bundled inside, and delivered in body of the request. This is to avoid integration with assets pipeline or webpacker.
462
+
463
+ For UI changes you need to use Bulma CSS (https://bulma.io/documentation).
464
+
465
+ ## Why
466
+
467
+ The idea of this gem grew from curiosity how many RPM my app receiving per day. Later it evolved to something more powerful.
468
+
469
+ ## Development
470
+
471
+ 1. Clone the repo
472
+ 2. Run `bundle install`
473
+ 3. Set up the dummy app with `cd test/dummy && bundle install && rails db:create && rails db:migrate`
474
+ 4. Run `rails s` in the project root
475
+ 5. Run `rails test`
476
+
477
+ ## Contributing
478
+
479
+ You are welcome to contribute. I've a big list of TODO.
480
+
481
+ If the storage schema changes in a breaking way, add a migration and document the upgrade path in the changelog and README.
482
+
483
+ ## License
484
+
485
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rdoc/task'
10
+
11
+ RDoc::Task.new(:rdoc) do |rdoc|
12
+ rdoc.rdoc_dir = 'rdoc'
13
+ rdoc.title = 'Railswatch'
14
+ rdoc.options << '--line-numbers'
15
+ rdoc.rdoc_files.include('README.md')
16
+ rdoc.rdoc_files.include('lib/**/*.rb')
17
+ end
18
+
19
+ APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
20
+ load 'rails/tasks/engine.rake'
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+ require 'bundler/gem_tasks'
24
+ require 'rake/testtask'
25
+ begin
26
+ require 'standard/rake'
27
+ rescue LoadError
28
+ nil
29
+ end
30
+
31
+ Rake::TestTask.new(:test) do |t|
32
+ t.libs << 'test'
33
+ t.pattern = 'test/**/*_test.rb'
34
+ t.verbose = false
35
+ end
36
+
37
+ task default: [:test]
File without changes
@@ -0,0 +1,13 @@
1
+ <?xml version="1.0" ?><svg height="48" id="activity" viewBox="0 0 48 48" width="48" xmlns="http://www.w3.org/2000/svg"><defs><style>
2
+ .vi-primary {
3
+ fill: #FF6E6E;
4
+ }
5
+
6
+ .vi-primary, .vi-accent {
7
+ fill-rule: evenodd;
8
+ }
9
+
10
+ .vi-accent {
11
+ fill: #0C0058;
12
+ }
13
+ </style></defs><path class="vi-primary" d="M20,33a2,2,0,0,1-1.788-1.1l-3.6-7.183-2.047,2.551A2,2,0,0,1,11,28.013H7a1.994,1.994,0,1,1,0-3.988h3.039l3.4-4.237a2,2,0,0,1,3.351.354l2.7,5.386,2.588-9.03a2,2,0,0,1,3.75-.262l3.473,7.79H32a1.994,1.994,0,1,1,0,3.988H28a2,2,0,0,1-1.827-1.184L24.41,22.874l-2.487,8.678a2,2,0,0,1-1.737,1.437C20.123,33,20.061,33,20,33Z"/><path class="vi-accent" d="M37,32a6,6,0,1,1,6-6A6,6,0,0,1,37,32Zm0-8.717A2.72,2.72,0,1,0,39.728,26,2.724,2.724,0,0,0,37,23.283Z"/></svg>
@@ -0,0 +1 @@
1
+ <?xml version="1.0" ?><svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M21.928 11.607c-.202-.488-.635-.605-.928-.633V8c0-1.103-.897-2-2-2h-6V4.61c.305-.274.5-.668.5-1.11a1.5 1.5 0 0 0-3 0c0 .442.195.836.5 1.11V6H5c-1.103 0-2 .897-2 2v2.997l-.082.006A1 1 0 0 0 1.99 12v2a1 1 0 0 0 1 1H3v5c0 1.103.897 2 2 2h14c1.103 0 2-.897 2-2v-5a1 1 0 0 0 1-1v-1.938a1.006 1.006 0 0 0-.072-.455zM5 20V8h14l.001 3.996L19 12v2l.001.005.001 5.995H5z"/><ellipse cx="8.5" cy="12" rx="1.5" ry="2"/><ellipse cx="15.5" cy="12" rx="1.5" ry="2"/><path d="M8 16h8v2H8z"/></svg>
@@ -0,0 +1,13 @@
1
+ <?xml version="1.0" ?><svg height="48" id="close" viewBox="0 0 48 48" width="48" xmlns="http://www.w3.org/2000/svg"><defs><style>
2
+ .vi-primary {
3
+ fill: #FF6E6E;
4
+ }
5
+
6
+ .vi-primary, .vi-accent {
7
+ fill-rule: evenodd;
8
+ }
9
+
10
+ .vi-accent {
11
+ fill: #0C0058;
12
+ }
13
+ </style></defs><path class="vi-primary" d="M35,32l-3,3-7.5-7.5L17,35l-3-3,7.5-7.5L14,17l3-3,7.5,7.5L32,14l3,3-7.5,7.5Z"/><path class="vi-accent" d="M20,32l-3,3-3-3,3-3ZM35,17l-3,3-3-3,3-3ZM29,32l3,3,3-3-3-3ZM14,17l3,3,3-3-3-3Z"/></svg>
@@ -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,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="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3" />
3
+ </svg>
@@ -0,0 +1,13 @@
1
+ <?xml version="1.0" ?><svg height="48" id="share" viewBox="0 0 48 48" width="48" xmlns="http://www.w3.org/2000/svg"><defs><style>
2
+ .vi-primary {
3
+ fill: #FF6E6E;
4
+ stroke: #fff;
5
+ stroke-linecap: round;
6
+ stroke-width: 0;
7
+ }
8
+
9
+ .vi-accent {
10
+ fill: #0C0058;
11
+ fill-rule: evenodd;
12
+ }
13
+ </style></defs><rect class="vi-primary" height="28" width="28" x="5" y="10"/><path class="vi-accent" d="M17,25H39l-3,3,2,2,6-6V23l-6-6-2,2,3,3H17v3Z"/></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>