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.
Prerequisites
Section titled “Prerequisites”- 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 port —
4317(gRPC) or4318(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:
# Gemfilegem "yabeda-rails"gem "yabeda-prometheus"Mount the metrics endpoint (a Rack app on your chosen path). Yabeda’s Railtie registers the Rails metrics automatically:
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:
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::Exporterendmount 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 pathSecond, 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:
sudo systemctl restart leansignal-agent # Linuxsudo launchctl kickstart -k system/com.leansignal.agent # macOSOn 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:
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 and traces
Section titled “Logs and traces”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 labelThen 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.
Demands
Section titled “Demands”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:
ruby-otel-demand-essentialDashboard — Request rate, 5xx error rate, Request latency p95, and Database time p95.
Alerts (3)
| Alert | Severity | Fires when |
|---|---|---|
| High HTTP 5xx error rate | critical | 5xx ratio > 5% for 5m |
| High request latency (p95) | warning | p95 request latency > 1s for 5m |
| High database time (p95) | warning | DB time p95 > 0.5s for 10m |
Well-rounded coverage. 1 dashboard · 8 panels · 6 alerts — adds per-action traffic, status breakdowns, and view time. The recommended default.
Import this variant from the catalog. Its demand slug:
ruby-otel-demand-standardDashboard — everything in Essential, plus Request rate by controller#action, Requests by status, Request latency p50, and View render time p95.
Alerts (6)
| Alert | Severity | Fires when |
|---|---|---|
| High HTTP 5xx error rate | critical | 5xx ratio > 5% for 5m |
| High request latency (p95) | warning | p95 request latency > 1s for 5m |
| High database time (p95) | warning | DB time p95 > 0.5s for 10m |
| Critical HTTP 5xx error rate | critical | 5xx ratio > 20% for 5m |
| High request latency (p99) | warning | p99 request latency > 2.5s for 5m |
| No inbound traffic | warning | no requests for 15m |
Deep Rails visibility. 1 dashboard · 14 panels · 10 alerts — response formats, slowest actions, and where request time goes for apps you need to diagnose, not just watch.
Import this variant from the catalog. Its demand slug:
ruby-otel-demand-extendedDashboard — everything in Standard, plus Throughput by format, 4xx client error rate, Request latency p99, Slowest actions (p95), Request time breakdown (avg), and Database share of request time.
Alerts (10)
| Alert | Severity | Fires when |
|---|---|---|
| High HTTP 5xx error rate | critical | 5xx ratio > 5% for 5m |
| High request latency (p95) | warning | p95 request latency > 1s for 5m |
| High database time (p95) | warning | DB time p95 > 0.5s for 10m |
| Critical HTTP 5xx error rate | critical | 5xx ratio > 20% for 5m |
| High request latency (p99) | warning | p99 request latency > 2.5s for 5m |
| No inbound traffic | warning | no requests for 15m |
| Elevated 4xx client error rate | warning | 4xx ratio > 30% for 10m |
| High view render time (p95) | warning | view time p95 > 0.3s for 10m |
| High database time (p99) | warning | DB time p99 > 1s for 10m |
| Slow controller action (p95) | warning | an action p95 > 2s for 10m |
Dashboards
Section titled “Dashboards”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.
Alerts
Section titled “Alerts”Thresholds are conservative starting points — tune them to your workload.
Next steps
Section titled “Next steps”- Demand-driven observability — why importing a demand also shapes what your agents collect.
- Agent configuration — the full agent config surface.
Thanks for your feedback!
Report sent — thank you!