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

  1. Navigate to any route, request, query, job, or job run detail page
  2. Click the ”+ tag” button
  3. Select from your configured tags
  4. 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: mysql2 defaults to prepared_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

Next Steps

Explore Features

Learn more about the dashboard, SQL tracking, and job monitoring.

View Features →

Common Questions

Find answers to frequently asked questions.

View FAQ →