Project

allstak

0.0
The project is in a healthy, maintained state
Production-ready Ruby SDK for AllStak observability: Rack/Rails middleware, ActiveRecord instrumentation, outbound HTTP capture, distributed tracing, cron monitoring, and structured logs.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 3.0
~> 3.12
~> 3.19
 Project Readme

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_exit triggers 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.message }.to_json
  end
end

License

MIT