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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +485 -0
- data/Rakefile +37 -0
- data/app/assets/config/railswatch_manifest.js +0 -0
- data/app/assets/images/activity.svg +13 -0
- data/app/assets/images/bot.svg +1 -0
- data/app/assets/images/close.svg +13 -0
- data/app/assets/images/details.svg +3 -0
- data/app/assets/images/download.svg +3 -0
- data/app/assets/images/export.svg +13 -0
- data/app/assets/images/external.svg +1 -0
- data/app/assets/images/git.svg +1 -0
- data/app/assets/images/github.svg +1 -0
- data/app/assets/images/home.svg +16 -0
- data/app/assets/images/import.svg +13 -0
- data/app/assets/images/menu.svg +16 -0
- data/app/assets/images/moon.svg +3 -0
- data/app/assets/images/stat.svg +1 -0
- data/app/assets/images/sun.svg +4 -0
- data/app/assets/images/user.svg +1 -0
- data/app/controllers/railswatch/base_controller.rb +35 -0
- data/app/controllers/railswatch/concerns/csv_exportable.rb +31 -0
- data/app/controllers/railswatch/railswatch_controller.rb +183 -0
- data/app/engine_assets/javascripts/apex_ext.js +30 -0
- data/app/engine_assets/javascripts/application.js +9 -0
- data/app/engine_assets/javascripts/autoupdate.js +79 -0
- data/app/engine_assets/javascripts/charts.js +279 -0
- data/app/engine_assets/javascripts/navbar.js +11 -0
- data/app/engine_assets/javascripts/panel.js +43 -0
- data/app/engine_assets/javascripts/table.js +12 -0
- data/app/engine_assets/javascripts/theme.js +43 -0
- data/app/engine_assets/stylesheets/panel.css +111 -0
- data/app/engine_assets/stylesheets/responsive.css +102 -0
- data/app/engine_assets/stylesheets/style.css +960 -0
- data/app/helpers/railswatch/railswatch_helper.rb +338 -0
- data/app/views/railswatch/_panel.html.erb +15 -0
- data/app/views/railswatch/layouts/railswatch.html.erb +81 -0
- data/app/views/railswatch/railswatch/_card.html.erb +7 -0
- data/app/views/railswatch/railswatch/_chart.html.erb +13 -0
- data/app/views/railswatch/railswatch/_crashes_table_content.html.erb +62 -0
- data/app/views/railswatch/railswatch/_custom_events_table_content.html.erb +27 -0
- data/app/views/railswatch/railswatch/_delayed_job_table_content.html.erb +52 -0
- data/app/views/railswatch/railswatch/_export.html.erb +4 -0
- data/app/views/railswatch/railswatch/_grape_requests_table_content.html.erb +31 -0
- data/app/views/railswatch/railswatch/_overview.html.erb +124 -0
- data/app/views/railswatch/railswatch/_rake_tasks_table_content.html.erb +25 -0
- data/app/views/railswatch/railswatch/_recent_requests_table_content.html.erb +28 -0
- data/app/views/railswatch/railswatch/_recent_row.html.erb +41 -0
- data/app/views/railswatch/railswatch/_requests_table_content.html.erb +51 -0
- data/app/views/railswatch/railswatch/_sidekiq_jobs_table_content.html.erb +50 -0
- data/app/views/railswatch/railswatch/_summary.html.erb +50 -0
- data/app/views/railswatch/railswatch/_table.html.erb +30 -0
- data/app/views/railswatch/railswatch/_trace.html.erb +78 -0
- data/app/views/railswatch/railswatch/crashes.html.erb +2 -0
- data/app/views/railswatch/railswatch/custom.html.erb +6 -0
- data/app/views/railswatch/railswatch/delayed_job.html.erb +6 -0
- data/app/views/railswatch/railswatch/grape.html.erb +6 -0
- data/app/views/railswatch/railswatch/index.html.erb +9 -0
- data/app/views/railswatch/railswatch/rake.html.erb +6 -0
- data/app/views/railswatch/railswatch/recent.html.erb +2 -0
- data/app/views/railswatch/railswatch/requests.html.erb +2 -0
- data/app/views/railswatch/railswatch/resources.html.erb +28 -0
- data/app/views/railswatch/railswatch/sidekiq.html.erb +6 -0
- data/app/views/railswatch/railswatch/slow.html.erb +2 -0
- data/app/views/railswatch/railswatch/summary.js.erb +3 -0
- data/app/views/railswatch/railswatch/trace.js.erb +9 -0
- data/app/views/railswatch/shared/_header.html.erb +39 -0
- data/app/views/railswatch/shared/_page_header.html.erb +23 -0
- data/config/routes.rb +27 -0
- data/lib/generators/railswatch/install/USAGE +19 -0
- data/lib/generators/railswatch/install/install_generator.rb +46 -0
- data/lib/generators/railswatch/install/templates/create_railswatch_tables.rb +140 -0
- data/lib/generators/railswatch/install/templates/initializer.rb +87 -0
- data/lib/railswatch/data_source.rb +106 -0
- data/lib/railswatch/engine.rb +103 -0
- data/lib/railswatch/events/record.rb +63 -0
- data/lib/railswatch/extensions/trace.rb +14 -0
- data/lib/railswatch/extensions/trace_db.rb +21 -0
- data/lib/railswatch/gems/custom_ext.rb +38 -0
- data/lib/railswatch/gems/delayed_job_ext.rb +70 -0
- data/lib/railswatch/gems/grape_ext.rb +64 -0
- data/lib/railswatch/gems/rake_ext.rb +69 -0
- data/lib/railswatch/gems/sidekiq_ext.rb +55 -0
- data/lib/railswatch/instrument/metrics_collector.rb +70 -0
- data/lib/railswatch/interface.rb +9 -0
- data/lib/railswatch/models/application_record.rb +31 -0
- data/lib/railswatch/models/base_record.rb +59 -0
- data/lib/railswatch/models/collection.rb +37 -0
- data/lib/railswatch/models/custom_record.rb +32 -0
- data/lib/railswatch/models/delayed_job_record.rb +39 -0
- data/lib/railswatch/models/event_record.rb +11 -0
- data/lib/railswatch/models/grape_record.rb +61 -0
- data/lib/railswatch/models/rake_record.rb +33 -0
- data/lib/railswatch/models/request_record.rb +105 -0
- data/lib/railswatch/models/resource_record.rb +33 -0
- data/lib/railswatch/models/sidekiq_record.rb +41 -0
- data/lib/railswatch/models/trace_record.rb +21 -0
- data/lib/railswatch/pruner.rb +47 -0
- data/lib/railswatch/rails/middleware.rb +117 -0
- data/lib/railswatch/rails/query_builder.rb +20 -0
- data/lib/railswatch/reports/annotations_report.rb +13 -0
- data/lib/railswatch/reports/base_report.rb +48 -0
- data/lib/railswatch/reports/breakdown_report.rb +11 -0
- data/lib/railswatch/reports/crash_report.rb +11 -0
- data/lib/railswatch/reports/overview_report.rb +88 -0
- data/lib/railswatch/reports/percentile_report.rb +16 -0
- data/lib/railswatch/reports/recent_requests_report.rb +21 -0
- data/lib/railswatch/reports/requests_report.rb +75 -0
- data/lib/railswatch/reports/resources_report.rb +42 -0
- data/lib/railswatch/reports/response_time_report.rb +27 -0
- data/lib/railswatch/reports/slow_requests_report.rb +21 -0
- data/lib/railswatch/reports/throughput_report.rb +16 -0
- data/lib/railswatch/reports/trace_report.rb +17 -0
- data/lib/railswatch/system_monitor/resources_monitor.rb +88 -0
- data/lib/railswatch/thread/current_request.rb +37 -0
- data/lib/railswatch/utils.rb +58 -0
- data/lib/railswatch/version.rb +7 -0
- data/lib/railswatch/widgets/base.rb +17 -0
- data/lib/railswatch/widgets/card.rb +19 -0
- data/lib/railswatch/widgets/chart.rb +33 -0
- data/lib/railswatch/widgets/crashes_table.rb +27 -0
- data/lib/railswatch/widgets/custom_events_table.rb +48 -0
- data/lib/railswatch/widgets/delayed_job_table.rb +31 -0
- data/lib/railswatch/widgets/grape_requests_table.rb +31 -0
- data/lib/railswatch/widgets/percentile_card.rb +23 -0
- data/lib/railswatch/widgets/rake_tasks_table.rb +31 -0
- data/lib/railswatch/widgets/recent_requests_table.rb +35 -0
- data/lib/railswatch/widgets/requests_table.rb +27 -0
- data/lib/railswatch/widgets/resource_chart.rb +116 -0
- data/lib/railswatch/widgets/response_time_chart.rb +29 -0
- data/lib/railswatch/widgets/sidekiq_jobs_table.rb +31 -0
- data/lib/railswatch/widgets/slow_requests_table.rb +33 -0
- data/lib/railswatch/widgets/table.rb +43 -0
- data/lib/railswatch/widgets/throughput_chart.rb +29 -0
- data/lib/railswatch.rb +184 -0
- data/lib/tasks/railswatch.rake +9 -0
- 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
|
+
[](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
|
+

|
|
10
|
+
|
|
11
|
+
P50, P90, P99, throughput, and more is available.
|
|
12
|
+
|
|
13
|
+
Detailed p50, p90, p99 response time information.
|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
Per-controller breakdown with response time percentiles.
|
|
18
|
+
|
|
19
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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>
|