AllStak Ruby SDK
Official Ruby SDK for AllStak — error tracking, structured logs, HTTP + ActiveRecord monitoring, distributed tracing, and cron monitoring for Rack-based Ruby applications (Rails, Sinatra, Roda, Hanami).
gem "allstak"
bundle install
# or
gem install allstak
60-second setup
require "allstak"
AllStak.configure do |c|
c.api_key = ENV["ALLSTAK_API_KEY"]
c.environment = "production"
c.release = "myapp@1.2.3"
c.service_name = "myapp-api"
end
# Rack / Sinatra / Rails:
use AllStak::Integrations::Rack::Middleware
# Manual capture:
begin
risky!
rescue => e
AllStak.capture_exception(e)
end
That is the whole setup. Every request, every unhandled exception, every ActiveRecord query, every outbound Net::HTTP call, and every trace are captured automatically.
Public API (cross-SDK consistent)
Every method below is a module-level method on AllStak, matching the
names used by the JS, Python, Java, Go, PHP, and .NET SDKs so docs carry
across languages.
AllStak.configure { |c| ... } # once at bootstrap
AllStak.set_user(id: "42", email: "alice@example.com") # user context
AllStak.clear_user
AllStak.set_tag("service", "checkout") # sticky tag
AllStak.set_tags(region: "us-east-1", tier: "web") # bulk
AllStak.set_context("deployment", "canary") # sticky context
AllStak.capture_exception(exc) # preferred for errors
AllStak.capture_error("DomainError", "bad input") # without a throwable
AllStak.capture_message("hello", level: "info") # plain string event
AllStak.log.info("request started", metadata: {...}) # structured logs
AllStak.tracing # Tracing module
AllStak.http # HTTP monitor
AllStak.database # DB query monitor
AllStak.cron.job("daily-report") { run_job } # cron heartbeats
AllStak.flush # drain buffers
AllStak.shutdown # drain + close
AllStak.capture_message, AllStak.set_tag, and AllStak.set_context
landed in 0.1.1 as cross-SDK parity additions. Older 0.1.0 code that only
used capture_exception / capture_error keeps working — no breaking
changes.
Rails
# config/initializers/allstak.rb
require "allstak"
AllStak.configure do |c|
c.api_key = ENV["ALLSTAK_API_KEY"]
c.environment = Rails.env
c.release = "myapp@#{ENV['GIT_SHA'] || 'dev'}"
c.service_name = "myapp-api"
end
Rails.application.config.middleware.use AllStak::Integrations::Rack::Middleware
Sinatra
require "sinatra/base"
require "allstak"
AllStak.configure { |c| c.api_key = ENV["ALLSTAK_API_KEY"] }
class MyApp < Sinatra::Base
use AllStak::Integrations::Rack::Middleware
# ...
end
What gets captured automatically
| What | How |
|---|---|
| Unhandled exceptions | Rack middleware |
| Inbound HTTP requests | Rack middleware |
| Per-request trace ID | Rack middleware |
| User context (from env/session) | Rack middleware |
| ActiveRecord SQL queries | sql.active_record subscriber |
Outbound HTTP via Net::HTTP |
Net::HTTP#request patched |
The ActiveRecord subscriber and Net::HTTP patch are installed automatically
by AllStak.configure — no extra setup needed.
ActiveRecord
AllStak.configure installs an ActiveSupport::Notifications subscriber on
sql.active_record, which gives you normalized SQL, duration, row counts,
status, and error messages for every query — whether it's from an Active Record
relation, a find_by_sql, or a raw connection.execute. No duplication,
because every AR query fires exactly one sql.active_record event.
# Nothing to do — this just works:
User.where(email: "alice@example.com").first
# → captured as: SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?
Outbound HTTP (Net::HTTP)
# Also nothing to do:
Net::HTTP.get(URI("https://api.example.com/v1/data"))
# → captured as outbound HTTP telemetry with method, host, path, status, duration
The SDK patches Net::HTTP#request at configure time. Since every
convenience method (get, post, post_form, etc.) funnels through
#request, there is no duplication. Calls to your AllStak ingest host are
skipped to avoid recursive instrumentation.
Manual capture cheat sheet
# Errors
AllStak.capture_exception(exc, metadata: { order_id: "ORD-123" })
AllStak.capture_error("StripeTimeout", "Stripe /v1/charges timed out after 30s", level: "error")
# Logs (buffered, flushed in background)
AllStak.log.info("Order placed", metadata: { id: "ORD-1" })
AllStak.log.warn("Slow query", metadata: { ms: 4500 })
AllStak.log.error("Payment failed", metadata: { gateway: "stripe" })
# valid levels: debug | info | warn | error | fatal
# Distributed tracing (block-form)
AllStak.tracing.in_span("db.query", description: "SELECT users") do |span|
span.set_tag("db.type", "postgresql")
rows = User.all.to_a
end
# Cron monitoring — slug auto-created on first ping
AllStak.cron.job("daily-report") do
generate_report
end
# User context (for events that should show who was affected)
AllStak.set_user(id: "u-1", email: "alice@example.com")
AllStak.clear_user
# Graceful flush
AllStak.flush
Dashboard mapping
| Your code | Dashboard page |
|---|---|
AllStak.capture_exception / middleware |
Errors, Incidents |
AllStak.log.* |
Logs |
| Rack middleware (inbound) | Requests |
Net::HTTP (outbound, auto) |
Requests (outbound) |
| ActiveRecord queries (auto) | Database |
AllStak.tracing.in_span |
Traces |
AllStak.cron.job / cron.ping |
Cron Jobs |
Configuration
| Option | Default | Notes |
|---|---|---|
api_key |
ENV["ALLSTAK_API_KEY"] |
Your ask_live_... key. |
host |
https://api.allstak.sa |
Override with your AllStak ingest host. |
environment |
nil |
e.g. "production" |
release |
nil |
e.g. "myapp@1.2.3" |
service_name |
"ruby-service" |
Shown on spans and logs. |
flush_interval_ms |
2000 |
Background flush interval. |
buffer_size |
500 |
Max buffered items per feature. |
debug |
false |
Verbose SDK logging. |
connect_timeout |
3 |
Transport connect timeout (seconds). |
read_timeout |
3 |
Transport read timeout (seconds). |
max_retries |
5 |
Retry 5xx with exponential backoff. |
capture_unhandled_exceptions |
true |
Auto-capture from middleware. |
capture_http_requests |
true |
Auto-capture inbound HTTP. |
capture_user_context |
true |
Attach user claims to errors. |
capture_sql |
true |
Auto-capture AR queries. |
Environment variables: ALLSTAK_API_KEY, ALLSTAK_HOST, ALLSTAK_ENVIRONMENT,
ALLSTAK_RELEASE, ALLSTAK_SERVICE, ALLSTAK_DEBUG.
Production notes
- Never crashes your app. Every integration catches its own exceptions and logs at debug level. The middleware re-raises so your framework's exception handler still runs.
- Retries. 5xx and network errors retry with exponential backoff (1s → 2s → 4s → 8s, +jitter, max 5 attempts). 4xx are not retried.
- 401 disables the SDK. An invalid API key disables the SDK for the rest of the process — no further events are sent, a warning is logged once, and your app keeps running.
- Flush on shutdown.
at_exittriggers a best-effort flush. - Thread-safe. All public APIs are safe to call from any thread. Trace context uses Ruby's thread-local storage.
- Non-blocking. Telemetry is buffered and flushed on background threads. Your request pipeline is never blocked by SDK work.
Troubleshooting
| Symptom | Fix |
|---|---|
| No events in dashboard | Check host and api_key. Set debug = true. |
| 401 warning | Invalid API key. Create a new one in Settings. |
| Inbound requests missing | Make sure use AllStak::Integrations::Rack::Middleware. |
| DB queries missing | Make sure AllStak.configure runs BEFORE your first AR query. |
| Outbound HTTP missing | Same — configure must run before the first Net::HTTP call. |
| Cron monitor not appearing | Auto-created on first ping; check the slug matches. |
Full Sinatra + ActiveRecord example
require "sinatra/base"
require "active_record"
require "allstak"
AllStak.configure do |c|
c.api_key = ENV["ALLSTAK_API_KEY"]
c.environment = "production"
c.release = "taskflow@1.4.2"
c.service_name = "taskflow-api"
end
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: "app.db")
class Task < ActiveRecord::Base; end
class TaskFlow < Sinatra::Base
use AllStak::Integrations::Rack::Middleware
get "/tasks" do
Task.all.to_json
end
post "/tasks/:id/notify" do
task = Task.find(params[:id])
AllStak.tracing.in_span("http.notify", description: "POST httpbin.org/post") do |span|
span.set_tag("task.id", task.id.to_s)
uri = URI("https://httpbin.org/post")
Net::HTTP.post(uri, { task_id: task.id }.to_json, "Content-Type" => "application/json")
end
{ ok: true }.to_json
end
error do
# Framework-level rescue. Sinatra handles the exception before Rack middleware
# sees it, so forward manually:
e = env["sinatra.error"]
AllStak.capture_exception(e) if e
status 500
{ error: e.class.name, message: e. }.to_json
end
end
License
MIT