rails_pulse 0.3.0.pre.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc9e30553a044b4643c309e84d8050ae5c18f23058a8a2a78150c3b299df4008
4
- data.tar.gz: af91f5fed9c14e313044dd0c679ea3a26fcec60bb20e7236d1e405ebffc6da2f
3
+ metadata.gz: 73e953ecde75c821f2b087aa65de061abcb05b817afa5a1bbaa2c0493bae2f7a
4
+ data.tar.gz: 405125c84023db9fe95cc614c0786fb9ead5601f6085987e0acdf22913e5ff38
5
5
  SHA512:
6
- metadata.gz: dd090f8858ee591ee03448c98db3beeea3e9c6a7fd3b16267a955f5378f17098c922e8da2bf603e1aedd96cc937fd63e23679695eaa2dbd6e7ab92e021dd0bb3
7
- data.tar.gz: d97a700ec52614de349776f941b3e61bd732549822c6f78528297261cd2ad59cdf5b4e0b3ecf5c201a4e6e49738247a0dba648344bc37148b2011d41a9db5cfa
6
+ metadata.gz: a3d92ff1d329fd5e08cc874e048520acc4b57400cdb7a3c14cfd67eee3b3d970758f74f978b1ec6325f19b4112c68a60eee5a4b8ec26195c604ad03f25f85a50
7
+ data.tar.gz: 4536029a848aaf8ccec5b852b8c3d14ae1a7948282b5282809793134b75c3869c37a8867d137918b86107ebcadde16f2a85b4dca00aacc36203cfc37c5b2a9b2
data/README.md CHANGED
@@ -1,133 +1,49 @@
1
1
  <div align="center">
2
2
  <img src="app/assets/images/rails_pulse/rails-pulse-logo.png" alt="Rails Pulse" width="200" />
3
-
4
- # Rails Pulse
5
-
6
- **Real-time performance monitoring and debugging for Rails applications**
7
-
8
- ![Gem Version](https://img.shields.io/gem/v/rails_pulse)
9
- ![Rails Version](https://img.shields.io/badge/Rails-7.2+-8.1-blue)
10
- ![License](https://img.shields.io/badge/License-MIT-green)
11
- ![Ruby Version](https://img.shields.io/badge/Ruby-3.0+-red)
12
3
  </div>
13
4
 
14
5
  ---
15
6
 
16
- ## Table of Contents
17
-
18
- - [Introduction](#introduction)
19
- - [Features](#features)
20
- - [Screenshots](#screenshots)
21
- - [Getting Started](#getting-started)
22
- - [Installation](#installation)
23
- - [Quick Setup](#quick-setup)
24
- - [Basic Configuration](#basic-configuration)
25
- - [Background Job Monitoring](#background-job-monitoring)
26
- - [Overview](#overview)
27
- - [Supported Adapters](#supported-adapters)
28
- - [Job Tracking Configuration](#job-tracking-configuration)
29
- - [Privacy & Security](#privacy--security)
30
- - [Authentication](#authentication)
31
- - [Authentication Setup](#authentication-setup)
32
- - [Authentication Examples](#authentication-examples)
33
- - [Tagging System](#tagging-system)
34
- - [Configuring Tags](#configuring-tags)
35
- - [Using Tags](#using-tags)
36
- - [Filtering by Tags](#filtering-by-tags)
37
- - [Data Management](#data-management)
38
- - [Cleanup Strategies](#cleanup-strategies)
39
- - [Cleanup Configuration](#cleanup-configuration)
40
- - [Manual Cleanup Operations](#manual-cleanup-operations)
41
- - [Separate Database Support](#separate-database-support)
42
- - [Configuration](#configuration)
43
- - [Database Configuration](#database-configuration)
44
- - [Schema Loading](#schema-loading)
45
- - [Performance Impact](#performance-impact)
46
- - [Standalone Dashboard Deployment](#standalone-dashboard-deployment)
47
- - [Testing](#testing)
48
- - [Technology Stack](#technology-stack)
49
- - [Advantages Over Other Solutions](#advantages-over-other-solutions)
50
- - [License](#license)
7
+ # Rails Pulse
51
8
 
52
- ---
9
+ **Self-hosted performance monitoring for Rails apps**
53
10
 
54
- ## Introduction
11
+ ![Gem Version](https://img.shields.io/gem/v/rails_pulse)
12
+ ![Rails Version](https://img.shields.io/badge/Rails-7.2%2B-blue)
13
+ ![License](https://img.shields.io/badge/License-MIT-green)
14
+ ![Ruby Version](https://img.shields.io/badge/Ruby-3.0%2B-red)
55
15
 
56
- 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.
16
+ Rails Pulse is a Rails engine that monitors your app's performance from the inside. It tracks slow requests, N+1 queries, SQL performance, and background jobs. All data stays in your own database, no third-party cloud, no SaaS subscription, no data leaving your servers.
57
17
 
58
- ## Features
59
-
60
- ### Performance Monitoring
61
- - Interactive dashboard with response time charts and request analytics
62
- - SQL query performance tracking with slow query identification
63
- - Route-specific metrics with configurable performance thresholds
64
- - **Background job monitoring** with execution tracking and failure analysis
65
- - Week-over-week trend analysis with visual indicators
66
-
67
- ### Background Job Tracking
68
- - **Universal job tracking** compatible with all ActiveJob adapters
69
- - Monitor job performance, failures, and retries
70
- - Track individual job executions with detailed metrics
71
- - View operations and SQL queries executed during jobs
72
- - Configurable privacy controls for job arguments
73
-
74
- ### Developer Experience
75
- - Zero configuration setup with sensible defaults
76
- - Beautiful responsive interface with dark/light mode
77
- - Smart caching with minimal performance overhead
78
- - Multiple database support (SQLite, PostgreSQL, MySQL)
79
-
80
- ### Organization & Filtering
81
- - Flexible tagging system for routes, requests, queries, and jobs
82
- - Filter performance data by custom tags
83
- - Organize monitoring data by environment, priority, or custom categories
84
-
85
- ## Screenshots
86
-
87
- <table>
18
+ <table border="0">
19
+ <tr border="0" style="border:0">
20
+ <td border="0" style="border:0">
21
+ <img src="app/assets/images/rails_pulse/dashboard.png" alt="Dashboard" width="400" /></td>
22
+ <td style="border:0"><img src="app/assets/images/rails_pulse/request-show.png" alt="Request detail" width="400" /></td>
23
+ </tr>
88
24
  <tr>
89
- <td><img src="app/assets/images/rails_pulse/dashboard.png" alt="Rails Pulse Dashboard" width="400" /></td>
90
- <td><img src="app/assets/images/rails_pulse/request.png" alt="Rails Pulse Requests" width="400" /></td>
25
+ <td style="border:0"><img src="app/assets/images/rails_pulse/query-show.png" alt="Query detail" width="400" /></td>
26
+ <td style="border:0"><img src="app/assets/images/rails_pulse/route-show.png" alt="Route detail" width="400" /></td>
91
27
  </tr>
92
28
  </table>
93
29
 
30
+ ## Installation
94
31
 
95
- ## Getting Started
96
-
97
- ### Installation
98
-
99
- Add Rails Pulse to your application's Gemfile:
32
+ Add to your Gemfile:
100
33
 
101
34
  ```ruby
102
35
  gem 'rails_pulse'
103
36
  ```
104
37
 
105
- Install the gem:
38
+ Run the installer:
106
39
 
107
40
  ```bash
108
41
  bundle install
109
- ```
110
-
111
- Generate the installation files:
112
-
113
- ```bash
114
- # Install with single database setup (default - recommended)
115
42
  rails generate rails_pulse:install
116
-
117
- # Or install with separate database setup
118
- rails generate rails_pulse:install --database=separate
119
- ```
120
-
121
- **For single database setup (default):**
122
- ```bash
123
- rails db:migrate # Creates Rails Pulse tables in your main database
43
+ rails db:migrate
124
44
  ```
125
45
 
126
- **For separate database setup:**
127
- 1. Configure `config/database.yml` with your Rails Pulse database settings
128
- 2. Run: `rails db:prepare` to create and load the schema
129
-
130
- Add the Rails Pulse route to your application:
46
+ Mount the dashboard in `config/routes.rb`:
131
47
 
132
48
  ```ruby
133
49
  Rails.application.routes.draw do
@@ -135,757 +51,74 @@ Rails.application.routes.draw do
135
51
  end
136
52
  ```
137
53
 
138
- Schedule background jobs:
54
+ Schedule the background jobs:
139
55
 
140
56
  ```ruby
141
- # Schedule to run 5 minutes past every hour. cron: 5 * * * *
142
- RailsPulse::SummaryJob.perform_later
143
-
144
- # Schedule to run daily. cron: 0 1 * * *
145
- RailsPulse::CleanupJob.perform_later
57
+ RailsPulse::SummaryJob.perform_later # cron: 5 * * * *
58
+ RailsPulse::CleanupJob.perform_later # cron: 0 1 * * *
146
59
  ```
147
60
 
148
- ### Quick Setup
61
+ Your dashboard is now at `http://localhost:3000/rails_pulse`.
149
62
 
150
- Rails Pulse automatically starts collecting performance data once installed. Access your monitoring dashboard at:
63
+ Full install guide: [railspulse.com/documentation/installation](https://railspulse.com/documentation/installation)
151
64
 
152
- ```
153
- http://localhost:3000/rails_pulse
154
- ```
155
-
156
- **Database Setup:**
157
- - **Single Database (default)**: Rails Pulse tables are created in your main database - no additional configuration needed
158
- - **Separate Database**: See the [Separate Database Support](#separate-database-support) section for setup instructions
65
+ Separate database setup: [railspulse.com/documentation/database](https://railspulse.com/documentation/database)
159
66
 
160
- ### Basic Configuration
67
+ ## Configuration
161
68
 
162
- Customize Rails Pulse in `config/initializers/rails_pulse.rb`:
69
+ Rails Pulse works out of the box with sensible defaults. To customise, edit `config/initializers/rails_pulse.rb`:
163
70
 
164
71
  ```ruby
165
72
  RailsPulse.configure do |config|
166
- # Enable or disable Rails Pulse
167
73
  config.enabled = true
168
74
 
169
- # Set performance thresholds for route response times (in milliseconds)
170
- config.route_thresholds = {
171
- slow: 500,
172
- very_slow: 1500,
173
- critical: 3000
174
- }
175
-
176
- # Set performance thresholds for request response times (in milliseconds)
177
75
  config.request_thresholds = {
178
76
  slow: 700,
179
77
  very_slow: 2000,
180
78
  critical: 4000
181
79
  }
182
80
 
183
- # Set performance thresholds for database queries (in milliseconds)
184
- config.query_thresholds = {
185
- slow: 100,
186
- very_slow: 500,
187
- critical: 1000
188
- }
189
-
190
- # Set performance thresholds for background jobs (in milliseconds)
191
- config.job_thresholds = {
192
- slow: 5_000, # 5 seconds
193
- very_slow: 30_000, # 30 seconds
194
- critical: 60_000 # 1 minute
195
- }
196
-
197
- # Asset tracking configuration
198
- config.track_assets = false # Ignore asset requests by default
199
- config.custom_asset_patterns = [] # Additional asset patterns to ignore
200
-
201
- # Job tracking configuration
202
- config.track_jobs = true # Enable background job tracking
203
- config.capture_job_arguments = false # Disable argument capture for privacy
204
-
205
- # Rails Pulse mount path (optional)
206
- # Specify if Rails Pulse is mounted at a custom path to prevent self-tracking
207
- config.mount_path = nil # e.g., "/admin/monitoring"
208
-
209
- # Route filtering - ignore specific routes from performance tracking
210
- config.ignored_routes = [] # Array of strings or regex patterns
211
- config.ignored_requests = [] # Array of request patterns to ignore
212
- config.ignored_queries = [] # Array of query patterns to ignore
213
- config.ignored_jobs = [] # Array of job class names to ignore
214
- config.ignored_queues = [] # Array of queue names to ignore
215
-
216
- # Tagging system - define available tags for categorizing performance data
217
- config.tags = ["production", "staging", "critical", "needs-optimization"]
218
-
219
- # Data cleanup
220
- config.archiving_enabled = true # Enable automatic cleanup
221
- config.full_retention_period = 30.days # Delete records older than this
222
- config.max_table_records = { # Maximum records per table
223
- rails_pulse_operations: 100_000,
224
- rails_pulse_requests: 50_000,
225
- rails_pulse_job_runs: 50_000,
226
- rails_pulse_queries: 10_000,
227
- rails_pulse_routes: 1_000,
228
- rails_pulse_jobs: 1_000
229
- }
230
-
231
- # Multiple database support (optional)
232
- # Uncomment to store Rails Pulse data in a separate database
233
- # config.connects_to = {
234
- # database: { writing: :rails_pulse, reading: :rails_pulse }
235
- # }
236
- end
237
- ```
238
-
239
- ## Background Job Monitoring
240
-
241
- Rails Pulse provides comprehensive monitoring for ActiveJob background jobs, tracking performance, failures, and execution details across all major job adapters.
242
-
243
- ### Overview
244
-
245
- Background job monitoring is **enabled by default** and works automatically with any ActiveJob adapter. Rails Pulse captures:
246
-
247
- - **Job execution metrics**: Duration, status, retry attempts
248
- - **Failure tracking**: Error class, error message, failure rates
249
- - **Performance analysis**: Slow jobs, aggregate metrics by job class
250
- - **Operation timeline**: SQL queries and operations during job execution
251
- - **Job arguments**: Optional capture for debugging (disabled by default for privacy)
252
-
253
- Access the jobs dashboard at `/rails_pulse/jobs` to view:
254
- - All job classes with aggregate metrics (total runs, failure rate, average duration)
255
- - Individual job executions with detailed performance data
256
- - Filtering by time range, status, queue, and performance thresholds
257
- - Tagging support for organizing jobs by team, priority, or category
258
-
259
- ### Supported Adapters
260
-
261
- Rails Pulse works with all ActiveJob adapters through universal tracking:
262
-
263
- - **Sidekiq** - Enhanced tracking via custom middleware
264
- - **Solid Queue** - Universal ActiveJob tracking
265
- - **Good Job** - Universal ActiveJob tracking
266
- - **Delayed Job** - Enhanced tracking via custom plugin
267
- - **Resque** - Universal ActiveJob tracking
268
- - **Any ActiveJob adapter** - Falls back to universal tracking
269
-
270
- No additional configuration needed - job tracking works out of the box with your existing setup.
271
-
272
- ### Job Tracking Configuration
273
-
274
- Customize job tracking in your Rails Pulse initializer:
275
-
276
- ```ruby
277
- RailsPulse.configure do |config|
278
- # Enable or disable job tracking (default: true)
279
81
  config.track_jobs = true
82
+ config.capture_job_arguments = false # keep false to protect sensitive data
280
83
 
281
- # Set performance thresholds for jobs (in milliseconds)
282
- config.job_thresholds = {
283
- slow: 5_000, # 5 seconds
284
- very_slow: 30_000, # 30 seconds
285
- critical: 60_000 # 1 minute
286
- }
287
-
288
- # Ignore specific job classes from tracking
289
- config.ignored_jobs = [
290
- "ActiveStorage::AnalyzeJob",
291
- "ActiveStorage::PurgeJob"
292
- ]
293
-
294
- # Ignore specific queues from tracking
295
- config.ignored_queues = ["low_priority", "mailers"]
296
-
297
- # Capture job arguments for debugging (default: false)
298
- # WARNING: May expose sensitive data - use with caution
299
- config.capture_job_arguments = false
300
-
301
- # Configure adapter-specific settings
302
- config.job_adapters = {
303
- sidekiq: { enabled: true, track_queue_depth: false },
304
- solid_queue: { enabled: true, track_recurring: false },
305
- good_job: { enabled: true, track_cron: false },
306
- delayed_job: { enabled: true },
307
- resque: { enabled: true }
308
- }
309
- end
310
- ```
311
-
312
- **Disabling job tracking for specific jobs:**
313
-
314
- ```ruby
315
- class MyBackgroundJob < ApplicationJob
316
- # Skip Rails Pulse tracking for this job
317
- def perform(*args)
318
- RailsPulse.with_tracking_disabled do
319
- # Job logic here
320
- end
321
- end
322
- end
323
- ```
324
-
325
- ### Privacy & Security
326
-
327
- **Job argument capture is disabled by default** to protect sensitive information. Job arguments may contain:
328
- - User credentials or tokens
329
- - Personal identifiable information (PII)
330
- - API keys or secrets
331
- - Sensitive business data
332
-
333
- Only enable `capture_job_arguments` in development or when explicitly needed for debugging. Consider using parameter filtering if you need to capture arguments:
334
-
335
- ```ruby
336
- # In your job class
337
- class SensitiveJob < ApplicationJob
338
- def perform(user_id:, api_key:)
339
- # Rails Pulse will track execution but not arguments by default
340
- end
84
+ config.full_retention_period = 30.days
341
85
  end
342
86
  ```
343
87
 
344
- **Performance impact:**
345
- - Minimal overhead: ~1-2ms per job execution
346
- - No blocking of job processing
347
- - Configurable cleanup prevents database growth
348
- - Can be disabled per-job or globally
88
+ Full configuration reference: [railspulse.com/documentation/advanced](https://railspulse.com/documentation/advanced)
349
89
 
350
90
  ## Authentication
351
91
 
352
- Rails Pulse supports flexible authentication to secure access to your monitoring dashboard.
353
-
354
- ### Authentication Setup
355
-
356
- Enable authentication by configuring the following options in your Rails Pulse initializer:
92
+ Rails Pulse has no built-in authentication, you protect the dashboard using your app's existing auth. Add an `authentication_method` to the initializer:
357
93
 
358
94
  ```ruby
359
- # config/initializers/rails_pulse.rb
360
95
  RailsPulse.configure do |config|
361
- # Enable authentication
362
96
  config.authentication_enabled = true
363
-
364
- # Where to redirect unauthorized users (optional, defaults to "/")
365
97
  config.authentication_redirect_path = "/login"
366
98
 
367
- # Define your authentication logic
368
99
  config.authentication_method = proc {
369
- # Your authentication logic here
370
- }
371
- end
372
- ```
373
-
374
- ### Authentication Examples
375
-
376
- Rails Pulse works with any authentication system. Here are common patterns:
377
-
378
- #### Devise with Admin Role
379
-
380
- ```ruby
381
- config.authentication_method = proc {
382
- unless user_signed_in? && current_user.admin?
383
- redirect_to main_app.root_path, alert: "Access denied"
384
- end
385
- }
386
- ```
387
-
388
- #### Custom Authorization Logic
389
-
390
- ```ruby
391
- config.authentication_method = proc {
392
- current_user = User.find_by(id: session[:user_id])
393
- unless current_user&.can_access_monitoring?
394
- render plain: "Forbidden", status: :forbidden
395
- end
396
- }
397
- ```
398
-
399
- ## Tagging System
400
-
401
- Rails Pulse includes a flexible tagging system that allows you to categorize and organize your performance data. Tag routes, requests, queries, jobs, and job runs with custom labels to better organize and filter your monitoring data.
402
-
403
- ### Configuring Tags
404
-
405
- Define available tags in your Rails Pulse initializer:
406
-
407
- ```ruby
408
- RailsPulse.configure do |config|
409
- config.tags = [
410
- "production",
411
- "staging",
412
- "critical",
413
- "needs-optimization",
414
- "high-traffic",
415
- "background-job"
416
- ]
417
- end
418
- ```
419
-
420
- ### Using Tags
421
-
422
- **Tag from the UI:**
423
-
424
- 1. Navigate to any route, request, query, job, or job run detail page
425
- 2. Click the "+ tag" button next to the record
426
- 3. Select from your configured tags
427
- 4. Remove tags by clicking the × button on any tag badge
428
-
429
- **Tag Programmatically:**
430
-
431
- ```ruby
432
- # Tag a route
433
- route = RailsPulse::Route.find_by(path: "/api/users")
434
- route.add_tag("critical")
435
- route.add_tag("high-traffic")
436
-
437
- # Tag a query
438
- query = RailsPulse::Query.find_by(normalized_sql: "SELECT * FROM users WHERE id = ?")
439
- query.add_tag("needs-optimization")
440
-
441
- # Tag a job
442
- job = RailsPulse::Job.find_by(name: "UserNotificationJob")
443
- job.add_tag("high-priority")
444
- job.add_tag("user-facing")
445
-
446
- # Tag a specific job run
447
- job_run = RailsPulse::JobRun.find_by(run_id: "abc123")
448
- job_run.add_tag("investigated")
449
-
450
- # Remove a tag
451
- route.remove_tag("critical")
452
-
453
- # Check if has tag
454
- route.has_tag?("production") # => true
455
- ```
456
-
457
- ### Filtering by Tags
458
-
459
- Use the global filters modal to filter performance data by tags:
460
-
461
- 1. Click the filter icon in the top navigation
462
- 2. Select one or more tags from the tag selector
463
- 3. Apply filters to see only records with those tags
464
- 4. Tags appear as badges in all data tables for quick visual identification
465
-
466
- **Common Tagging Strategies:**
467
-
468
- - **By Environment**: `production`, `staging`, `development`
469
- - **By Priority**: `critical`, `high`, `medium`, `low`
470
- - **By Status**: `needs-optimization`, `investigating`, `resolved`
471
- - **By Type**: `api`, `background-job`, `user-facing`, `admin`
472
- - **By Team**: `team-frontend`, `team-backend`, `team-data`
473
-
474
- ## Data Management
475
-
476
- Rails Pulse provides data cleanup to prevent your monitoring database from growing indefinitely while preserving essential performance insights.
477
-
478
- ### Cleanup Strategies
479
-
480
- **Time-based Cleanup**
481
- - Automatically delete performance records older than a specified period
482
- - Configurable retention period (default: 2 days)
483
- - Keeps recent data for debugging while removing historical noise
484
-
485
- **Count-based Cleanup**
486
- - Enforce maximum record limits per table
487
- - Prevents any single table from consuming excessive storage
488
- - Configurable limits for each Rails Pulse table
489
-
490
- ### Cleanup Configuration
491
-
492
- ```ruby
493
- RailsPulse.configure do |config|
494
- # Enable or disable automatic cleanup
495
- config.archiving_enabled = true
496
-
497
- # Time-based retention
498
- config.full_retention_period = 2.weeks
499
-
500
- # Count-based retention - maximum records per table
501
- config.max_table_records = {
502
- rails_pulse_requests: 10000, # HTTP requests
503
- rails_pulse_operations: 50000, # Operations within requests
504
- rails_pulse_routes: 1000, # Unique routes
505
- rails_pulse_queries: 500 # Normalized SQL queries
506
- }
507
- end
508
- ```
509
-
510
- ### Manual Cleanup Operations
511
-
512
- **Run cleanup manually:**
513
- ```bash
514
- rails rails_pulse:cleanup
515
- ```
516
-
517
- **Check current database status:**
518
- ```bash
519
- rails rails_pulse:cleanup_stats
520
- ```
521
-
522
- **Schedule automated cleanup:**
523
- ```ruby
524
- RailsPulse::CleanupJob.perform_later
525
- ```
526
-
527
- ## Separate Database Support
528
-
529
- Rails Pulse offers two database setup options to fit your application's needs:
530
-
531
- ### Option 1: Single Database (Default - Recommended)
532
-
533
- Stores Rails Pulse data in your main application database alongside your existing tables. This is the simplest setup and works great for most applications.
534
-
535
- **Advantages:**
536
- - Zero additional configuration required
537
- - Simpler backup and deployment strategies
538
- - Works with any database (SQLite, PostgreSQL, MySQL)
539
-
540
- **Installation:**
541
- ```bash
542
- rails generate rails_pulse:install
543
- rails db:migrate
544
- ```
545
-
546
- ### Option 2: Separate Database
547
-
548
- Stores Rails Pulse data in a dedicated database, completely isolated from your main application.
549
-
550
- **Use a separate database when you want:**
551
- - **Isolating monitoring data** from your main application database
552
- - **Using different database engines** optimized for time-series data
553
- - **Scaling monitoring independently** from your application
554
- - **Simplified backup strategies** with separate retention policies
555
-
556
- ### Configuration
557
-
558
- To use a separate database, install with the `--database=separate` flag, then configure the `connects_to` option in your Rails Pulse initializer:
559
-
560
- ```ruby
561
- RailsPulse.configure do |config|
562
- # Single separate database
563
- config.connects_to = {
564
- database: { writing: :rails_pulse, reading: :rails_pulse }
565
- }
566
-
567
- # Or primary/replica configuration
568
- config.connects_to = {
569
- database: { writing: :rails_pulse_primary, reading: :rails_pulse_replica }
100
+ unless user_signed_in? && current_user.admin?
101
+ redirect_to main_app.root_path, alert: "Access denied"
102
+ end
570
103
  }
571
104
  end
572
105
  ```
573
106
 
574
- ### Database Configuration
575
-
576
- Add the corresponding database configurations to your `config/database.yml`:
577
-
578
- ```yaml
579
- # For SQLite
580
- production:
581
- # ... your main database ...
582
- rails_pulse:
583
- adapter: sqlite3
584
- database: storage/rails_pulse_production.sqlite3
585
- migrations_paths: db/rails_pulse_migrate
586
- pool: 5
587
- timeout: 5000
588
-
589
- # For PostgreSQL
590
- production:
591
- # ... your main database ...
592
- rails_pulse:
593
- adapter: postgresql
594
- database: myapp_rails_pulse_production
595
- username: rails_pulse_user
596
- password: <%= Rails.application.credentials.dig(:rails_pulse, :database_password) %>
597
- host: localhost
598
- migrations_paths: db/rails_pulse_migrate
599
- pool: 5
600
-
601
- # For MySQL
602
- production:
603
- # ... your main database ...
604
- rails_pulse:
605
- adapter: mysql2
606
- database: myapp_rails_pulse_production
607
- username: rails_pulse_user
608
- password: <%= Rails.application.credentials.dig(:rails_pulse, :database_password) %>
609
- host: localhost
610
- migrations_paths: db/rails_pulse_migrate
611
- pool: 5
612
- ```
613
-
614
- ### Installation Steps
615
-
616
- **For separate database setup:**
617
-
618
- 1. **Generate installation files:**
619
- ```bash
620
- rails generate rails_pulse:install --database=separate
621
- ```
622
-
623
- 2. **Configure `config/database.yml`** (see examples above)
624
-
625
- 3. **Create and load the schema:**
626
- ```bash
627
- rails db:prepare
628
- ```
629
- This automatically creates the database and loads the Rails Pulse schema.
630
-
631
- **Schema Management:**
632
-
633
- The schema file `db/rails_pulse_schema.rb` serves as your single source of truth for the database structure. It:
634
- - Defines all Rails Pulse tables in one place
635
- - Is loaded by the installation migration
636
- - Should not be deleted or modified
637
- - Future updates will provide migrations in `db/rails_pulse_migrate/`
638
-
639
- ## Performance Impact
640
-
641
- Rails Pulse uses **fiber-based async tracking** for minimal performance overhead:
107
+ Authentication guide: [railspulse.com/documentation/authentication](https://railspulse.com/documentation/authentication)
642
108
 
643
- - **Request overhead:** ~0.1ms per request (data collection only)
644
- - **Database writes:** Non-blocking, handled in background fibers
645
- - **Memory allocation:** Minimal, ~200 KB per request (temporary)
646
- - **Thread safety:** Proper connection pooling with isolated database connections
647
-
648
- This is handled automatically and requires no configuration.
649
-
650
- ## Standalone Dashboard Deployment
651
-
652
- For production environments, you can run the Rails Pulse dashboard as a standalone application, separate from your main Rails app. This provides several benefits:
653
-
654
- - **Dashboard remains accessible when main app is under heavy load**
655
- - **Separate resource allocation** for monitoring vs application
656
- - **Enhanced security** - isolate dashboard access from public app
657
- - **Independent scaling** - dashboard doesn't scale with app instances
658
-
659
- **Quick Setup:**
660
-
661
- ```bash
662
- # Option 1: Set DATABASE_URL environment variable (recommended for production)
663
- export DATABASE_URL="postgresql://user:pass@host/db"
664
- bundle exec rails_pulse_server
665
-
666
- # Option 2: Use config/database.yml (recommended for development)
667
- # Looks for 'rails_pulse' connection, falls back to primary
668
- # No environment variable needed - automatically reads from config/database.yml
669
- bundle exec rails_pulse_server
670
- ```
671
-
672
- **Healthcheck Endpoint:**
673
-
674
- The standalone server includes a `/health` endpoint that verifies database connectivity:
675
-
676
- ```bash
677
- curl http://localhost:3001/health
678
- # Returns: {"status":"ok","mode":"dashboard","database":"connected","timestamp":"..."}
679
- ```
680
-
681
- **Deployment Options:**
682
-
683
- 1. **Separate subdomain (recommended):**
684
- ```nginx
685
- server {
686
- server_name pulse.myapp.com;
687
- location / {
688
- proxy_pass http://localhost:3001;
689
- proxy_set_header Host $host;
690
- proxy_set_header X-Real-IP $remote_addr;
691
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
692
- proxy_set_header X-Forwarded-Proto $scheme;
693
- }
694
- }
695
- ```
696
-
697
- 2. **Kamal Deployment:**
698
- Deploy the dashboard as an accessory
699
-
700
- ```yaml
701
- # config/deploy.yml
702
- accessories:
703
- rails_pulse:
704
- image: your-app-image # Same image as your main app
705
- host: your-server
706
- cmd: bundle exec rails_pulse_server
707
- env:
708
- clear:
709
- DATABASE_URL: "postgresql://user:pass@host/db"
710
- RAILS_ENV: production
711
- SECRET_KEY_BASE: <%= ENV.fetch("SECRET_KEY_BASE") %>
712
- port: "3001:3001"
713
- healthcheck:
714
- path: /health
715
- port: 3001
716
- interval: 10s
717
- timeout: 5s
718
- ```
719
-
720
- **Note:** When running standalone, the dashboard is read-only and doesn't track its own requests (tracking is automatically disabled).
721
-
722
- For detailed deployment instructions, see [docs/deployment-modes.md](docs/deployment-modes.md).
723
-
724
- ## Testing
725
-
726
- Rails Pulse includes a comprehensive test suite designed for speed and reliability across multiple databases and Rails versions.
727
-
728
- ### Running the Complete Test Suite
729
-
730
- ```bash
731
- # Run all tests (unit, functional, integration, instrumentation)
732
- rails test:all
733
- ```
734
-
735
- ### Running Individual Test Types
736
-
737
- ```bash
738
- # Unit tests (models, helpers, utilities)
739
- rails test:unit
740
-
741
- # Functional tests (controllers, views)
742
- rails test:functional
743
-
744
- # Integration tests (end-to-end workflows)
745
- rails test:integration
746
- ```
747
-
748
- ### Running Individual Test Files
749
-
750
- ```bash
751
- # Run a specific test file
752
- rails test test/models/rails_pulse/request_test.rb
753
- ```
754
-
755
- ### Multi-Database and Rails Version Testing
756
-
757
- Test against multiple databases and Rails versions using the matrix task:
758
-
759
- ```bash
760
- # Test all database and Rails version combinations locally
761
- rails test:matrix
762
- ```
763
-
764
- This command tests all combinations locally:
765
- - **Databases**: SQLite3, PostgreSQL, MySQL2 (local testing only)
766
- - **Rails versions**: 7.2, 8.0, 8.1
767
-
768
- **Note**: CI only tests SQLite3 + PostgreSQL for reliability. MySQL is available for local testing but excluded from CI due to flakiness.
769
-
770
- ### Development Environment Setup
771
-
772
- 1. **Copy the environment template:**
773
- ```bash
774
- cp .env.example .env
775
- ```
776
-
777
- 2. **Configure your database credentials in `.env` (for local multi-database testing):**
778
- ```bash
779
- # PostgreSQL Configuration (used in CI + local)
780
- POSTGRES_USERNAME=your_username
781
- POSTGRES_PASSWORD=your_password
782
- POSTGRES_HOST=localhost
783
- POSTGRES_PORT=5432
784
-
785
- # MySQL Configuration (local testing only)
786
- MYSQL_USERNAME=root
787
- MYSQL_PASSWORD=your_password
788
- MYSQL_HOST=localhost
789
- MYSQL_PORT=3306
790
- ```
791
-
792
- 3. **Create test databases:**
793
- ```bash
794
- # PostgreSQL
795
- createdb rails_pulse_test
796
-
797
- # MySQL
798
- mysql -u root -p -e "CREATE DATABASE rails_pulse_test;"
799
- ```
800
-
801
- ### Manual Database Testing
802
-
803
- Test individual databases locally:
804
-
805
- ```bash
806
- # Test with SQLite (default)
807
- rails test:all
109
+ ## Features
808
110
 
809
- # Test with PostgreSQL
810
- DB=postgresql FORCE_DB_CONFIG=true rails test:all
111
+ - **Request monitoring** — every request is timed and stored with its route, status, SQL count, and duration. Slow requests are flagged automatically based on thresholds you control.
112
+ - **Query analysis** — captures the queries behind each request, detects N+1 patterns, and tracks normalized SQL across requests so you can see which queries are hurting you in production, not just in development.
113
+ - **Job tracking** — monitors background job duration, queue wait time, and failure rates. Works with any Active Job adapter.
114
+ - **System health bar** — at-a-glance dashboard summary showing healthy, slow, and critical counts across your routes, queries, jobs, and storage. Lets you see the overall state of your app before drilling into specifics.
115
+ - **No data leaves your app** — everything is stored in your own database. No third-party cloud, no SaaS subscription, no outbound connections.
116
+ - **Low overhead** — tracking is async and uses a thread-local flag to skip recording Rails Pulse's own internal requests.
811
117
 
812
- # Test with MySQL (local only)
813
- DB=mysql2 FORCE_DB_CONFIG=true rails test:all
814
- ```
118
+ ## Contributing
815
119
 
816
- ### CI Testing
817
-
818
- GitHub Actions CI automatically tests:
819
- - **Databases**: SQLite3, PostgreSQL only (MySQL excluded for reliability)
820
- - **Rails versions**: 7.2, 8.0, 8.1
821
- - **Environment**: Uses memory SQLite and PostgreSQL service
822
-
823
- **Local vs CI differences**:
824
- - **Local**: Can test all 3 databases (SQLite3, PostgreSQL, MySQL2)
825
- - **CI**: Only SQLite3 + PostgreSQL for fast, reliable builds
826
- - **Database switching**: Requires `FORCE_DB_CONFIG=true` locally
827
-
828
- ## Technology Stack
829
-
830
- Rails Pulse is built using modern, battle-tested technologies that ensure reliability, performance, and maintainability:
831
-
832
- ### **Frontend Technologies**
833
- - **[CSS Zero](https://github.com/lazaronixon/css-zero)** - Modern utility-first CSS framework bundled for asset independence
834
- - **[Stimulus](https://stimulus.hotwired.dev/)** - Progressive JavaScript framework for enhanced interactivity
835
- - **Fetch API + DOMParser** - Lightweight partial page updates with CSP-safe DOM manipulation
836
-
837
- ### **Data Visualization**
838
- - **[Apache ECharts](https://echarts.apache.org/)** - Powerful, interactive charting library
839
- - **[Lucide Icons](https://lucide.dev/)** - Beautiful, consistent iconography with pre-compiled SVG bundle
840
-
841
- ### **Asset Management**
842
- - **Pre-compiled Assets** - All CSS, JavaScript, and icons bundled into the gem
843
- - **CSP-Safe Implementation** - Secure DOM methods and nonce-based asset loading
844
- - **Build System** - Node.js-based build process for asset compilation
845
- - **Zero External Dependencies** - Self-contained assets work with any Rails build system
846
-
847
- ### **Performance & Optimization**
848
- - **[Request Store](https://github.com/steveklabnik/request_store)** - Thread-safe request-scoped storage for performance data
849
- - **[Rails Caching](https://guides.rubyonrails.org/caching_with_rails.html)** - Fragment caching with smart invalidation strategies
850
- - **[ActiveRecord Instrumentation](https://guides.rubyonrails.org/active_support_instrumentation.html)** - Built-in Rails performance monitoring hooks
851
-
852
- ### **Development & Testing**
853
- - **[Rails Generators](https://guides.rubyonrails.org/generators.html)** - Automated installation and configuration
854
- - **[Omakase Ruby Styling](https://github.com/rails/rubocop-rails-omakase)** - Consistent code formatting and style
855
-
856
- ## Advantages Over Other Solutions
857
-
858
- ### **vs. Application Performance Monitoring (APM) Services**
859
- - **No External Dependencies**: Everything runs in your Rails application with pre-compiled assets
860
- - **Zero Monthly Costs**: No subscription fees or usage-based pricing
861
- - **Data Privacy**: All performance data stays in your database(s)
862
- - **Customizable**: Full control over metrics, thresholds, and interface
863
- - **Asset Independence**: Works with any Rails build system (Sprockets, esbuild, Webpack, Vite)
864
-
865
- ### **vs. Custom Monitoring Solutions**
866
- - **Batteries Included**: Complete monitoring solution out of the box
867
- - **Proven Architecture**: Built on Rails best practices
868
- - **Community Driven**: Open source with active development
869
- - **Professional Design**: Production-ready interface
870
-
871
- ### **Key Differentiators**
872
- - **Rails-Native**: Designed specifically for Rails applications
873
- - **Developer Experience**: Optimized for debugging and development
874
- - **Positive Focus**: Celebrates good performance alongside problem identification
875
- - **Contextual Insights**: Deep Rails framework integration for meaningful metrics
876
- - **Security First**: CSP-compliant by default with secure asset handling
877
- - **Zero Build Dependencies**: Pre-compiled assets work with any Rails setup
878
- - **Flexible Data Storage**: Support for multiple database backends (SQLite, PostgreSQL, MySQL)
120
+ Bug reports and pull requests are welcome on [GitHub](https://github.com/railspulse/rails_pulse).
879
121
 
880
122
  ## License
881
123
 
882
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
883
-
884
- ---
885
-
886
- <div align="center">
887
- <strong>Built with ❤️ for the Rails community</strong>
888
-
889
- [Issues](https://github.com/railspulse/rails_pulse/issues) •
890
- [Discussions](https://github.com/railspulse/rails_pulse/discussions)
891
- </div>
124
+ Available as open source under the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -13,17 +13,8 @@ task :verify_dummy_migrations do
13
13
  if Dir.exist?("db/rails_pulse_migrate")
14
14
  gem_migrations = Dir["db/rails_pulse_migrate/*.rb"].map { |f| File.basename(f) }.sort
15
15
 
16
- # Get all RailsPulse migrations from dummy app (exclude dummy app's own migrations)
17
- all_dummy_migrations = Dir["test/dummy/db/migrate/*.rb"].map { |f| File.basename(f) }
18
-
19
- # Filter to only RailsPulse migrations (contain "rails_pulse" in name or match known patterns)
20
- dummy_migrations = all_dummy_migrations.select do |m|
21
- m.include?("rails_pulse") ||
22
- m.include?("jobs") ||
23
- m.include?("query") ||
24
- m.include?("request_uuid") ||
25
- m.include?("diagnostic")
26
- end.sort
16
+ # Get all migrations from dummy app
17
+ dummy_migrations = Dir["test/dummy/db/migrate/*.rb"].map { |f| File.basename(f) }
27
18
 
28
19
  missing = gem_migrations - dummy_migrations
29
20
 
@@ -195,7 +186,7 @@ end
195
186
 
196
187
  desc "Test all database and Rails version combinations"
197
188
  task :test_matrix do
198
- databases = %w[sqlite3 postgresql mysql2]
189
+ databases = %w[mysql2 postgresql sqlite3]
199
190
  rails_versions = %w[rails-7-2 rails-8-0 rails-8-1]
200
191
 
201
192
  failed_combinations = []
@@ -4,7 +4,7 @@ class AddDiagnosticFields < ActiveRecord::Migration[7.0]
4
4
  add_column :rails_pulse_operations, :row_count, :integer
5
5
  end
6
6
  unless column_exists?(:rails_pulse_operations, :cache_hit)
7
- add_column :rails_pulse_operations, :cache_hit, :boolean
7
+ add_column :rails_pulse_operations, :cache_hit, :boolean, null: false, default: false
8
8
  end
9
9
  unless column_exists?(:rails_pulse_operations, :repeated_query_group)
10
10
  add_column :rails_pulse_operations, :repeated_query_group, :text
@@ -0,0 +1,28 @@
1
+ class ExpandNormalizedQueryColumn < ActiveRecord::Migration[7.0]
2
+ def up
3
+ return unless table_exists?(:rails_pulse_queries)
4
+
5
+ adapter = connection.adapter_name.downcase
6
+
7
+ # SQLite doesn't enforce string length limits, so no migration needed there.
8
+ # change_column is also not supported on SQLite.
9
+ return unless adapter.include?("postgresql") || adapter.include?("mysql")
10
+
11
+ column = connection.columns(:rails_pulse_queries).find { |c| c.name == "normalized_sql" }
12
+ return unless column
13
+
14
+ # Only migrate if the column is still a limited string type (not already text)
15
+ return unless column.type == :string
16
+
17
+ change_column :rails_pulse_queries, :normalized_sql, :text, null: false
18
+ end
19
+
20
+ def down
21
+ return unless table_exists?(:rails_pulse_queries)
22
+
23
+ adapter = connection.adapter_name.downcase
24
+ return unless adapter.include?("postgresql") || adapter.include?("mysql")
25
+
26
+ change_column :rails_pulse_queries, :normalized_sql, :string, limit: 1000, null: false
27
+ end
28
+ end
@@ -1,3 +1,3 @@
1
1
  module RailsPulse
2
- VERSION = "0.3.0.pre.1"
2
+ VERSION = "0.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_pulse
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0.pre.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
- - Rails Pulse
7
+ - Scott Harvey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-04-19 00:00:00.000000000 Z
11
+ date: 2026-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -190,9 +190,9 @@ dependencies:
190
190
  - - ">="
191
191
  - !ruby/object:Gem::Version
192
192
  version: '0'
193
- description: Ruby on Rails performance monitoring tool that provides insights into
194
- your application's performance, helping you identify bottlenecks and optimize your
195
- code for better efficiency.
193
+ description: Self-hosted performance monitoring engine for Rails apps. Tracks slow
194
+ requests, N+1 queries, and SQL performance. All data stays in your own database
195
+ no third-party cloud required.
196
196
  email:
197
197
  - hey@railspulse.com
198
198
  executables:
@@ -204,8 +204,11 @@ files:
204
204
  - README.md
205
205
  - Rakefile
206
206
  - app/assets/images/rails_pulse/dashboard.png
207
+ - app/assets/images/rails_pulse/query-show.png
207
208
  - app/assets/images/rails_pulse/rails-pulse-logo.png
209
+ - app/assets/images/rails_pulse/request-show.png
208
210
  - app/assets/images/rails_pulse/request.png
211
+ - app/assets/images/rails_pulse/route-show.png
209
212
  - app/assets/stylesheets/rails_pulse/application.css
210
213
  - app/assets/stylesheets/rails_pulse/components/alert.css
211
214
  - app/assets/stylesheets/rails_pulse/components/badge.css
@@ -431,8 +434,9 @@ files:
431
434
  - config/importmap.rb
432
435
  - config/initializers/rails_pulse.rb
433
436
  - config/routes.rb
434
- - db/rails_pulse_migrate/20250209000001_add_p95_p99_duration_to_rails_pulse_jobs.rb
435
437
  - db/rails_pulse_migrate/20260417000001_add_diagnostic_fields.rb
438
+ - db/rails_pulse_migrate/20260419000001_add_p95_p99_duration_to_rails_pulse_jobs.rb
439
+ - db/rails_pulse_migrate/20260420000001_expand_normalized_query_column.rb
436
440
  - db/rails_pulse_schema.rb
437
441
  - exe/rails_pulse_server
438
442
  - lib/generators/rails_pulse/base_methods.rb
@@ -497,13 +501,15 @@ files:
497
501
  - vendor/css-zero/utilities.css
498
502
  - vendor/css-zero/variables.css
499
503
  - vendor/flatpickr.css
500
- homepage: https://www.railspulse.com
504
+ homepage: https://railspulse.com
501
505
  licenses:
502
506
  - MIT
503
507
  metadata:
504
508
  allowed_push_host: https://rubygems.org
505
- homepage_uri: https://www.railspulse.com
509
+ homepage_uri: https://railspulse.com
506
510
  source_code_uri: https://github.com/railspulse/rails_pulse
511
+ changelog_uri: https://github.com/railspulse/rails_pulse/blob/main/CHANGELOG.md
512
+ documentation_uri: https://railspulse.com/documentation/installation
507
513
  post_install_message:
508
514
  rdoc_options: []
509
515
  require_paths:
@@ -522,5 +528,5 @@ requirements: []
522
528
  rubygems_version: 3.5.22
523
529
  signing_key:
524
530
  specification_version: 4
525
- summary: Ruby on Rails performance monitoring tool.
531
+ summary: Self-hosted performance monitoring engine for Rails apps.
526
532
  test_files: []