A self-hosted error tracking engine for Rails 8+ applications. Track errors, get notified, and resolve issues—all without external services.
- Automatic Error Capture - Rack middleware + Rails error reporting API
- Smart Grouping - Errors are grouped by fingerprint (class + message + location)
- Debugger Inspector - Side-by-side source code and local variables view, like a real debugger
- Local Variables Capture - Automatic capture of variable values at the raise point
- Full-Text Search - Search errors by exception class, message, or file path
- Status Management - Mark errors as resolved, unresolved, or ignored
- Auto-Reopen - Resolved errors automatically reopen when they recur
- GitHub Integration - Create GitHub issues directly from errors with full context
- Rate Limiting - Configurable cooldown prevents notification spam during error storms
- Pluggable Notifiers - Telegram, Slack, Resend (email), webhooks, or build your own
- Standalone Dashboard - Clean Tailwind UI with interactive charts and time-range zooming
- Configurable Authentication - Integrate with Devise, Warden, or custom auth
- Request Context - Capture URL, params, headers, user info, and custom data
- Ruby >= 3.2
- Rails >= 8.0
- PostgreSQL, MySQL, or SQLite
Add to your Gemfile:
gem "faultline", git: "https://github.com/dlt/faultline.git"
# Or from RubyGems (when published)
# gem "faultline"Run the installer:
bundle install
rails generate faultline:install
rails db:migrateEdit config/initializers/faultline.rb:
Faultline.configure do |config|
# Devise with admin role
config.authenticate_with = lambda { |request|
user = request.env["warden"]&.user
user&.admin?
}
# Or any authenticated user
config.authenticate_with = lambda { |request|
request.env["warden"]&.authenticated?
}
endCreate GitHub issues directly from the error dashboard with full context including stack traces, local variables, and source code snippets.
# Store in credentials: rails credentials:edit
# faultline:
# github:
# token: "ghp_xxxxx"
config.github_repo = "your-org/your-repo"
config.github_token = Rails.application.credentials.dig(:faultline, :github, :token)
config.github_labels = ["bug", "faultline"] # default labels for created issuesNotifications are optional. Faultline will track all errors and display them in the dashboard regardless of whether any notifiers are configured. Add notifiers only if you want to be alerted when errors occur.
# Store in credentials: rails credentials:edit
# faultline:
# resend:
# api_key: "re_xxxxx"
config.add_notifier(
Faultline::Notifiers::Resend.new(
api_key: Rails.application.credentials.dig(:faultline, :resend, :api_key),
from: "[email protected]",
to: "[email protected]" # or array: ["[email protected]", "[email protected]"]
)
)# Store in credentials: rails credentials:edit
# faultline:
# telegram:
# bot_token: "your-bot-token"
# chat_id: "your-chat-id"
config.add_notifier(
Faultline::Notifiers::Telegram.new(
bot_token: Rails.application.credentials.dig(:faultline, :telegram, :bot_token),
chat_id: Rails.application.credentials.dig(:faultline, :telegram, :chat_id)
)
)config.add_notifier(
Faultline::Notifiers::Slack.new(
webhook_url: Rails.application.credentials.dig(:faultline, :slack, :webhook_url),
channel: "#errors",
username: "Faultline"
)
)config.add_notifier(
Faultline::Notifiers::Webhook.new(
url: "https://your-service.com/errors",
method: :post,
headers: { "Authorization" => "Bearer #{ENV['WEBHOOK_TOKEN']}" }
)
)Prevent notification spam during error storms:
config.notification_cooldown = 5.minutes # default, nil to disableconfig.notification_rules = {
on_first_occurrence: true, # New error types
on_reopen: true, # Resolved errors that recur
on_threshold: [10, 50, 100, 500], # At these occurrence counts
critical_exceptions: [ # Always notify for these
"Stripe::APIError",
"ActiveRecord::StatementInvalid"
],
notify_in_environments: ["production"]
}Optionally subscribe to Rails' error reporting API to capture errors from background jobs and explicit Rails.error calls:
config.register_error_subscriber = trueThis captures errors reported via:
Rails.error.handle { ... }- handled errors (swallowed)Rails.error.record { ... }- errors re-raised after reportingRails.error.report(exception)- manual reporting
The middleware captures unhandled exceptions with local variable capture. The error subscriber captures everything else. Both can be enabled together.
# Exceptions to ignore
config.ignored_exceptions = [
"ActiveRecord::RecordNotFound",
"ActionController::RoutingError"
]
# Bots/crawlers to ignore
config.ignored_user_agents = [/bot/i, /crawler/i, /Googlebot/i]
# Paths to ignore
config.middleware_ignore_paths = ["/assets", "/health"]Add custom data to every error occurrence. The lambda receives the request and Rack env, and should return a hash:
config.custom_context = lambda { |request, env|
controller = env["action_controller.instance"]
{
account_id: controller&.current_account&.id,
tenant: request.subdomain,
feature_flags: controller&.enabled_features
}
}This data is stored with each occurrence and displayed in the dashboard under "Custom Context".
Visit /faultline to access the error dashboard.
begin
risky_operation
rescue => e
Faultline.track(e, {
request: request,
user: current_user,
custom_data: { order_id: @order.id }
})
raise
endFaultline automatically captures local variables at the point where exceptions are raised. This helps you debug errors without needing to reproduce them.
Variables are:
- Automatically captured via
TracePointwhen exceptions are raised - Filtered for sensitive data (passwords, tokens, API keys, etc.)
- Serialized safely with depth limits and circular reference handling
- Displayed in the occurrence detail page
No configuration needed—it works out of the box.
# Skip certain errors
config.before_track = lambda { |exception, context|
return false if exception.message.include?("Timeout")
true
}
# Post-tracking integration
config.after_track = lambda { |error_group, occurrence|
Analytics.track("error", { type: error_group.exception_class })
}# Group errors by custom criteria
config.custom_fingerprint = lambda { |exception, context|
{ extra_components: [context.dig(:custom_data, :tenant_id)] }
}class MyNotifier < Faultline::Notifiers::Base
def initialize(api_key:)
@api_key = api_key
end
def call(error_group, error_occurrence)
data = format_message(error_group, error_occurrence)
# Send to your service
MyService.notify(data, api_key: @api_key)
end
# Optional: control when to notify
def should_notify?(error_group, error_occurrence)
error_group.occurrences_count < 100 # Stop after 100
end
end
# Usage
config.add_notifier(MyNotifier.new(api_key: "..."))The engine creates three tables:
faultline_error_groups- Grouped errors by fingerprintfaultline_error_occurrences- Individual error instancesfaultline_error_contexts- Custom key-value context data
Configure automatic cleanup:
config.retention_days = 90 # nil = keep foreverSet up a scheduled job to clean old data:
# In a cron job or Sidekiq scheduler
Faultline::ErrorOccurrence
.where("created_at < ?", 90.days.ago)
.in_batches
.delete_all| Feature | Faultline | Solid Errors | Sentry | Honeybadger | Rollbar |
|---|---|---|---|---|---|
| Self-hosted | ✅ | ✅ | ❌ | ❌ | |
| Free tier | ✅ Unlimited | ✅ Unlimited | 5K errors/mo | 1 project | 5K errors/mo |
| Rails native | ✅ | ✅ | ✅ | ✅ | ✅ |
| Local variables | ✅ | ❌ | ✅ | ✅ | ❌ |
| GitHub integration | ✅ | ❌ | ✅ | ✅ | ✅ |
| Full-text search | ✅ | ❌ | ✅ | ✅ | ✅ |
| Notifications | ✅ | ❌ | ✅ | ✅ | ✅ |
| Error grouping | ✅ | ❌ | ✅ | ✅ | ✅ |
| Source maps (JS) | ❌ | ❌ | ✅ | ✅ | ✅ |
| Performance/APM | ❌ | ❌ | ✅ | ✅ | ❌ |
| Multi-language | Ruby | Ruby | 30+ | 10+ | 20+ |
Faultline is ideal for:
- Teams wanting full control over error data
- Projects with privacy/compliance requirements (GDPR, HIPAA)
- Rails-only applications without complex frontend needs
- Developers who prefer simplicity over feature bloat
- Organizations avoiding vendor lock-in and recurring SaaS costs
cd engines/faultline
bundle install
bundle exec rspecMIT License. See LICENSE for details.
