lens-rails
Zero-config APM integration for Lens. Drop this gem into any Rails app and logs, metrics, and request traces start flowing to your Lens instance automatically — no OpenTelemetry SDK, no protobuf, no additional processes.
Installation
Add to your Gemfile:
gem 'lens-rails'
Set two environment variables (e.g. in .env or your secrets manager):
LENS_URL=https://lens.example.com
LENS_APP_TOKEN=your-app-token
That's it. No initializer required.
What gets collected automatically
| Signal | Source | Detail |
|---|---|---|
| Logs | Rails.logger |
Every log line with severity, body, controller, action, and request ID |
| Metrics | Rack middleware | Request count, error count, mean/p50/p95 duration — flushed every 30 s |
| HTTP requests | process_action.action_controller |
Controller, action, method, path, status, duration, SQL summary, operation waterfall |
| Background jobs | perform.active_job |
Job class, queue, duration, SQL summary, operation waterfall |
Each HTTP request and job record includes a flat operation waterfall: one entry per SQL query (name + SQL text + duration + offset from request start) and per view render.
Configuration
To override defaults, add an initializer:
# config/initializers/lens.rb
Lens::Rails.configure do |config|
config.service_name = "my-app" # defaults to Rails app module name
config.url = "https://..." # overrides LENS_URL
config.app_token = "tok_..." # overrides LENS_APP_TOKEN
# Only record SQL queries that took at least this long (ms). Default: 0.0 (all queries).
config.sql_threshold_ms = 5.0
# Drop SQL queries whose text matches any of these patterns.
# Useful for suppressing framework noise from the waterfall.
config.sql_ignore = [
/solid_queue/i,
/\ASELECT.*schema_migrations/i,
]
# Minimum severity to export. Default: Logger::INFO (drops DEBUG-level SQL noise).
# Set to Logger::WARN to export only warnings and above.
config.min_log_severity = Logger::INFO
# Ring-buffer size for log records (drop-oldest when full). Default: 10_000.
config.max_log_buffer = 10_000
# HTTP timeout for each exporter flush call (seconds). Default: 2.
config.exporter_open_timeout = 2
config.exporter_read_timeout = 2
# Maximum time to wait for a final flush on process shutdown (seconds). Default: 5.
config.shutdown_timeout = 5
end
How it works
The gem wires up three components via a Railtie:
LogExporter— broadcasts intoRails.loggerand batches records toPOST /v1/logsevery 5 s.MetricsExporter— Rack middleware that tracks request durations via reservoir sampling and flushes toPOST /v1/metricsevery 30 s.RequestsExporter— subscribes toActiveSupport::Notifications(process_action,sql.active_record,render_template,perform.active_job) and flushes request and job records toPOST /v1/requestsevery 30 s.
All payloads are gzip-compressed JSON. Background flush threads restart automatically after a Puma or Unicorn fork.
SolidQueue dispatcher jobs are filtered out of the job waterfall automatically.
Requirements
- Ruby >= 3.1
- Rails >= 7.1