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:migrateMounting
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.newConfiguration
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]
endPurging 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