Skip to content

Ruby on Rails

Collect Rails HTTP request metrics — rate, errors, latency, plus view and database time — and get curated dashboards and alerts for them with a single import.

Ruby uses the Prometheus path, not the OpenTelemetry SDK. OpenTelemetry Ruby has no automatic metrics today — its Rails/Rack instrumentation emits traces only, and the OpenTelemetry Ruby metrics SDK is still experimental. The mature, production-ready way to get Rails-level metrics right now is Prometheus: yabeda-rails with yabeda-prometheus exposes a /metrics endpoint, and your LeanSignal agent scrapes it directly with its built-in Prometheus receiver — no extra collector to run (one agent per cluster or environment). The gateway keeps full fidelity locally and forwards only the demanded subset to your central dataplane — no traces involved.

The demands below are built against the default yabeda-rails metric names (rails_requests_total, rails_request_duration_seconds, rails_db_runtime_seconds, rails_view_runtime_seconds). If you instrument with prometheus_exporter or a custom setup instead, adjust the imported demand’s queries to match your metric names.

  • A LeanSignal agent gateway deployed and connected — one per cluster or environment. If you haven’t deployed it yet, follow Install the agent (source: LeanSignal/leansignal-agent). It should show as Connected under Agents in the LeanSignal app.
  • Network reachability to the gateway’s OTLP port4317 (gRPC) or 4318 (HTTP). See Agent configuration.
  • Your Ruby on Rails app exposing Prometheus metrics, plus an OpenTelemetry Collector that scrapes them and forwards OTLP to the gateway — both configured in Setup.
  • Editor or admin role in the LeanSignal app (importing a demand creates dashboards and alert rules).

1. Instrument Rails with Yabeda. Add the gems:

# Gemfile
gem "yabeda-rails"
gem "yabeda-prometheus"

Mount the metrics endpoint (a Rack app on your chosen path). Yabeda’s Railtie registers the Rails metrics automatically:

config/routes.rb
require "yabeda/prometheus/exporter"
mount Yabeda::Prometheus::Exporter => "/metrics"

This publishes rails_requests_total, rails_request_duration_seconds, rails_db_runtime_seconds, and rails_view_runtime_seconds at /metrics.

2. Protect the endpoint if it is reachable from outside. /metrics reveals request paths, status codes, and traffic patterns — it must not be public. If your Rails app faces the internet, wrap the exporter in HTTP Basic authentication with a strong generated password (openssl rand -base64 24) instead of mounting it bare:

config/routes.rb
require "yabeda/prometheus/exporter"
metrics_app = Rack::Builder.new do
use Rack::Auth::Basic do |user, password|
ActiveSupport::SecurityUtils.secure_compare(user, ENV.fetch("METRICS_USER")) &
ActiveSupport::SecurityUtils.secure_compare(password, ENV.fetch("METRICS_PASSWORD"))
end
run Yabeda::Prometheus::Exporter
end
mount metrics_app => "/metrics"

secure_compare keeps the check constant-time; the credentials come from the environment (or Rails credentials), never from the code. If the endpoint is only reachable on a private network, plain mounting works — but the auth costs nothing and keeps you safe when the topology changes.

3. Point your LeanSignal agent at it. The agent is an OpenTelemetry Collector and scrapes Prometheus endpoints natively — no extra software to run, and no OpenTelemetry knowledge needed. Three edits to its configuration file (/etc/leansignal-agent/config.yaml on Linux, /usr/local/etc/leansignal-agent/config.yaml on macOS):

First, under the existing receivers: block, add the scrape job — with the same Basic-auth credentials from step 2 if you set them:

prometheus/rails:
config:
scrape_configs:
- job_name: rails # keep as-is: the dashboards' job label
scrape_interval: 15s
metrics_path: /metrics
scheme: https # use http for plain in-network targets
basic_auth:
username: metrics
password: "<the generated password>"
static_configs:
- targets: ["www.my-rails-app.com:443"] # host:port — no scheme, no path

Second, find the metrics/all: pipeline near the bottom of the file and add the new receiver to its list:

metrics/all:
receivers: [otlp, hostmetrics, prometheus/localstores, prometheus/rails]

Third, restart only the agent — the local stores keep running and no data is lost:

Terminal window
sudo systemctl restart leansignal-agent # Linux
sudo launchctl kickstart -k system/com.leansignal.agent # macOS

On Kubernetes the agent’s config is managed through Helm values — see extend the agent.

Verify on the agent host — the Rails metrics land in its local store within one scrape interval:

Terminal window
curl -s --get 'http://127.0.0.1:8428/api/v1/query' --data-urlencode 'query=rails_requests_total'

They also appear in the app under Metrics with the source set to Available. Importing a demand below is what forwards them to central storage.

Alternative: a separate collector. If the gateway has no network route to your Rails app, run a small OpenTelemetry Collector next to the app with the same prometheus receiver and an otlp exporter pointed at the gateway (leansignal-agent:4317). In Kubernetes, the Prometheus receiver’s kubernetes_sd_configs (or the PodMonitor/annotation setup you already use) replaces static_configs.

Logs — no SDK needed. OpenTelemetry Ruby has no stable logs SDK yet, and for Rails you don’t need one: Rails already writes log/production.log, and the agent tails files natively. If the agent runs on the same host (or can read the file), add a receiver to its config — the same file, edit points, and restart commands as the metrics scrape in Setup:

filelog/rails:
include: [/srv/my-app/log/production.log] # your app's log path
start_at: end
resource:
service.name: my-rails-app # becomes the stream's service_name label

Then add filelog/rails to the logs/all: pipeline’s receivers: list and restart the agent. Within seconds the stream is searchable under Logs with the source set to Available as {service_name="my-rails-app"} — and demanding it (a dashboard log panel, or an ingestion rule) is what forwards it centrally. One request per line reads best: lograge collapses Rails’ multi-line request output into single log lines. If Rails runs in containers or on hosts the agent can’t reach, keep logs on stdout and point the shipper you already run at the gateway’s Loki push endpoint (:3500) instead.

Traces are where OpenTelemetry Ruby shines — add opentelemetry-sdk, opentelemetry-exporter-otlp, and opentelemetry-instrumentation-all, then point OTEL_EXPORTER_OTLP_ENDPOINT at the gateway and spans flow with no further code changes. See the Ruby OpenTelemetry docs.

Ruby on Rails ships as a ready-made demand in three variants. You don’t copy any JSON — in the LeanSignal app go to Demands, press the ˅ arrow on the Add Demand button (the arrow opens the import menu), choose Import from catalog…, and pick the variant. Each variant below lists the slug that identifies its published bundle. See Integrations for the full import flow.

The variants are nested — Standard is a superset of Essential, Extended a superset of Standard. Pick whichever is closest to what you need and treat it as a starting point: after import everything is a normal, editable copy, so retune thresholds, add or drop panels, and adjust queries for your environment — your edits reshape the demand automatically. Start small and re-import a larger variant later if you outgrow it.

The golden signals. 1 dashboard · 4 panels · 3 alerts — traffic, errors, latency, and database time at a glance.

Import this variant from the catalog. Its demand slug:

Demand slug
ruby-otel-demand-essential

Dashboard — Request rate, 5xx error rate, Request latency p95, and Database time p95.

Alerts (3)

AlertSeverityFires when
High HTTP 5xx error ratecritical5xx ratio > 5% for 5m
High request latency (p95)warningp95 request latency > 1s for 5m
High database time (p95)warningDB time p95 > 0.5s for 10m

Review the bundle JSON →

Each variant imports one Ruby on Rails dashboard, its panels grouped into collapsible sections. Every dashboard carries an Instance filter (on the instance label), so one import covers the whole fleet — view every instance at once, or focus on one. Legends are sortable tables showing last/mean/max per series, so the outlier instance stands out. Dashboards are demand-driven: importing one tells your agents to forward exactly the timeseries its panels query, and nothing else. Panels are ordinary Perses panels — edit queries, add panels, or retune them after import, and your changes reshape the demand automatically.

Thresholds are conservative starting points — tune them to your workload.

Was this page helpful?