ErrorLens
Self-hosted error tracking for Rails apps, built as a mountable engine. No separate service, no Redis, no SaaS bill — just two database tables in your existing Postgres.
Captures web request errors and Sidekiq job failures, groups them by type and location, and gives you a clean UI to browse, search, and resolve them.
Features
- Groups errors by class and backtrace location (fingerprinting)
- Captures full context: request params, headers, backtrace, IP, user agent
- Sidekiq job support: job class, queue, job ID, arguments
- Search errors by class name or message
- Resolve / reopen / delete error groups
- Paginated occurrence history with newer/older navigation
- Zero performance impact — captures are queued in-process and written asynchronously
- No external dependencies beyond Rails and ActiveRecord
Installation
Add to your Gemfile:
gem "error_lens", path: "vendor/error_lens"
Or once published to RubyGems:
gem "error_lens"
Run the installer:
bundle install
rails generate error_lens:install
rails db:migrate
Mounting
In config/routes.rb:
mount ErrorLens::Engine, at: "/admin/error_lens"
ErrorLens has no built-in authentication — mount it behind your existing auth constraint:
mount ErrorLens::Engine, at: "/admin/error_lens", constraints: AdminConstraint.new
Configuration
In an initializer (config/initializers/error_lens.rb):
ErrorLens.configure do |config|
# Exception classes to ignore entirely
config.ignored_exceptions = %w[
ActionController::RoutingError
ActiveRecord::RecordNotFound
]
# Parameters to filter from captured request data
config.filter_parameters = %w[password token secret credit_card]
end
Purging old data
Occurrences older than 60 days can be purged via a Rake task or a scheduled job:
ErrorLens::ErrorOccurrence.purge_older_than(60)
Run it from a cron or a Sidekiq scheduled job to keep the table from growing unbounded. Error groups are kept even after their occurrences are purged.
How it works
ErrorLens::Middlewarewraps every Rack request. On exception it builds an event hash and pushes it to an in-memorySizedQueue(capped at 500), then re-raises so your app handles the response normally.- A background thread (
ErrorLens::Processor) drains the queue and callsErrorLens::Writer. Writercomputes an MD5 fingerprint from the exception class and first app backtrace line, upserts theerror_lens_groupsrecord, and inserts a newerror_lens_occurrencesrow.- Sidekiq errors follow the same path via
ErrorLens::SidekiqMiddleware.
The request thread only does a queue push — the DB write never blocks your request.
License
MIT