Module: Findbug
- Defined in:
- lib/findbug.rb,
lib/findbug/engine.rb,
lib/findbug/railtie.rb,
lib/findbug/version.rb,
lib/findbug/configuration.rb,
app/jobs/findbug/alert_job.rb,
lib/findbug/adapter_helper.rb,
lib/findbug/capture/context.rb,
app/jobs/findbug/cleanup_job.rb,
app/jobs/findbug/persist_job.rb,
lib/findbug/alerts/throttler.rb,
lib/findbug/alerts/dispatcher.rb,
app/models/findbug/error_event.rb,
lib/findbug/capture/middleware.rb,
app/models/findbug/alert_channel.rb,
lib/findbug/alerts/channels/base.rb,
lib/findbug/background_persister.rb,
lib/findbug/storage/redis_buffer.rb,
lib/findbug/alerts/channels/email.rb,
lib/findbug/alerts/channels/slack.rb,
lib/findbug/alerts/channels/discord.rb,
lib/findbug/alerts/channels/webhook.rb,
lib/findbug/capture/message_handler.rb,
lib/findbug/performance/transaction.rb,
lib/findbug/storage/circuit_breaker.rb,
lib/findbug/storage/connection_pool.rb,
app/models/findbug/performance_event.rb,
lib/findbug/processing/data_scrubber.rb,
lib/findbug/rails/controller_methods.rb,
lib/findbug/capture/exception_handler.rb,
lib/findbug/performance/instrumentation.rb,
lib/findbug/capture/exception_subscriber.rb,
lib/generators/findbug/install_generator.rb,
lib/generators/findbug/upgrade_generator.rb,
app/controllers/findbug/alerts_controller.rb,
app/controllers/findbug/errors_controller.rb,
app/controllers/findbug/dashboard_controller.rb,
app/controllers/findbug/application_controller.rb,
app/controllers/findbug/performance_controller.rb
Overview
Findbug - Self-hosted error tracking and performance monitoring for Rails
ARCHITECTURE OVERVIEW
Findbug is designed with ONE critical goal: NEVER slow down your application.
How we achieve this:
-
ASYNC WRITES When an error occurs, we don’t write to the database immediately. Instead, we push to a Redis buffer in a background thread. This takes ~1-2ms and doesn’t block your request.
-
BACKGROUND PERSISTENCE A periodic job (via ActiveJob or built-in thread) pulls events from Redis and batch-inserts them to the database. This happens outside your request cycle.
-
CIRCUIT BREAKER If Redis is down, we don’t keep retrying and slowing down your app. The circuit breaker “opens” after 5 failures and stops attempting writes for 30 seconds.
-
CONNECTION POOLING We maintain our OWN Redis connection pool, separate from your app’s Redis/Sidekiq. This prevents connection contention.
-
SAMPLING For high-traffic apps, you can sample errors (e.g., capture 50%) to reduce overhead further.
DATA FLOW
[Exception occurs]
|
v
[Middleware catches it]
|
v
[Scrub sensitive data]
|
v
[Push to Redis buffer] <-- Async, non-blocking (Thread.new)
|
v
[BackgroundPersister runs every 30s]
|
v
[Batch insert to Database]
|
v
[Dashboard displays data]
Defined Under Namespace
Modules: AdapterHelper, Alerts, Capture, Generators, Performance, Processing, RailsExt, Storage Classes: AlertChannel, AlertConfiguration, AlertJob, AlertsController, ApplicationController, BackgroundPersister, CleanupJob, Configuration, ConfigurationError, DashboardController, Engine, Error, ErrorEvent, ErrorsController, PerformanceController, PerformanceEvent, PersistJob, Railtie
Constant Summary collapse
- VERSION =
"0.5.0"
Class Method Summary collapse
-
.capture_exception(exception, context = {}) ⇒ Object
Capture an exception manually.
-
.capture_message(message, level = :info, context = {}) ⇒ Object
Capture a message (non-exception event).
-
.config ⇒ Configuration
Access the configuration object.
-
.configure {|Configuration| ... } ⇒ Object
Configure Findbug with a block.
-
.enabled? ⇒ Boolean
Check if Findbug is enabled.
-
.logger ⇒ Object
Get the logger instance.
-
.logger=(new_logger) ⇒ Object
Set a custom logger.
-
.reset! ⇒ Object
Reset configuration to defaults (useful for testing).
-
.track_performance(name) { ... } ⇒ Object
Wrap a block with performance tracking.
Class Method Details
.capture_exception(exception, context = {}) ⇒ Object
Capture an exception manually
WHY A MANUAL CAPTURE METHOD?
Sometimes you want to capture an exception without crashing. For example, in a rescue block where you handle the error gracefully but still want to track it.
157 158 159 160 161 162 163 164 165 |
# File 'lib/findbug.rb', line 157 def capture_exception(exception, context = {}) return unless enabled? return unless config.should_capture_exception?(exception) Capture::ExceptionHandler.capture(exception, context) rescue StandardError => e # CRITICAL: Never let Findbug crash your app logger.error("[Findbug] Failed to capture exception: #{e.}") end |
.capture_message(message, level = :info, context = {}) ⇒ Object
Capture a message (non-exception event)
176 177 178 179 180 181 182 |
# File 'lib/findbug.rb', line 176 def (, level = :info, context = {}) return unless enabled? Capture::MessageHandler.capture(, level, context) rescue StandardError => e logger.error("[Findbug] Failed to capture message: #{e.}") end |
.config ⇒ Configuration
Access the configuration object
WHY A CLASS METHOD?
We use ‘Findbug.config` instead of a global variable because:
-
It’s lazily initialized (created on first access)
-
It’s thread-safe (||= is atomic in MRI Ruby)
-
It’s mockable in tests
-
It follows Ruby conventions (like Rails.config)
79 80 81 |
# File 'lib/findbug.rb', line 79 def config @config ||= Configuration.new end |
.configure {|Configuration| ... } ⇒ Object
Configure Findbug with a block
97 98 99 100 101 |
# File 'lib/findbug.rb', line 97 def configure yield(config) if block_given? config.validate! config end |
.enabled? ⇒ Boolean
Check if Findbug is enabled
This is a convenience method used throughout the codebase. It checks both the enabled flag AND validates we’re properly configured.
134 135 136 |
# File 'lib/findbug.rb', line 134 def enabled? config.enabled && config.redis_url.present? end |
.logger ⇒ Object
Get the logger instance
Falls back to Rails.logger, then to a null logger
120 121 122 |
# File 'lib/findbug.rb', line 120 def logger @logger ||= config.logger || (defined?(Rails) && Rails.logger) || Logger.new(IO::NULL) end |
.logger=(new_logger) ⇒ Object
Set a custom logger
125 126 127 |
# File 'lib/findbug.rb', line 125 def logger=(new_logger) @logger = new_logger end |
.reset! ⇒ Object
Reset configuration to defaults (useful for testing)
WHY EXPOSE THIS?
In tests, you often want to reset state between examples. This makes Findbug test-friendly.
110 111 112 113 114 |
# File 'lib/findbug.rb', line 110 def reset! @config = nil @redis_pool = nil @logger = nil end |
.track_performance(name) { ... } ⇒ Object
Wrap a block with performance tracking
194 195 196 197 198 199 200 201 |
# File 'lib/findbug.rb', line 194 def track_performance(name, &block) return yield unless enabled? && config.performance_enabled Performance::Transaction.track(name, &block) rescue StandardError => e logger.error("[Findbug] Performance tracking failed: #{e.}") yield # Still execute the block even if tracking fails end |