Ruby SDK
Install DebugBundle in Ruby, Rails, Rack, and Sidekiq applications with request, exception, log, probe, and browser relay capture.
The Ruby SDK ships as the debugbundle gem and supports Rails, Rack, Sidekiq, Ruby Logger, Semantic Logger, local file transport, connected HTTP transport, remote capture policy, probes, and a full browser relay handler.
Installation
gem "debugbundle"Then install with Bundler:
bundle installConfiguration Reference
Configuration precedence is intentionally explicit:
- Values passed to
DebugBundle.init(...),DebugBundle::Client.new(...), orDebugBundle::Relay::Handler.new(...). - Rails
config.debugbundle.*values when the Railtie configures the default client or relay route. - Environment variables only when your application passes them into those Ruby calls.
- Server-owned capture policy and remote probe directives fetched from
GET /v1/sdk/config.
Capture policy fields are server-owned. Do not set local SDK config fields for capture policy; configure capture policy in DebugBundle and let the SDK poll it.
| Option | Default | Purpose |
|---|---|---|
project_token | required for connected capture | Write-only DebugBundle project token. |
service | ruby-service | Service name in event envelopes. |
environment | development | Runtime environment label. |
endpoint | https://api.debugbundle.com/v1/events | Connected ingestion endpoint. |
enabled | true | Capture kill switch. |
project_mode | connected | connected or local_only. |
local_events_dir | .debugbundle/local/events | Local event file destination. |
spool_dir | .debugbundle/local/browser-relay-spool | Browser relay durable spool destination. |
redact_fields | [] | Additional sensitive field names merged with built-in redaction defaults. |
batch_size | 25 | Max events per flush batch. |
flush_interval | 5 | Flush interval in seconds. |
sample_rate | 1.0 | Per-event sampling rate. |
log_level | warning | Minimum captured log severity. |
relay_enabled | true | Enable the Rails relay route. |
relay_rate_limit_per_minute | 60 | Per-IP browser relay rate limit. |
relay_durable_write | true | Write relay batches to spool before connected forwarding. |
max_probe_labels | 50 | Max distinct probe labels retained in memory. |
max_probe_entries_per_label | 10 | Ring buffer capacity per probe label. |
probe_flush_on_error | true | Attach probe buffers to captured exceptions. |
probes_poll_interval | 60 | Remote config poll interval in seconds. |
Install Examples By Mode
Connected mode:
DebugBundle.init(
project_token: ENV.fetch("DEBUGBUNDLE_TOKEN"),
service: "checkout-api",
environment: "production",
endpoint: "https://api.debugbundle.com/v1/events"
)Local-only mode:
DebugBundle.init(
project_token: ENV.fetch("DEBUGBUNDLE_TOKEN"),
service: "checkout-api",
environment: "development",
project_mode: :local_only,
local_events_dir: ".debugbundle/local/events"
)Rails
Configure the SDK in an initializer:
# config/initializers/debugbundle.rb
Rails.application.configure do
config.debugbundle.project_token = ENV["DEBUGBUNDLE_TOKEN"]
config.debugbundle.service = "patients-api"
config.debugbundle.environment = Rails.env
config.debugbundle.project_mode = :connected
endThe Railtie inserts Rack request capture, preserves X-Request-Id and X-DebugBundle-Trace-Id, captures Rails route/controller/action metadata when available, wires Rails logger capture, reuses Rails filter_parameters as additional redaction keys, and mounts the browser relay route at POST /debugbundle/browser by default.
You can adjust relay behavior in the same config block:
Rails.application.configure do
config.debugbundle.relay_enabled = true
config.debugbundle.relay_path = "/debugbundle/browser"
config.debugbundle.relay_allowed_origins = ["https://app.example.com"]
config.debugbundle.relay_rate_limit_per_minute = 60
config.debugbundle.relay_rate_limit_store = Rails.cache
endRack
Use the Rack middleware directly in non-Rails apps:
require "debugbundle"
DebugBundle.init(
project_token: ENV["DEBUGBUNDLE_TOKEN"],
service: "checkout-api",
environment: "production"
)
use DebugBundle::Rack::Middleware, client: DebugBundle.clientThe middleware captures request method, path, sanitized allowlisted headers, response status, duration, request ID, trace ID, exceptions, and recent probe buffers. It preserves downstream responses and re-raises exceptions after capture.
Sidekiq
Register the server middleware:
require "debugbundle"
require "debugbundle/sidekiq/server_middleware"
DebugBundle.init(
project_token: ENV["DEBUGBUNDLE_TOKEN"],
service: "billing-worker",
environment: "production"
)
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.add DebugBundle::Sidekiq::ServerMiddleware
end
endSidekiq events include job class, queue, jid, retry count, sanitized argument type summaries, exception details, runtime facts, and trace IDs when present on the job payload. The middleware never swallows job exceptions, so Sidekiq retry and failure behavior remains intact.
Vanilla Ruby
require "debugbundle"
DebugBundle.init(
project_token: ENV["DEBUGBUNDLE_TOKEN"],
service: "ruby-service",
environment: "production"
)
DebugBundle.capture_exception(RuntimeError.new("boom"))
DebugBundle.capture_log("payment retry failed", level: :warning, context: { order_id: "ord_123" })
DebugBundle.capture_message("worker started")
DebugBundle.set_context(:account_id, "acct_123")
DebugBundle.probe("checkout.cart", { item_count: 3 })
DebugBundle.flushDebugBundle.init arms best-effort Ruby exception capture through explicit at-exit and thread exception hooks. Ruby exception hooks cannot capture every hosting model; Rails, Rack, and Sidekiq integrations are the primary production capture paths.
Logger Integrations
Ruby Logger capture is additive and preserves existing output:
logger = Logger.new($stdout)
DebugBundle.capture_logger(logger)
logger.warn("retrying charge", order_id: "ord_123")Semantic Logger is supported when the gem is present:
DebugBundle.capture_semantic_loggerRails logger capture is auto-registered by the Railtie.
Browser Relay
The Ruby SDK includes a full browser relay handler for backend-owned browser event delivery:
DebugBundle::Relay::Handlerfor framework-neutral handling,DebugBundle::Rack::RelayMiddlewarefor Rack apps,- the Rails Railtie-mounted route at
POST /debugbundle/browser.
The relay validates same-origin or configured origins, requires Content-Type: application/json, rejects bodies over 256 KB, accepts only browser event types, strips browser-supplied credentials and trust fields, forces sdk_name to @debugbundle/sdk-browser, preserves browser correlation fields, rate limits per IP, writes local-only files, writes connected durable spool files by default, and forwards connected events with the server-side project token.
For split frontend/backend hosts, set explicit allowed origins. For connected relay delivery, a missing token means forwarding cannot succeed; keep the route disabled until server-side credentials are configured. To disable the relay, leave the route unmounted or set relay_enabled = false.
Transport Modes
| Mode | Behavior |
|---|---|
| Local/development/test | Writes event batches to .debugbundle/local/events/ |
project_mode: :local_only | Writes event batches to .debugbundle/local/events/ |
| Connected production/staging | Sends event batches to POST /v1/events and fetches GET /v1/sdk/config |
Local files use owner-only permissions, canonical path validation, symlink checks, unpredictable temp names, and atomic temp-file rename.
Probes And Capture Policy
The Ruby SDK fetches server-owned capture policy and probe configuration from GET /v1/sdk/config in connected production/staging environments. It supports ETag refreshes, minimal fallback when config fetch fails, always-on probe ring buffers, remote probe activations, heavy probes, and request-scoped trigger tokens via _debug_probe or X-DebugBundle-Probe-Trigger.
DebugBundle.probe("checkout.tax", -> { expensive_tax_state }, heavy: true)Heavy probes stay dormant unless a matching remote directive or trigger token is active.
Privacy Defaults
Ruby defaults are conservative for healthcare, financial, and enterprise systems:
- request and response bodies are off by default,
- request headers are allowlisted,
- sensitive fields are recursively redacted before buffering or transport,
- Rails
filter_parametersare merged into redaction rules, - existing request IDs are preserved,
X-DebugBundle-Trace-Idlinks browser and backend events,- SDK failures are swallowed internally and never raise into host application code.
Runtime Support
| Surface | Minimum compatibility version | Recommended production version | Installed-base compatibility lane | Rolling CI lane | Out of scope |
|---|---|---|---|---|---|
| Ruby runtime | 3.1 | current maintained Ruby 3.4.x patch line | 3.1 and 3.2 remain supported for installed-base coverage | 3.1, 3.2, 3.3, 3.4 | 3.0 and older |
| Rails | 7.0 | latest 7.1 patch line | 7.0 compatibility support | 7.0 and 7.1 | 6.x and older |
| Rack | 2.2 | latest 3.x patch line | 2.2 compatibility support | 2.2 and 3.x | 2.1 and older |
| Sidekiq | 7.x | latest 8.x patch line | 7.x compatibility support | 7.x and 8.x | 6.x and older |
Ruby 3.1 compatibility is maintained for the Rails installed base, but current upstream-maintained Ruby branches are recommended for production security fixes.
Post-V1 integrations remain out of scope here: Resque, Delayed Job, GoodJob, Sneakers, Shoryuken, Sinatra, Hanami, and deep ActiveRecord or SQL auto-instrumentation.
Dependency Alignment
debugbundle ships as one gem in V1, so there is no multi-package version-alignment surface.
- Pin one
debugbundleversion across Ruby web apps and worker repos when you want identical SDK behavior everywhere. - Keep Rack, Rails, and Sidekiq inside the supported lanes above.
- Do not mix unsupported framework majors into deployment examples.
Service Naming
Use distinct service names when one DebugBundle project receives events from multiple deployables:
- Browser frontend:
checkout-web - Rails or Rack API:
checkout-api - Sidekiq worker:
checkout-worker
The Ruby relay preserves the browser-provided service name and environment by default. Only override relay-level service or environment when you intentionally want the backend relay host to rewrite browser event identity.
Safe Startup And Status
The SDK never raises host-facing exceptions because of missing config, bad transport responses, or malformed remote config payloads.
DebugBundle.init(project_token: "")leaves capture disabled andDebugBundle.statusreports:degraded.DebugBundle.init(enabled: false)keeps the SDK as a no-op andDebugBundle.statusreports:disconnected.DebugBundle.last_event_atstaysniluntil a successful flush completes.DebugBundle.flushis a no-op when the SDK has no buffered events, no transport, or is still waiting out a retry window.
Status values:
:healthymeans idle or the last flush succeeded.:degradedmeans missing token or a temporary rate-limit window is preventing immediate delivery.:disconnectedmeans the SDK is disabled or repeated transport failures exhausted the connection path.
First-Event Verification
Use an explicit test message or exception during setup:
require "debugbundle"
DebugBundle.init(
project_token: ENV.fetch("DEBUGBUNDLE_TOKEN"),
service: "debugbundle-first-event",
environment: "staging"
)
DebugBundle.capture_message("debugbundle first-event verification", level: :error)
DebugBundle.flush
puts [DebugBundle.status, DebugBundle.last_event_at].inspectThen confirm receipt through your mock ingestion endpoint, a staging project, or the DebugBundle CLI verification flow. The Ruby SDK repository also ships make smoke and make smoke-published VERSION=... to verify the built and published gem paths with a Rack app event plus browser relay batch.