lens-rails
Zero-config APM integration for Lens. Drop this gem into any Rails app and logs, metrics, requests, jobs, and errors 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 |
| Errors | Rails.error |
All reported exceptions — class, message, backtrace, context, severity |
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.
Errors are deduplicated server-side by fingerprint (app + class + message) and surfaced with a resolve workflow.
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
# Exception classes to suppress from error reporting.
# Defaults to common Rails routing and auth errors (RoutingError, RecordNotFound, etc.).
config.error_ignore_classes = %w[
ActionController::RoutingError
ActiveRecord::RecordNotFound
]
# Queue size for error records (drop-on-full). Default: 1_000.
config.max_error_buffer = 1_000
end
Reporting errors explicitly
The gem subscribes to Rails.error automatically, so any exception reported through that interface (including unhandled exceptions in requests and jobs) is captured. To report an error directly:
Lens.error(exception, context: { user_id: current_user.id })
This delegates to Rails.error.report with handled: true, so the exception is not re-raised.
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.ErrorExporter— registers as aRails.errorsubscriber and ships error records toPOST /v1/errors. Context hashes are sanitized (circular refs, depth limits, 64 KB string cap) before serialization.
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