rails_pulse 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +72 -176
- data/Rakefile +77 -2
- data/app/assets/stylesheets/rails_pulse/application.css +0 -12
- data/app/controllers/concerns/chart_table_concern.rb +21 -4
- data/app/controllers/concerns/response_range_concern.rb +6 -3
- data/app/controllers/concerns/time_range_concern.rb +5 -10
- data/app/controllers/concerns/zoom_range_concern.rb +1 -1
- data/app/controllers/rails_pulse/application_controller.rb +8 -4
- data/app/controllers/rails_pulse/dashboard_controller.rb +12 -0
- data/app/controllers/rails_pulse/queries_controller.rb +65 -50
- data/app/controllers/rails_pulse/requests_controller.rb +24 -12
- data/app/controllers/rails_pulse/routes_controller.rb +59 -24
- data/app/helpers/rails_pulse/application_helper.rb +0 -1
- data/app/helpers/rails_pulse/chart_formatters.rb +3 -3
- data/app/helpers/rails_pulse/chart_helper.rb +6 -2
- data/app/helpers/rails_pulse/status_helper.rb +10 -4
- data/app/javascript/rails_pulse/controllers/index_controller.js +117 -33
- data/app/javascript/rails_pulse/controllers/pagination_controller.js +17 -27
- data/app/jobs/rails_pulse/backfill_summaries_job.rb +41 -0
- data/app/jobs/rails_pulse/summary_job.rb +53 -0
- data/app/models/rails_pulse/dashboard/charts/average_response_time.rb +28 -7
- data/app/models/rails_pulse/dashboard/charts/p95_response_time.rb +18 -22
- data/app/models/rails_pulse/dashboard/tables/slow_queries.rb +18 -7
- data/app/models/rails_pulse/dashboard/tables/slow_routes.rb +34 -41
- data/app/models/rails_pulse/operation.rb +1 -1
- data/app/models/rails_pulse/queries/cards/average_query_times.rb +47 -23
- data/app/models/rails_pulse/queries/cards/execution_rate.rb +33 -26
- data/app/models/rails_pulse/queries/cards/percentile_query_times.rb +34 -45
- data/app/models/rails_pulse/queries/charts/average_query_times.rb +23 -97
- data/app/models/rails_pulse/queries/tables/index.rb +74 -0
- data/app/models/rails_pulse/query.rb +1 -0
- data/app/models/rails_pulse/requests/charts/average_response_times.rb +23 -84
- data/app/models/rails_pulse/route.rb +1 -6
- data/app/models/rails_pulse/routes/cards/average_response_times.rb +45 -23
- data/app/models/rails_pulse/routes/cards/error_rate_per_route.rb +38 -45
- data/app/models/rails_pulse/routes/cards/percentile_response_times.rb +34 -47
- data/app/models/rails_pulse/routes/cards/request_count_totals.rb +30 -25
- data/app/models/rails_pulse/routes/charts/average_response_times.rb +23 -100
- data/app/models/rails_pulse/routes/tables/index.rb +57 -40
- data/app/models/rails_pulse/summary.rb +143 -0
- data/app/services/rails_pulse/summary_service.rb +199 -0
- data/app/views/layouts/rails_pulse/application.html.erb +4 -4
- data/app/views/rails_pulse/components/_empty_state.html.erb +11 -0
- data/app/views/rails_pulse/components/_metric_card.html.erb +10 -24
- data/app/views/rails_pulse/dashboard/index.html.erb +54 -36
- data/app/views/rails_pulse/queries/_show_table.html.erb +1 -1
- data/app/views/rails_pulse/queries/_table.html.erb +10 -12
- data/app/views/rails_pulse/queries/index.html.erb +41 -34
- data/app/views/rails_pulse/queries/show.html.erb +38 -31
- data/app/views/rails_pulse/requests/_operations.html.erb +32 -26
- data/app/views/rails_pulse/requests/_table.html.erb +1 -3
- data/app/views/rails_pulse/requests/index.html.erb +42 -34
- data/app/views/rails_pulse/routes/_table.html.erb +13 -13
- data/app/views/rails_pulse/routes/index.html.erb +43 -35
- data/app/views/rails_pulse/routes/show.html.erb +42 -35
- data/config/initializers/rails_pulse.rb +0 -12
- data/db/migrate/20241222000001_create_rails_pulse_summaries.rb +54 -0
- data/db/rails_pulse_schema.rb +121 -0
- data/lib/generators/rails_pulse/install_generator.rb +41 -4
- data/lib/generators/rails_pulse/templates/db/rails_pulse_schema.rb +60 -0
- data/lib/generators/rails_pulse/templates/rails_pulse.rb +0 -12
- data/lib/rails_pulse/configuration.rb +0 -11
- data/lib/rails_pulse/engine.rb +0 -1
- data/lib/rails_pulse/version.rb +1 -1
- data/lib/tasks/rails_pulse.rake +58 -0
- data/public/rails-pulse-assets/rails-pulse.css +1 -1
- data/public/rails-pulse-assets/rails-pulse.css.map +1 -1
- data/public/rails-pulse-assets/rails-pulse.js +1 -1
- data/public/rails-pulse-assets/rails-pulse.js.map +3 -3
- data/public/rails-pulse-assets/search.svg +43 -0
- metadata +27 -11
- data/app/controllers/rails_pulse/caches_controller.rb +0 -115
- data/app/helpers/rails_pulse/cached_component_helper.rb +0 -73
- data/app/models/rails_pulse/component_cache_key.rb +0 -33
- data/app/views/rails_pulse/caches/show.html.erb +0 -9
- data/db/migrate/20250227235904_create_routes.rb +0 -12
- data/db/migrate/20250227235915_create_requests.rb +0 -19
- data/db/migrate/20250228000000_create_queries.rb +0 -14
- data/db/migrate/20250228000056_create_operations.rb +0 -24
- data/lib/rails_pulse/migration.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c6b5219e5da388e0ec200a6382527d227ab0e2ee20faa11e3ce8e49992ce6b8
|
4
|
+
data.tar.gz: 1a713357c2ab7eb454b269f358c9b9fb1939eef9e3d17c8cb7cf9a12cab8206c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf37a0c4d1ac19450ae702ee22b4f460d8b16528a00603d1cbb0fbad6ff5d6b8caf1dca90ff9ec0834683f56651af76ec0d14465c244d3e95482c76a8fcf9ccc
|
7
|
+
data.tar.gz: f150bd31c92462f5c1594368816aaaa60fe6e17ec4e4e0cb67d8b0ff7c0732be9d544df040548c6c80050fea6e66d6f21cf960dcec1b4af6bec4cbb4c318bb24
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
**Real-time performance monitoring and debugging for Rails applications**
|
7
7
|
|
8
8
|

|
9
|
-

|
10
10
|

|
11
11
|

|
12
12
|
</div>
|
@@ -25,16 +25,14 @@
|
|
25
25
|
- [Authentication](#authentication)
|
26
26
|
- [Authentication Setup](#authentication-setup)
|
27
27
|
- [Authentication Examples](#authentication-examples)
|
28
|
-
- [Security Considerations](#security-considerations)
|
29
28
|
- [Data Management](#data-management)
|
30
29
|
- [Cleanup Strategies](#cleanup-strategies)
|
31
30
|
- [Cleanup Configuration](#cleanup-configuration)
|
32
31
|
- [Manual Cleanup Operations](#manual-cleanup-operations)
|
33
|
-
|
34
|
-
- [Multiple Database Support](#multiple-database-support)
|
32
|
+
- [Separate Database Support](#separate-database-support)
|
35
33
|
- [Configuration](#configuration)
|
36
34
|
- [Database Configuration](#database-configuration)
|
37
|
-
- [
|
35
|
+
- [Schema Loading](#schema-loading)
|
38
36
|
- [Testing](#testing)
|
39
37
|
- [Technology Stack](#technology-stack)
|
40
38
|
- [Advantages Over Other Solutions](#advantages-over-other-solutions)
|
@@ -44,33 +42,17 @@
|
|
44
42
|
|
45
43
|
## Introduction
|
46
44
|
|
47
|
-
Rails Pulse is a comprehensive performance monitoring and debugging gem that provides
|
48
|
-
|
49
|
-
**Why Rails Pulse?**
|
50
|
-
|
51
|
-
- **Visual**: Beautiful, responsive dashboards with actionable insights
|
52
|
-
- **Comprehensive**: Monitors requests, database queries, and application operations
|
53
|
-
- **Real-time**: Live performance metrics
|
54
|
-
- **Zero Configuration**: Works out of the box with sensible defaults
|
55
|
-
- **Lightweight**: Minimal performance overhead in production
|
56
|
-
- **Asset Independent**: Pre-compiled assets work with any Rails build system
|
57
|
-
- **CSP Compliant**: Secure by default with Content Security Policy support
|
45
|
+
Rails Pulse is a comprehensive performance monitoring and debugging gem that provides insights into your Rails application's health. Built as a Rails Engine, it seamlessly integrates with your existing application to capture, analyze, and visualize performance metrics without impacting your production workload.
|
58
46
|
|
59
47
|
## Features
|
60
48
|
|
61
|
-
###
|
49
|
+
### Performance Monitoring
|
62
50
|
- Interactive dashboard with response time charts and request analytics
|
63
51
|
- SQL query performance tracking with slow query identification
|
64
52
|
- Route-specific metrics with configurable performance thresholds
|
65
53
|
- Week-over-week trend analysis with visual indicators
|
66
54
|
|
67
|
-
###
|
68
|
-
- Content Security Policy (CSP) compliant with pre-compiled assets
|
69
|
-
- Flexible authentication system with multiple authentication methods
|
70
|
-
- Automatic data cleanup with configurable retention policies
|
71
|
-
- Zero build dependencies - works with any Rails setup
|
72
|
-
|
73
|
-
### ⚡ **Developer Experience**
|
55
|
+
### Developer Experience
|
74
56
|
- Zero configuration setup with sensible defaults
|
75
57
|
- Beautiful responsive interface with dark/light mode
|
76
58
|
- Smart caching with minimal performance overhead
|
@@ -102,22 +84,30 @@ Generate the installation files:
|
|
102
84
|
rails generate rails_pulse:install
|
103
85
|
```
|
104
86
|
|
105
|
-
|
87
|
+
Load the database schema:
|
106
88
|
|
107
89
|
```bash
|
108
|
-
rails db:
|
90
|
+
rails db:prepare
|
109
91
|
```
|
110
92
|
|
111
93
|
Add the Rails Pulse route to your application:
|
112
94
|
|
113
95
|
```ruby
|
114
|
-
# config/routes.rb
|
115
96
|
Rails.application.routes.draw do
|
116
97
|
mount RailsPulse::Engine => "/rails_pulse"
|
117
|
-
# ... your other routes
|
118
98
|
end
|
119
99
|
```
|
120
100
|
|
101
|
+
Schedule background jobs:
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
# Schedule to run 5 minutes past every hour
|
105
|
+
RailsPulse::SummaryJob.perform_later
|
106
|
+
|
107
|
+
# Schedule to run daily
|
108
|
+
RailsPulse::CleanupJob.perform_later
|
109
|
+
```
|
110
|
+
|
121
111
|
### Quick Setup
|
122
112
|
|
123
113
|
Rails Pulse automatically starts collecting performance data once installed. Access your monitoring dashboard at:
|
@@ -135,14 +125,14 @@ RailsPulse.configure do |config|
|
|
135
125
|
# Enable or disable Rails Pulse
|
136
126
|
config.enabled = true
|
137
127
|
|
138
|
-
# Set performance thresholds for
|
128
|
+
# Set performance thresholds for route response times (in milliseconds)
|
139
129
|
config.route_thresholds = {
|
140
130
|
slow: 500,
|
141
131
|
very_slow: 1500,
|
142
132
|
critical: 3000
|
143
133
|
}
|
144
134
|
|
145
|
-
# Set performance thresholds for
|
135
|
+
# Set performance thresholds for request response times (in milliseconds)
|
146
136
|
config.request_thresholds = {
|
147
137
|
slow: 700,
|
148
138
|
very_slow: 2000,
|
@@ -157,8 +147,8 @@ RailsPulse.configure do |config|
|
|
157
147
|
}
|
158
148
|
|
159
149
|
# Asset tracking configuration
|
160
|
-
config.track_assets = false
|
161
|
-
config.custom_asset_patterns = []
|
150
|
+
config.track_assets = false # Ignore asset requests by default
|
151
|
+
config.custom_asset_patterns = [] # Additional asset patterns to ignore
|
162
152
|
|
163
153
|
# Rails Pulse mount path (optional)
|
164
154
|
# Specify if Rails Pulse is mounted at a custom path to prevent self-tracking
|
@@ -171,7 +161,7 @@ RailsPulse.configure do |config|
|
|
171
161
|
|
172
162
|
# Data cleanup
|
173
163
|
config.archiving_enabled = true # Enable automatic cleanup
|
174
|
-
config.full_retention_period = 2.weeks
|
164
|
+
config.full_retention_period = 2.weeks # Delete records older than this
|
175
165
|
config.max_table_records = { # Maximum records per table
|
176
166
|
rails_pulse_requests: 10000,
|
177
167
|
rails_pulse_operations: 50000,
|
@@ -179,10 +169,6 @@ RailsPulse.configure do |config|
|
|
179
169
|
rails_pulse_queries: 500
|
180
170
|
}
|
181
171
|
|
182
|
-
# Metric caching for performance
|
183
|
-
config.component_cache_enabled = true
|
184
|
-
config.component_cache_duration = 1.day
|
185
|
-
|
186
172
|
# Multiple database support (optional)
|
187
173
|
# Uncomment to store Rails Pulse data in a separate database
|
188
174
|
# config.connects_to = {
|
@@ -219,7 +205,7 @@ end
|
|
219
205
|
|
220
206
|
Rails Pulse works with any authentication system. Here are common patterns:
|
221
207
|
|
222
|
-
####
|
208
|
+
#### Devise with Admin Role
|
223
209
|
|
224
210
|
```ruby
|
225
211
|
config.authentication_method = proc {
|
@@ -229,36 +215,7 @@ config.authentication_method = proc {
|
|
229
215
|
}
|
230
216
|
```
|
231
217
|
|
232
|
-
####
|
233
|
-
|
234
|
-
```ruby
|
235
|
-
config.authentication_method = proc {
|
236
|
-
unless session[:user_id] && User.find_by(id: session[:user_id])&.admin?
|
237
|
-
redirect_to main_app.login_path, alert: "Please log in as an admin"
|
238
|
-
end
|
239
|
-
}
|
240
|
-
```
|
241
|
-
|
242
|
-
#### **HTTP Basic Authentication**
|
243
|
-
|
244
|
-
```ruby
|
245
|
-
config.authentication_method = proc {
|
246
|
-
authenticate_or_request_with_http_basic do |username, password|
|
247
|
-
username == ENV['RAILS_PULSE_USERNAME'] &&
|
248
|
-
password == ENV['RAILS_PULSE_PASSWORD']
|
249
|
-
end
|
250
|
-
}
|
251
|
-
```
|
252
|
-
|
253
|
-
#### **Warden Authentication**
|
254
|
-
|
255
|
-
```ruby
|
256
|
-
config.authentication_method = proc {
|
257
|
-
warden.authenticate!(scope: :admin)
|
258
|
-
}
|
259
|
-
```
|
260
|
-
|
261
|
-
#### **Custom Authorization Logic**
|
218
|
+
#### Custom Authorization Logic
|
262
219
|
|
263
220
|
```ruby
|
264
221
|
config.authentication_method = proc {
|
@@ -269,16 +226,6 @@ config.authentication_method = proc {
|
|
269
226
|
}
|
270
227
|
```
|
271
228
|
|
272
|
-
### Security Considerations
|
273
|
-
|
274
|
-
- **Production Security**: Always enable authentication in production environments
|
275
|
-
- **Admin-only Access**: Limit access to administrators or authorized personnel
|
276
|
-
- **Environment Variables**: Use environment variables for credentials, never hardcode
|
277
|
-
- **HTTPS Required**: Always use HTTPS in production when authentication is enabled
|
278
|
-
- **Regular Access Review**: Periodically review who has access to monitoring data
|
279
|
-
|
280
|
-
**Important**: The authentication method runs in the context of the Rails Pulse ApplicationController, giving you access to all standard Rails controller methods like `redirect_to`, `render`, `session`, and any methods from your host application's authentication system.
|
281
|
-
|
282
229
|
## Data Management
|
283
230
|
|
284
231
|
Rails Pulse provides data cleanup to prevent your monitoring database from growing indefinitely while preserving essential performance insights.
|
@@ -329,28 +276,22 @@ rails rails_pulse:cleanup_stats
|
|
329
276
|
|
330
277
|
**Schedule automated cleanup:**
|
331
278
|
```ruby
|
332
|
-
# Using whenever gem or similar scheduler
|
333
279
|
RailsPulse::CleanupJob.perform_later
|
334
280
|
```
|
335
281
|
|
336
|
-
|
282
|
+
## Separate Database Support
|
337
283
|
|
338
|
-
|
339
|
-
2. **Count-based Phase**: If tables still exceed limits, delete oldest remaining records
|
340
|
-
3. **Safe Deletion**: Respects foreign key constraints (operations → requests → queries/routes)
|
341
|
-
4. **Comprehensive Logging**: Detailed cleanup statistics and operation logs
|
284
|
+
Rails Pulse supports storing performance monitoring data in a **separate database**. By default, Rails Pulse stores data in your main application database alongside your existing tables.
|
342
285
|
|
343
|
-
|
344
|
-
|
345
|
-
## Multiple Database Support
|
346
|
-
|
347
|
-
Rails Pulse supports storing performance monitoring data in a separate database. This is particularly useful for:
|
286
|
+
Use a separate database when you want:
|
348
287
|
|
349
288
|
- **Isolating monitoring data** from your main application database
|
350
289
|
- **Using different database engines** optimized for time-series data
|
351
290
|
- **Scaling monitoring independently** from your application
|
352
291
|
- **Simplified backup strategies** with separate retention policies
|
353
292
|
|
293
|
+
**For shared database setup (default)**, no database configuration is needed - simply run `rails db:prepare` after installation.
|
294
|
+
|
354
295
|
### Configuration
|
355
296
|
|
356
297
|
To use a separate database, configure the `connects_to` option in your Rails Pulse initializer:
|
@@ -380,6 +321,7 @@ production:
|
|
380
321
|
rails_pulse:
|
381
322
|
adapter: sqlite3
|
382
323
|
database: storage/rails_pulse_production.sqlite3
|
324
|
+
migrations_paths: db/rails_pulse_migrate
|
383
325
|
pool: 5
|
384
326
|
timeout: 5000
|
385
327
|
|
@@ -392,6 +334,7 @@ production:
|
|
392
334
|
username: rails_pulse_user
|
393
335
|
password: <%= Rails.application.credentials.dig(:rails_pulse, :database_password) %>
|
394
336
|
host: localhost
|
337
|
+
migrations_paths: db/rails_pulse_migrate
|
395
338
|
pool: 5
|
396
339
|
|
397
340
|
# For MySQL
|
@@ -403,35 +346,31 @@ production:
|
|
403
346
|
username: rails_pulse_user
|
404
347
|
password: <%= Rails.application.credentials.dig(:rails_pulse, :database_password) %>
|
405
348
|
host: localhost
|
349
|
+
migrations_paths: db/rails_pulse_migrate
|
406
350
|
pool: 5
|
407
351
|
```
|
408
352
|
|
409
|
-
###
|
353
|
+
### Schema Loading
|
410
354
|
|
411
|
-
|
355
|
+
After installation, load the Rails Pulse database schema:
|
412
356
|
|
413
357
|
```bash
|
414
|
-
|
415
|
-
rails db:migrate
|
416
|
-
|
417
|
-
# If you need to run migrations on a specific database
|
418
|
-
RAILS_ENV=production rails db:migrate
|
358
|
+
rails db:prepare
|
419
359
|
```
|
420
360
|
|
421
|
-
|
361
|
+
This command works for both:
|
362
|
+
- Shared database setup (default): Loads tables into your main application database
|
363
|
+
- Separate database setup: Automatically loads tables into your configured Rails Pulse database
|
422
364
|
|
423
365
|
## Testing
|
424
366
|
|
425
|
-
Rails Pulse includes a comprehensive test suite designed for speed and reliability across multiple databases
|
367
|
+
Rails Pulse includes a comprehensive test suite designed for speed and reliability across multiple databases and Rails versions.
|
426
368
|
|
427
369
|
### Running the Complete Test Suite
|
428
370
|
|
429
371
|
```bash
|
430
|
-
# Run all tests (unit, functional, integration)
|
372
|
+
# Run all tests (unit, functional, integration, instrumentation)
|
431
373
|
rails test:all
|
432
|
-
|
433
|
-
# Run tests with speed optimizations
|
434
|
-
rails test:fast
|
435
374
|
```
|
436
375
|
|
437
376
|
### Running Individual Test Types
|
@@ -452,83 +391,46 @@ rails test:integration
|
|
452
391
|
```bash
|
453
392
|
# Run a specific test file
|
454
393
|
rails test test/models/rails_pulse/request_test.rb
|
455
|
-
|
456
|
-
# Run controller tests
|
457
|
-
rails test test/controllers/rails_pulse/dashboard_controller_test.rb
|
458
|
-
|
459
|
-
# Run helper tests
|
460
|
-
rails test test/helpers/rails_pulse/application_helper_test.rb
|
461
|
-
|
462
|
-
# Run factory verification tests
|
463
|
-
rails test test/factories_test.rb
|
464
394
|
```
|
465
395
|
|
466
|
-
### Multi-Rails Version Testing
|
396
|
+
### Multi-Database and Rails Version Testing
|
467
397
|
|
468
|
-
Test against multiple Rails versions using
|
398
|
+
Test against multiple databases and Rails versions using the matrix task:
|
469
399
|
|
470
400
|
```bash
|
471
|
-
#
|
472
|
-
|
473
|
-
|
474
|
-
# Run tests against all Rails versions
|
475
|
-
bundle exec appraisal rails test:all
|
476
|
-
|
477
|
-
# Run tests against specific Rails version
|
478
|
-
bundle exec appraisal rails-7-1 rails test:unit
|
401
|
+
# Test all database and Rails version combinations locally
|
402
|
+
rails test:matrix
|
479
403
|
```
|
480
404
|
|
481
|
-
|
482
|
-
|
483
|
-
- **
|
484
|
-
- **Transaction rollback**: Tests use database transactions for fast cleanup
|
485
|
-
- **Stubbed dependencies**: External calls and expensive operations are stubbed
|
486
|
-
- **Parallel execution**: Tests run in parallel when supported
|
487
|
-
|
488
|
-
### Database Testing
|
489
|
-
|
490
|
-
Rails Pulse supports testing with multiple database adapters using simplified Rake tasks:
|
491
|
-
|
492
|
-
```bash
|
493
|
-
# Quick Commands (Recommended)
|
494
|
-
rails test:sqlite # Test with SQLite (default)
|
495
|
-
rails test:postgresql # Test with PostgreSQL
|
496
|
-
rails test:mysql # Test with MySQL
|
497
|
-
|
498
|
-
# Test Matrix (before pushing)
|
499
|
-
rails test:matrix # Test SQLite + PostgreSQL
|
500
|
-
rails test:matrix_full # Test all databases (SQLite + PostgreSQL + MySQL)
|
501
|
-
```
|
405
|
+
This command tests all combinations locally:
|
406
|
+
- **Databases**: SQLite3, PostgreSQL, MySQL2 (local testing only)
|
407
|
+
- **Rails versions**: 7.2, 8.0
|
502
408
|
|
503
|
-
|
409
|
+
**Note**: CI only tests SQLite3 + PostgreSQL for reliability. MySQL is available for local testing but excluded from CI due to flakiness.
|
504
410
|
|
505
|
-
|
506
|
-
```bash
|
507
|
-
./scripts/setup-git-hooks
|
508
|
-
```
|
509
|
-
This installs a pre-commit hook that runs RuboCop before each commit.
|
411
|
+
### Development Environment Setup
|
510
412
|
|
511
|
-
|
413
|
+
1. **Copy the environment template:**
|
512
414
|
```bash
|
513
415
|
cp .env.example .env
|
514
416
|
```
|
515
417
|
|
516
|
-
|
418
|
+
2. **Configure your database credentials in `.env` (for local multi-database testing):**
|
517
419
|
```bash
|
518
|
-
# PostgreSQL Configuration
|
420
|
+
# PostgreSQL Configuration (used in CI + local)
|
519
421
|
POSTGRES_USERNAME=your_username
|
520
422
|
POSTGRES_PASSWORD=your_password
|
521
423
|
POSTGRES_HOST=localhost
|
522
424
|
POSTGRES_PORT=5432
|
523
425
|
|
524
|
-
# MySQL Configuration
|
426
|
+
# MySQL Configuration (local testing only)
|
525
427
|
MYSQL_USERNAME=root
|
526
428
|
MYSQL_PASSWORD=your_password
|
527
429
|
MYSQL_HOST=localhost
|
528
430
|
MYSQL_PORT=3306
|
529
431
|
```
|
530
432
|
|
531
|
-
|
433
|
+
3. **Create test databases:**
|
532
434
|
```bash
|
533
435
|
# PostgreSQL
|
534
436
|
createdb rails_pulse_test
|
@@ -537,32 +439,32 @@ rails test:matrix_full # Test all databases (SQLite + PostgreSQL + MySQL)
|
|
537
439
|
mysql -u root -p -e "CREATE DATABASE rails_pulse_test;"
|
538
440
|
```
|
539
441
|
|
540
|
-
|
541
|
-
|
442
|
+
### Manual Database Testing
|
443
|
+
|
444
|
+
Test individual databases locally:
|
542
445
|
|
543
446
|
```bash
|
544
|
-
# Test with SQLite (default
|
447
|
+
# Test with SQLite (default)
|
545
448
|
rails test:all
|
546
449
|
|
547
|
-
# Test with PostgreSQL
|
548
|
-
|
450
|
+
# Test with PostgreSQL
|
451
|
+
DB=postgresql FORCE_DB_CONFIG=true rails test:all
|
549
452
|
|
550
|
-
# Test with MySQL (
|
551
|
-
|
453
|
+
# Test with MySQL (local only)
|
454
|
+
DB=mysql2 FORCE_DB_CONFIG=true rails test:all
|
552
455
|
```
|
553
456
|
|
554
|
-
|
457
|
+
### CI Testing
|
555
458
|
|
556
|
-
|
557
|
-
-
|
558
|
-
-
|
559
|
-
-
|
459
|
+
GitHub Actions CI automatically tests:
|
460
|
+
- **Databases**: SQLite3, PostgreSQL only (MySQL excluded for reliability)
|
461
|
+
- **Rails versions**: 7.2, 8.0
|
462
|
+
- **Environment**: Uses memory SQLite and PostgreSQL service
|
560
463
|
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
```
|
464
|
+
**Local vs CI differences**:
|
465
|
+
- **Local**: Can test all 3 databases (SQLite3, PostgreSQL, MySQL2)
|
466
|
+
- **CI**: Only SQLite3 + PostgreSQL for fast, reliable builds
|
467
|
+
- **Database switching**: Requires `FORCE_DB_CONFIG=true` locally
|
566
468
|
|
567
469
|
## Technology Stack
|
568
470
|
|
@@ -602,12 +504,6 @@ Rails Pulse is built using modern, battle-tested technologies that ensure reliab
|
|
602
504
|
- **Customizable**: Full control over metrics, thresholds, and interface
|
603
505
|
- **Asset Independence**: Works with any Rails build system (Sprockets, esbuild, Webpack, Vite)
|
604
506
|
|
605
|
-
### **vs. Built-in Rails Logging**
|
606
|
-
- **Visual Interface**: Beautiful dashboards instead of log parsing
|
607
|
-
- **Structured Data**: Queryable metrics instead of text logs
|
608
|
-
- **Historical Analysis**: Persistent storage with trend analysis
|
609
|
-
- **Real-time Monitoring**: Live updates and health scoring
|
610
|
-
|
611
507
|
### **vs. Custom Monitoring Solutions**
|
612
508
|
- **Batteries Included**: Complete monitoring solution out of the box
|
613
509
|
- **Proven Architecture**: Built on Rails best practices
|
data/Rakefile
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require "bundler/setup"
|
2
2
|
|
3
|
+
# Load environment variables from .env file
|
4
|
+
require "dotenv/load" if File.exist?(".env")
|
5
|
+
|
3
6
|
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
4
7
|
load "rails/tasks/engine.rake"
|
5
8
|
load "rails/tasks/statistics.rake"
|
@@ -8,9 +11,9 @@ require "bundler/gem_tasks"
|
|
8
11
|
|
9
12
|
# Test tasks
|
10
13
|
namespace :test do
|
11
|
-
desc "Run unit tests (models, helpers, services)"
|
14
|
+
desc "Run unit tests (models, helpers, services, instrumentation)"
|
12
15
|
task :unit do
|
13
|
-
sh "rails test test/models test/helpers test/services test/support"
|
16
|
+
sh "rails test test/models test/helpers test/services test/support test/instrumentation"
|
14
17
|
end
|
15
18
|
|
16
19
|
desc "Run functional tests (controllers)"
|
@@ -27,6 +30,78 @@ namespace :test do
|
|
27
30
|
task :all do
|
28
31
|
sh "rails test"
|
29
32
|
end
|
33
|
+
|
34
|
+
desc "Run tests across all database and Rails version combinations (local only - CI uses sqlite3 + postgresql)"
|
35
|
+
task :matrix do
|
36
|
+
databases = [ "sqlite3", "postgresql", "mysql2" ]
|
37
|
+
rails_versions = [ "rails-7-2", "rails-8-0" ]
|
38
|
+
|
39
|
+
failed_combinations = []
|
40
|
+
|
41
|
+
databases.each do |database|
|
42
|
+
rails_versions.each do |rails_version|
|
43
|
+
puts "\n" + "=" * 80
|
44
|
+
puts "🧪 Local Testing: #{database.upcase} + #{rails_version.upcase}"
|
45
|
+
puts "(CI only tests SQLite3 + PostgreSQL for reliability)"
|
46
|
+
puts "=" * 80
|
47
|
+
|
48
|
+
begin
|
49
|
+
gemfile = "gemfiles/#{rails_version.gsub('-', '_')}.gemfile"
|
50
|
+
|
51
|
+
# Set environment variables
|
52
|
+
env_vars = {
|
53
|
+
"DB" => database,
|
54
|
+
"BUNDLE_GEMFILE" => gemfile,
|
55
|
+
"FORCE_DB_CONFIG" => "true"
|
56
|
+
}
|
57
|
+
|
58
|
+
# Add database-specific environment variables
|
59
|
+
case database
|
60
|
+
when "postgresql"
|
61
|
+
env_vars.merge!({
|
62
|
+
"POSTGRES_USERNAME" => ENV.fetch("POSTGRES_USERNAME", "postgres"),
|
63
|
+
"POSTGRES_PASSWORD" => ENV.fetch("POSTGRES_PASSWORD", ""),
|
64
|
+
"POSTGRES_HOST" => ENV.fetch("POSTGRES_HOST", "localhost"),
|
65
|
+
"POSTGRES_PORT" => ENV.fetch("POSTGRES_PORT", "5432")
|
66
|
+
})
|
67
|
+
when "mysql2"
|
68
|
+
env_vars.merge!({
|
69
|
+
"MYSQL_USERNAME" => ENV.fetch("MYSQL_USERNAME", "root"),
|
70
|
+
"MYSQL_PASSWORD" => ENV.fetch("MYSQL_PASSWORD", "password"),
|
71
|
+
"MYSQL_HOST" => ENV.fetch("MYSQL_HOST", "localhost"),
|
72
|
+
"MYSQL_PORT" => ENV.fetch("MYSQL_PORT", "3306")
|
73
|
+
})
|
74
|
+
end
|
75
|
+
|
76
|
+
# Build environment string
|
77
|
+
env_string = env_vars.map { |k, v| "#{k}=#{v}" }.join(" ")
|
78
|
+
|
79
|
+
# Run the test command
|
80
|
+
sh "#{env_string} bundle exec rails test:all"
|
81
|
+
|
82
|
+
puts "✅ PASSED: #{database} + #{rails_version}"
|
83
|
+
|
84
|
+
rescue => e
|
85
|
+
puts "❌ FAILED: #{database} + #{rails_version}"
|
86
|
+
puts "Error: #{e.message}"
|
87
|
+
failed_combinations << "#{database} + #{rails_version}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
puts "\n" + "=" * 80
|
93
|
+
puts "🏁 Local Test Matrix Results"
|
94
|
+
puts "(CI automatically tests SQLite3 + PostgreSQL only)"
|
95
|
+
puts "=" * 80
|
96
|
+
|
97
|
+
if failed_combinations.empty?
|
98
|
+
puts "✅ All combinations passed!"
|
99
|
+
else
|
100
|
+
puts "❌ Failed combinations:"
|
101
|
+
failed_combinations.each { |combo| puts " - #{combo}" }
|
102
|
+
exit 1
|
103
|
+
end
|
104
|
+
end
|
30
105
|
end
|
31
106
|
|
32
107
|
# Override default test task
|
@@ -80,18 +80,6 @@ a:hover {
|
|
80
80
|
position:absolute;
|
81
81
|
top:0
|
82
82
|
}
|
83
|
-
.bar.db {
|
84
|
-
background-color:#92c282
|
85
|
-
}
|
86
|
-
.bar.app {
|
87
|
-
background-color:#00adc4
|
88
|
-
}
|
89
|
-
.bar.gc {
|
90
|
-
background-color:#323333
|
91
|
-
}
|
92
|
-
.bar.view {
|
93
|
-
background-color:#b48da3
|
94
|
-
}
|
95
83
|
.bar:first-child {
|
96
84
|
border-bottom-left-radius:1px;
|
97
85
|
border-top-left-radius:1px
|
@@ -16,14 +16,17 @@ module ChartTableConcern
|
|
16
16
|
def setup_chart_and_table_data
|
17
17
|
ransack_params = params[:q] || {}
|
18
18
|
|
19
|
-
# Setup chart data first using original time range (no sorting from table)
|
20
19
|
unless turbo_frame_request?
|
21
|
-
|
20
|
+
# Setup chart data first using original time range (no sorting from table)
|
22
21
|
setup_chart_data(ransack_params)
|
22
|
+
setup_chart_formatters
|
23
23
|
end
|
24
24
|
|
25
25
|
# Setup table data using zoom parameters if present, otherwise use chart parameters
|
26
26
|
setup_table_data(ransack_params)
|
27
|
+
|
28
|
+
# Set flag to determine if we have meaningful data to display
|
29
|
+
@has_data = has_meaningful_data?
|
27
30
|
end
|
28
31
|
|
29
32
|
def setup_chart_data(ransack_params)
|
@@ -31,7 +34,10 @@ module ChartTableConcern
|
|
31
34
|
chart_ransack_query = chart_model.ransack(chart_ransack_params)
|
32
35
|
@chart_data = chart_class.new(
|
33
36
|
ransack_query: chart_ransack_query,
|
34
|
-
|
37
|
+
period_type: period_type,
|
38
|
+
start_time: @start_time,
|
39
|
+
end_time: @end_time,
|
40
|
+
start_duration: @start_duration,
|
35
41
|
**chart_options
|
36
42
|
).to_rails_chart
|
37
43
|
end
|
@@ -43,6 +49,7 @@ module ChartTableConcern
|
|
43
49
|
|
44
50
|
table_results = build_table_results
|
45
51
|
handle_pagination
|
52
|
+
|
46
53
|
@pagy, @table_data = pagy(table_results, limit: session_pagination_limit)
|
47
54
|
end
|
48
55
|
|
@@ -56,14 +63,24 @@ module ChartTableConcern
|
|
56
63
|
end
|
57
64
|
|
58
65
|
def setup_chart_formatters
|
59
|
-
@xaxis_formatter = RailsPulse::ChartFormatters.
|
66
|
+
@xaxis_formatter = RailsPulse::ChartFormatters.period_as_time_or_date(@time_diff_hours)
|
60
67
|
@tooltip_formatter = RailsPulse::ChartFormatters.tooltip_as_time_or_date_with_marker(@time_diff_hours)
|
61
68
|
end
|
62
69
|
|
70
|
+
def period_type
|
71
|
+
@time_diff_hours <= 25 ? :hour : :day
|
72
|
+
end
|
73
|
+
|
63
74
|
def group_by
|
64
75
|
@time_diff_hours <= 25 ? :group_by_hour : :group_by_day
|
65
76
|
end
|
66
77
|
|
78
|
+
def has_meaningful_data?
|
79
|
+
has_chart_data = @chart_data && @chart_data.values.any? { |v| v > 0 }
|
80
|
+
has_table_data = @table_data && @table_data.any?
|
81
|
+
has_chart_data || has_table_data
|
82
|
+
end
|
83
|
+
|
67
84
|
def handle_pagination
|
68
85
|
method = pagination_method
|
69
86
|
send(method, params[:limit]) if params[:limit].present?
|
@@ -5,10 +5,13 @@ module ResponseRangeConcern
|
|
5
5
|
ransack_params = params[:q] || {}
|
6
6
|
thresholds = RailsPulse.configuration.public_send("#{type}_thresholds")
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
# Check both avg_duration (for Summary) and duration (for Request/Operation)
|
9
|
+
duration_param = ransack_params[:avg_duration] || ransack_params[:duration]
|
10
|
+
|
11
|
+
if duration_param.present?
|
12
|
+
selected_range = duration_param
|
10
13
|
start_duration =
|
11
|
-
case
|
14
|
+
case duration_param.to_sym
|
12
15
|
when :slow then thresholds[:slow]
|
13
16
|
when :very_slow then thresholds[:very_slow]
|
14
17
|
when :critical then thresholds[:critical]
|