DEV Community

Soumit Das
Soumit Das

Posted on

FindBug - Self-hosted Error Tracking & Performance Monitoring for Rails (v0.5.0)

I got tired of paying $26/seat/month for error tracking on side projects, so I built FindBug - a Rails engine that captures exceptions and performance data with all the data living on your own infrastructure. Zero SaaS, full ownership, MIT-licensed.

🌐 Website: findbug.dev
πŸ“— Documentation: findbug.dev/docs
πŸ’Ž RubyGems: rubygems.org/gems/findbug
⭐ GitHub: github.com/ITSSOUMIT/findbug


The pain points that pushed me to build this

If you've run a Rails monolith with Sentry / Bugsnag / Rollbar, you've felt these:

  • Cost scales with team size, not value delivered. A 5-person team is $130+/mo before catching a single bug.
  • Data lives on someone else's servers - fine for most apps, awkward for anything with PII, GDPR, or SOC concerns.
  • Network dependency - if their edge is having a bad day, you lose telemetry exactly when you need it most.
  • Setup ritual - API keys, env-specific DSNs, separate dashboards to log into, SDK version drift.

What FindBug looks like

# Gemfile
gem "findbug", "~> 0.5.0"
Enter fullscreen mode Exit fullscreen mode
$ bundle install
$ rails generate findbug:install
$ rails db:migrate
Enter fullscreen mode Exit fullscreen mode

That's the entire install. After this:

  • Unhandled exceptions in controllers and middleware are captured automatically
  • HTTP performance + SQL queries + N+1 patterns are instrumented
  • A dashboard is mounted at /findbug
  • A background thread flushes data from Redis to your database every 30 seconds - no Sidekiq required

Architecture: never block the request

The non-negotiable design rule is that capturing an error must not slow down your users:

Request  β†’  Middleware catches exception  β†’  Scrub PII
         β†’  Push to Redis buffer  (Thread.new, ~1–2ms)
         β†’  Background persister flushes Redis β†’ DB every 30s in batches of 100
         β†’  Dashboard reads from DB on demand
Enter fullscreen mode Exit fullscreen mode
  • Capture overhead: ~1–2ms - single LPUSH on a dedicated Redis connection pool, fired via Thread.new
  • HTTP request blocking: 0ms - capture is fire-and-forget
  • Circuit breaker: 5 consecutive Redis failures opens the breaker for 30s, so your app stays fast even if Redis is unreachable
  • Dedicated connection pool: separate from your app's Redis/Sidekiq, so a spike in error volume can't starve your cache

Manual capture

When you want to swallow an exception but still track it:

begin
  risky_operation
rescue => e
  Findbug.capture_exception(e, user_id: current_user.id)
end
Enter fullscreen mode Exit fullscreen mode

Or capture a non-exception event:

Findbug.capture_message("Rate limit exceeded", :warning, user_id: 123)
Enter fullscreen mode Exit fullscreen mode

Track a custom block for performance:

Findbug.track_performance("external_api_call") do
  ExternalAPI.fetch_data
end
Enter fullscreen mode Exit fullscreen mode

Controller helpers

Attach request-scoped context that's included with every error captured during that request:

class ApplicationController < ActionController::Base
  before_action :set_findbug_context

  def set_findbug_context
    findbug_set_user(current_user)
    findbug_set_context(
      plan: current_user&.plan,
      organization_id: current_org&.id
    )
  end
end
Enter fullscreen mode Exit fullscreen mode

Breadcrumbs work the way you'd expect:

findbug_breadcrumb("User clicked checkout", category: "ui")
findbug_breadcrumb("Payment API called", category: "http", data: { amount: 99.99 })
Enter fullscreen mode Exit fullscreen mode

Database-agnostic (v0.5.0)

This was the biggest improvement in the latest release. The migrations and model layer detect your ActiveRecord::Base.connection.adapter_name at runtime and pick the right column types and SQL functions:

Adapter JSON column Time bucketing SQL
PostgreSQL jsonb date_trunc(...)
MySQL json DATE_FORMAT(...) / DATE(...)
SQLite text (with JSON serialisation) strftime(...) / DATE(...)

Application code is identical regardless of adapter - the JSON accessors always return native Ruby Hash / Array.

If you're writing your own migrations against the same multi-DB strategy, the helper is public API:

Findbug::AdapterHelper.json_column_type      # :jsonb / :json / :text
Findbug::AdapterHelper.json_default({})
Findbug::AdapterHelper.date_trunc_sql("hour", "captured_at")
Enter fullscreen mode Exit fullscreen mode

Alerts configured at runtime, not in code

Email / Slack / Discord / Webhook channels live in a DB table managed from the dashboard at /findbug/alerts. No redeploy to add a webhook. Same-error notifications are throttled by fingerprint (default 5-minute window).

Compared to existing solutions

Sentry / Bugsnag FindBug
Cost $26+ per seat / month Free, MIT
Data location Third-party servers Your infra
Network dependency Required None
Setup API keys + SDK + per-env config One gem, one command
Alert config Their dashboard Your dashboard
Customisation Bounded by their UI Fork the gem
Job scheduler needed No No (built-in thread)

Stack

  • Ruby 3.1+ Β· Rails 7.0+ (7.x and 8.x both tested) Β· Redis 4.0+
  • PostgreSQL / MySQL / SQLite - all supported, adapter auto-detected
  • 79 RSpec examples covering adapter detection, JSON-column normalisation, model behaviour, and aggregation

What it doesn't do (yet)

Being honest about the gap:

  • No source-map handling - server-side errors only for now
  • No frontend JS error tracking - on the v0.6 roadmap for Rails apps that render views
  • No multi-project support - one gem = one app
  • No SaaS option - you self-host or you don't use it

Links

If you find this useful, a ⭐ on GitHub means a lot - it helps other Rails developers find the gem:

GitHub logo ITSSOUMIT / findbug

Self-hosted error tracking and performance monitoring for Rails. Sentry-like functionality with all data on your infrastructure.


Built by Soumit Das. Would love feedback - especially from anyone running it on MySQL (the new adapter-agnostic path is fresh) or anyone with thoughts on the frontend-error-tracking direction.

Top comments (0)