Advanced Configuration
Customize Rails Pulse with performance thresholds, tagging, filtering, historical comparison, and data management options.
Performance Thresholds
Configure performance thresholds to categorize requests, routes, queries, and jobs. Exception thresholds are occurrence counts rather than durations.
# config/initializers/rails_pulse.rb
RailsPulse.configure do |config|
# Route thresholds (in milliseconds)
config.route_thresholds = {
slow: 500,
very_slow: 1500,
critical: 3000
}
# Request thresholds (in milliseconds)
config.request_thresholds = {
slow: 700,
very_slow: 2000,
critical: 4000
}
# Query thresholds (in milliseconds)
config.query_thresholds = {
slow: 100,
very_slow: 500,
critical: 1000
}
# Job thresholds (in milliseconds)
config.job_thresholds = {
slow: 5_000, # 5 seconds
very_slow: 30_000, # 30 seconds
critical: 60_000 # 1 minute
}
# Exception thresholds (occurrences over the dashboard period)
config.exception_thresholds = {
warning: 10,
critical: 100
}
end
Service Level Objectives
Objectives are drawn as horizontal lines on the response time and query performance percentile charts, so you can see at a glance when a percentile crosses the line. Thresholds are in milliseconds; query objectives are typically 5–10x stricter than request objectives.
RailsPulse.configure do |config|
# Shown on the Response Time Percentiles chart
config.service_level_objectives = [
{ percentile: 95, threshold: 200 },
{ percentile: 99, threshold: 500 }
]
# Shown on the Query Performance chart
config.query_service_level_objectives = [
{ percentile: 95, threshold: 50 },
{ percentile: 99, threshold: 100 }
]
end
Filtering & Ignoring
Exclude specific routes, requests, queries, or jobs from tracking:
RailsPulse.configure do |config|
# Ignore specific routes
config.ignored_routes = ["/health", /\A\/api\/internal/]
# Ignore specific request patterns
config.ignored_requests = []
# Ignore specific query patterns
config.ignored_queries = []
# Ignore asset requests
config.track_assets = false
# Extra asset paths to ignore, on top of the built-in defaults
# (only applies while track_assets is false)
config.custom_asset_patterns = [%r{^/uploads/}, "/special-assets/"]
# Rails Pulse mount path — set this to prevent self-tracking when mounted at a custom path
config.mount_path = nil # e.g., "/admin/monitoring"
# Ignore specific job classes
config.ignored_jobs = [
"ActiveStorage::AnalyzeJob",
"ActiveStorage::PurgeJob"
]
# Ignore specific queues
config.ignored_queues = ["low_priority", "mailers"]
end
Tagging System
Organize and categorize performance data with custom tags:
Configure Available Tags
RailsPulse.configure do |config|
config.tags = [
"production",
"staging",
"critical",
"needs-optimization",
"high-traffic"
]
end
Tag from the UI
- Navigate to any route, request, query, job, or job run detail page
- Click the ”+ tag” button
- Select from your configured tags
- Remove tags by clicking the × button
Tag Programmatically
Routes are identified by controller action and path, so look them up by controller_action. Several routes can share a path.
# Tag a route
route = RailsPulse::Route.find_by(controller_action: "api/users#index")
route.add_tag("critical")
route.add_tag("high-traffic")
# Tag a query
query = RailsPulse::Query.find_by(normalized_sql: "SELECT...")
query.add_tag("needs-optimization")
# Remove a tag
route.remove_tag("critical")
See Tagging for strategies and the full API.
Historical Comparison
Rails Pulse compares recent behaviour against a route, query, or job’s own history to answer “what changed?” rather than only “what is slow?”. Metric cards on detail pages show the current value against the subject’s normal, and when a regression is detected they include when it started, for example “Compared to its 28-day normal · since Sep 3 14:00”. Index pages, which have no single subject, fall back to period-over-period comparison.
RailsPulse.configure do |config|
# The baseline is the traffic-weighted metric across `baseline_window` of day
# summaries; `comparison_window` is the recent slice measured against it.
# The two never overlap.
config.baseline_window = 28.days # default
config.comparison_window = 1.day # default
# Hourly summaries decide how precisely a change point can be placed. Inside
# this window Rails Pulse can say a route slowed down at 14:00; beyond it, the
# finest answer is the day. Raising it grows the summary table.
config.hourly_summary_retention = 2.days # default
# A change is reported as a regression only when it clears both the ratio and
# the absolute floor for its unit. The ratio alone flags millisecond noise on
# fast endpoints; the floor alone flags slow endpoints that never changed.
# min_samples keeps low-traffic subjects quiet.
config.regression_thresholds = {
ratio: 1.5, # 50% worse than baseline
min_delta_ms: 50.0, # ...and at least 50ms worse
min_delta_rate: 1.0, # ...or 1 percentage point, for error rates
min_samples: 100, # minimum observations on each side
min_baseline_periods: 3 # minimum days of history before comparing
}
end
baseline_window must be longer than comparison_window, and ratio must be greater than 1. Misconfiguration raises at boot.
Regressions are correlated with recorded deployments, so a change point can be lined up against the release that caused it.
Data Management & Cleanup
Prevent database growth with automatic cleanup strategies. The Storage page in the dashboard shows how full each table is relative to its cap, disk usage, retention settings, and when cleanup last ran.
Cleanup Configuration
RailsPulse.configure do |config|
# Enable automatic cleanup
config.archiving_enabled = true
# Time-based retention (delete records older than this)
config.full_retention_period = 30.days
# Count-based retention (maximum records per table)
config.max_table_records = {
rails_pulse_requests: 50_000,
rails_pulse_operations: 100_000,
rails_pulse_routes: 1_000,
rails_pulse_queries: 10_000,
rails_pulse_job_runs: 50_000,
rails_pulse_jobs: 1_000,
rails_pulse_exception_occurrences: 50_000,
rails_pulse_exception_groups: 10_000,
rails_pulse_deployments: 1_000 # never pruned by age; oldest pruned first
}
end
The max_table_records hash is merged with the gem defaults, so you can override one table without listing them all. Set config.max_table_records = nil to disable count-based cleanup entirely.
Exception groups marked Preserve keep their occurrences through cleanup.
Manual Cleanup
# Run cleanup manually
rails rails_pulse:cleanup
# Check database status
rails rails_pulse:cleanup_stats
# Schedule via background job
RailsPulse::CleanupJob.perform_later
Background Job Configuration
Job tracking is opt-in.
RailsPulse.configure do |config|
# Enable job tracking (default: false)
config.track_jobs = true
# Capture job arguments (disabled by default for privacy)
config.capture_job_arguments = false
# :universal (all jobs) or :opt_in (only explicitly tracked jobs)
config.job_tracking_mode = :universal
# Configure adapter-specific settings
config.job_adapters = {
sidekiq: { enabled: true, track_queue_depth: false },
solid_queue: { enabled: true, track_recurring: false },
good_job: { enabled: true, track_cron: false },
delayed_job: { enabled: true },
resque: { enabled: true }
}
end
Privacy Warning: Job arguments may contain user credentials, PII, or API keys. Argument capture is disabled by default. Enable it only after reviewing your job argument contents.
Job failure messages are redacted before storage using the same rules as exception messages.
Disabling tracking for a specific job:
class MyBackgroundJob < ApplicationJob
def perform(*args)
RailsPulse.with_tracking_disabled do
# Job logic here — Rails Pulse will not track this block
end
end
end
See Job Adapters for adapter-specific notes.
Exception Tracking
RailsPulse.configure do |config|
config.track_exceptions = true # gem default: false
config.capture_exception_params = true # filtered via Rails' filter_parameters
config.exception_message_filter = nil # optional ->(message, exception) hook
end
See Exception Tracking for redaction rules and the dashboard workflow.
Query Capture
By default Rails Pulse stores normalized SQL (SELECT * FROM users WHERE id = ?). You can opt in to storing the raw statement for each operation, which makes EXPLAIN analysis more accurate:
RailsPulse.configure do |config|
config.capture_actual_sql = true # default: false
end
Warning:
mysql2defaults toprepared_statements: false, so every literal value (emails, passwords, tokens) is inlined into the SQL and would be stored in plaintext. The same applies to PostgreSQL behind PgBouncer in transaction-pool mode. Leave this off unless you have reviewed what your queries contain.
The query analyzer refuses to EXPLAIN any SQL containing ;, --, or /*, and on PostgreSQL runs inside a read-only transaction with a 5 second statement timeout.
Advanced Options
RailsPulse.configure do |config|
# Use a custom logger (default: Rails.logger)
config.logger = Logger.new("log/rails_pulse.log")
# Perform tracking writes on a background thread (default: true).
# When false, writes happen inline before the response is sent.
config.async = true
config.async = false if Rails.env.test? # transactional tests share one connection
# How many requests the background writer may hold before it drops the
# newest ones instead of slowing the app down (default: 1000). Drops are
# counted in RailsPulse::Tracker.stats and logged at most once a minute.
config.async_queue_size = 1000
# Show a dashboard banner when summary data is stale (default: true)
config.warn_on_stale_summaries = true
# When the gem is newer than its tables (deployed before db:migrate, or a
# rolling restart), pause tracking and answer dashboard pages with a 503
# that lists the upgrade commands (default: true)
config.schema_check_enabled = true
# Set to false to skip dashboard middleware and asset serving entirely.
# Useful for instrumentation-only apps that use a separate dashboard.
config.mount_dashboard = true
end
See Deployment Modes for when to use mount_dashboard = false.
Disabling Rails Pulse
Disable Rails Pulse per environment:
RailsPulse.configure do |config|
# Disable in test environment
config.enabled = !Rails.env.test?
end